Sei sulla pagina 1di 192

Corsi di Laurea triennale in

Ingegneria Elettronica,
delle Telecomunicazioni,
Biomedica, dell’Automazione, Informatica

Corso di Fondamenti di Informatica

Docente: Ing. Porfirio Tramontana


Dipartimento di Ingegneria Elettrica e Tecnologie dell’Informazione
Via Claudio 21, 4°piano
Università degli Studi di Napoli Federico II
e-mail: porfirio.tramontana@unina.it
tel. 081 7683901

1
Materiale didattico
• Libri di testo:
– A. Chianese, V. Moscato, A. Picariello, C. Sansone
Le radici dell'informatica.
Dai bit alla programmazione strutturata. Apogeo. 2017
– E.Burattini, A. Chianese, V. Moscato, A. Picariello, C.
Sansone
Che C serve. Per iniziare a programmare
Apogeo Editore, II edizione 2016

• Libri utili per consultazione


• B. Fadini, C. Savy, “Fondamenti di Informatica”, Liguori Editore
• Harvey M. Deitel, Paul J. Deitel, “C++. Fondamenti di
programmazione”, Apogeo
• Dispense di Fondamenti di Informatica del Prof. Giulio
Iannello
• Scaricabili dallo spazio web del corso
• Fondamentali per alcuni argomenti nella prima parte del corso
• Videocorso nell’ambito del progetto Federica
http://www.federica.eu/c/fondamenti_di_informatica/

2
Programma del corso e libri di testo

• Le radici dell'informatica
• Capitolo 1 tranne 1.15
• Capitolo 2 escluso 2.7 e approfondimenti
• Capitolo 3 da 3.1 fino a 3.7
• Capitoli 4 ,5, 6,7 (più dettagliati su Che Ci Serve)
• Le dispense del Prof. Iannello rappresentano un utile libro alternativo che copre
una parte degli argomenti trattati dagli altri libri, In particolare, sono da
consultare:
• Capitolo 1, 2, 3
• Capitolo 5
• Alcuni algoritmo del capitolo 6
• Capitoli 7, 8, 9 e 10

3
Programma del corso e libri di testo
• A Che C Serve?
• Capitolo 1 tranne 1.2.1, 1.4.1
• Capitolo 2 tranne 2.8.5 e 2.11.7
• Capitolo 3 tranne 3.4.1 e 3.4.3
• Capitolo 4
• Capitolo 5 tranne 5.2, 5.5 e 5.8
• Capitolo 6 tranne 6.8 e 6.12
• Capitolo 7, solo paragrafi 7.1, 7.2, 7.5
• Capitoli 8,9
• Gli esempi presentati nei diversi capitoli (in particolare nei capitoli 10,11) fungono
da ulteriori esempi svolti, in aggiunta a quelli visti a lezione
• Tutti i paragrafi saltati e il capitolo 12 rappresentano utili approfondimenti sul
linguaggio C++ non espressamente inseriti nel programma di questo corso,

4
Algoritmi: fonti

• Capitolo 3 da (Picariello, Chianese Moscato)


• Capitolo 1 da (Iannello)

• Ulteriore fonte di consultazione:


– https://www.federica.eu/c/fondamenti_di_informatica

5
Concetti fondamentali
• Informatica:
– Scienza della risoluzione dei problemi con l’aiuto degli
elaboratori
– Scienza per la elaborazione automatica delle informazioni
• Problema
– Specifica
• Elaborazione
• Algoritmo
• Sequenza finita di passi che porta alla risoluzione di un problema
• Esecutore
• Entità che deve realizzare il compito
– Linguaggio di programmazione
• Azioni elaborative
6
Concetti fondamentali
• Programma
– Specifica
– Dati di ingresso e di uscita
– Sequenza statica e sequenza dinamica
– Casi di test

7
Concetti Fondamentali

8
Logica: fonti

• Capitolo 2 da (Iannello)
• Capitolo 1 da (Chianese, Picariello, Moscato) (parte
centrale)

9
Logica delle proposizioni

• Proposizioni
• Valori logici
• Operatori di relazione
• Proposizioni semplici e composte
• Connettivi logici e tabelle di verità
– AND
– OR
– NOT
– XOR
• Precedenze: NOT →AND →OR XOR

10
• Esercizi:
• Riconoscere se un numero x è compreso tra 5 e
10 (escluso il 5)
• Riconoscere se si è entrati nell’aula giusta
• Riconoscere se un triangolo è equilatero, isoscele
o scaleno

11
Algebra di Boole
• Dalla logica delle proposizioni all'aritmetica binaria
-
• Algebra di Boole < D={0,1}, +, *, , min=0, max=1 >
• Proprietà:
– Commutativa
– Associativa
– Distributiva
– Idempotenza
– Dell'esistenza dell'elemento neutro
– Del complemento
– Del minimo e del massimo
– Dell'assorbimento
• Principio di Dualità
• Legge di De Morgan
Esercizi

In un'algebra di Boole, valutare le espressioni:


(A OR B) AND (A OR NOT(B))

A AND B = NOT (NOT A AND NOT B)

A AND C OR NOT C = A OR C

(NOT A AND NOT B) XOR (A OR B)

13
Esercizi

14
Informazione

• Iannello, capitolo 3 (parte iniziale)


• Chianese, Picariello, Moscato, capitolo 1, paragrafi 1-
4

• http://www.federica.eu/l/linformatica_e_la_codifica_de
lle_informazioni

15
Informazione

• Tipo
– Cardinalità
• Valore
• Attributo

• Rappresentazione delle informazioni


– Stringhe
• Misura dell’informazione
• Codifica e Codici
• Bit
• Multipli e sottomultipli
16
Rappresentazioni

• Valori logici
• Numeri naturali
– Sistema posizionale
– Trasformazione di numeri decimali in bit e viceversa
– Operazioni aritmetiche, overflow e underflow
• Numeri esadecimali
• Caratteri
– Codice ASCII
– American Standard Code for Information Interchange
– ASCII esteso
– Unicode

17
Codice ASCII (0-127)

18
Rappresentazione dei numeri relativi
– Rappresentazione in segno e modulo
• Problema del doppio zero

• Numeri relativi
– Rappresentazione in complemento
• Proprietà della somma
• Calcolo del complemento
• Complementa i bit e somma 1
• Trasformazione inversa
• Riconoscimento del segno
• Problema della proprietà associativa
• Problema dell’overflow

19
Rappresentazione in complementi alla base
• Dal numero alla rappresentazione
• Se è positivo, converti in binario
• Se è negativo:
• Cambia segno (cioè calcola il valore assoluto)
• Converti in binario
• Nega tutti i bit
• Somma uno
• Dalla rappresentazione al numero:
• Se il primo bit è zero (è positivo), converti in decimale
• Se il primo bit è uno (è negativo)
• Nega tutti i bit
• Somma uno
• Converti il numero in decimale
• Cambia segno (trasforma il valore assoluto nel valore)

20
Esercizio 1

Si vuole convertire il numero 16 in binario utilizzando


una rappresentazione binaria pura (cioè per numeri
naturali) su 8 bit.

21
Esercizio 1

Si vuole convertire il numero 16 in binario utilizzando


una rappresentazione binaria pura (cioè per numeri
naturali) su 8 bit.

00010000

22
Esercizio 2

Si vuole convertire il numero -12 in binario utilizzando


una rappresentazione per complementi a 2 su 5 bit
(cioè in base 25)

23
Esercizio 2

Si vuole convertire il numero -12 in binario utilizzando


una rappresentazione per complementi a 2 su 5 bit
(cioè in base 25)

-12 (abs) +12 (conv) 01100 (nega) 10011 (+1) 10100

Prova:
10100 (nega) 01011 (+1) 01100 (conv) +12 (-) -12

24
Esercizio 3

• Si vuole convertire il numero -10 in binario utilizzando


una rappresentazione per complementi a 2 su 4 bit
(cioè in base 24)

25
Esercizio 3

• Si vuole convertire il numero -10 in binario utilizzando


una rappresentazione per complementi a 2 su 4 bit
(cioè in base 24)

• -10 su 4 bit : OVERFLOW


• Su 5 bit, invece
-10 (abs) +10 (conv) 01010 (nega) 10101 (+1) 10110
Prova:
10110 (nega) 01001 (+1) 01010 (conv) +10 (-) -10

26
Esercizio 4

• Si vuole convertire il numero 111000 da complementi


a 2 in decimale (cioè in base 26).

27
Esercizio 4

• Si vuole convertire il numero 111000 da complementi


a 2 in decimale (cioè in base 26).

111000 (nega) 000111 (+1) 001000 (conv) +8 (-) -8

Prova:
-8 (abs) +8 (conv) 001000 (nega) 110111 (+1) 111000

28
Rappresentazione dei numeri reali

• Rappresentazione in virgola mobile


– Mantissa e esponente
– Underflow
– Normalizzazione
– Operazioni su virgola mobile
– Errore e precisione, assoluta e relativa
– Standard IEEE 754

29
Convergenza digitale

• Conversione analogico-digitale
• Campionamento e quantizzazione
• Compressione
• Codifica delle immagini
– Pixel e colori
• Codifica del video
• Codifica del suono

30
Macchina di Von Neumann: fonti

Alle radici dell’informatica, capitolo 2, tranne 2.7 ed


appendici
(Il libro fornisce talvolta maggiore approfondimento
rispetto a quanto oggetto di questo corso)
A che C serve, capitolo 1, paragrafi 1-3

31
Modello di Von Neumann

Modello fondamentale di Von Neumann


• Processore (CPU)
• Memoria
• Unità di input
• Unità di output

32
Algoritmo di Von Neumann

1. Boot
2. Fetch
• Preleva l’istruzione puntata da PI, la copia in IR e
incrementa PI
3. Operand Assembly
• Esamina l’istruzione IR, e preleva dalla posizione di
memoria puntata da PI (che poi incrementa) gli eventuali
altri operandi
4. Execute
• A seconda del comando da eseguire, manda comandi
alle altre unità, legge/scrive sulla memoria, avvia
operazioni di input/output, setta valori dei registri e dei
codici di controllo

33
Processore
Estensioni del Modello di Von Neumann
• Bus
• Bus dati
• Bus indirizzi
• Bus di controllo
• Parallelismo del bus
• Clock
• Sincronizzazione delle operazioni
• Frequenza di clock
• Buffer
• Buffer di input e di output
• Memoria di massa
• Sistema delle Interruzioni
• Segnali di interruzione
• Interrupt Service Routine (ISR)
• Uso delle interruzioni per la sincronizzazione con le periferiche
di input e output
• Algoritmo di Von Neumann con interruzioni

35
Estensioni del Modello di Von Neumann
Gerarchia delle memorie

• Registri del processore


• Memorie cache
• Memoria centrale
• Memoria di massa
• Memoria virtuale

37
Traduzione dei programmi

• Le radici dell’informatica, capitolo 7 (solo cenni)


• A che C serve, capitolo 1 paragrafo 4
• Dispense Iannello, capitolo 10

38
Linguaggi di programmazione

• Lessico
• Sintassi
• Semantica

• Linguaggio macchina
• Linguaggi ad alto livello
• Metalinguaggi

• Gli algoritmi sono indipendenti dai linguaggi

39
Realizzazione di un programma

1. Editing → Codice Sorgente


2. Compilazione → Codice oggetto
3. Linking →Codice eseguibile
4. Caricamento →Programma pronto per essere
eseguito
5. Esecuzione → Output del programma

40
Compilatore

• Fase di precompilazione
– Sostituzione automatica di parti di codice sorgente
• Esempio
#define PIGRECA 3.1415
• Il precompilatore sostituisce automaticamente a tutte le
occorrenza nel testo della parola PIGRECA il valore 3.1415
• Fase di analisi
– Analisi lessicale → Riconoscimento dei simboli (token)
– Analisi sintattica → Riconoscimento delle istruzioni
– Analisi semantica → Riconoscimento del significato delle
istruzioni
• Fase di sintesi
– Trasformazione della semantica individuata in codice oggetto

41
Introduzione alla Programmazione

• Iannello, capitolo 5
• Chianese, Picariello, Moscato, Alla scoperta dei
fondamenti dell’informatica, capitoli 4 e 6
• A che C serve, capitolo 1 paragrafo 5

42
Il primo programma in C++

#include <iostream>
// Il primo programma in C++

int main()

{
std::cout<<"Hello, world!"<<std::endl;
return 0;
}

43
Il primo programma in C++

#include <iostream>
// Il primo programma in C++

using namespace std;

int main()
{
cout<<"Hello, world!"<<endl;
return 0;
}

44
Il programma equivalente in C

#include <stdio.h>
/* Il primo programma in C */

int main()
{
printf("Hello, world!\n");
return 0;
}

45
• Direttive per il precompilatore
– #include <iostream>
• Simboli speciali
– Operatori e delimitatori
• { } ; <<
• Parole chiave
– using namespace return
• Commenti
– // /* */
• Identificatori
– main cout
• Stringhe Costanti
– “Hello, World!”

46
Dev CPP

• E’ una IDE (Integrated Development Environment)


• Offre funzionalità di:
– Editing
– Compilazione
• Usa un compilatore C++ o C
– Nella versione scaricata si tratta di Mingw
– Il compilatore non è parte dell’ambiente e può essere sostituito
– Linking
• Anche in questo caso il linker è un modulo esterno
– Esecuzione
• Tramite il sistema operativo che fa da esecutore vero e proprio
– Debugging
• Ricerca ed eliminazione dei difetti nel codice sorgente

47
Dev CPP

– Ambiente di sviluppo software Dev C++


• Versione Orwell Dev C++ scaricabile all’indirizzo
(attualmente è arrivato alla versione 5.11):
http://sourceforge.net/projects/orwelldevcpp/
• Esiste una versione precedente, che funziona meglio
su sistemi precedenti Windows 7, scaricabile
gratuitamente da:
http://www.bloodshed.net/devcpp.html
Se non si ha già un compilatore C++ è opportuna scegliere il primo
download, che include anche il compilatore Mingw
• Oppure direttamente da:
• https://sourceforge.net/projects/orwelldevcpp/

48
Tipi semplici

• Tipi semplici
– bool
– int
– char
– float
– double

– Modificatori:
– short
– unsigned
– long

49
Tipi semplici

50
Variabili e costanti
• Costanti
– Definizione tramite precompilatore: #define
– Definizione per compilatore: const
• Variabili
– Dichiarazione
– Definizione
– Assegnazione
– Scope
• Variabili Globali e variabili locali

51
Esempi

• Esempio:
– Calcolo della lunghezza di una circonferenza
• L=2  r
– Controesempio
• Calcolo di 6 * 9
– Con e senza l’utilizzo di define
– Swap
• Date due variabili x, y scrivere un programma che inverta
il valore di x con quello di y
– Esempio: se x=3, y=4 il programma deve restituire x=4,
y=3

52
Casting dei tipi

• Conversione da un tipo all’altro


– Con perdita di informazione:
float y= 3.14;
int x= (int) y

– Senza approssimazione e perdita di informazione


int y= 3;
float x = (float) y

53
Operatori sovraccaricati

• Divisione reale
int x=13;
int y=4;
float z=(float)x/(float)y
• Z=3.25
• Divisione intera
int x=13;
int y=4;
float z=(int)x/(int)y;
• Z=3

54
Input dei dati

• cin e cout

• Esempio:
– Modifica degli esempi visti con l’introduzione dei valori
in ingresso

55
Costrutti di selezione if-then e if-then-else

if (condizione) if (condizione)
{
{
Blocco di
Blocco di istruzioni
istruzioni }
} else
{
Blocco di istruzioni
alternativo
}

57
Operatori logici

All’interno di una condizione logica possiamo utilizzare operatori come i


seguenti;

== (uguaglianza)
!= (disuguaglianza)
<
>
&& (and logico)
|| (or logico)
! (not logico)
Attenzione: = (diventa uguale a) non è la stessa cosa di == (è uguale a), ma per il
compilatore scrivere = in un’espressione logica non è un errore (in realtà
potrebbe essere una scorciatoia)

58
Esempi

Circonferenza
Aggiungere il controllo che verifica che il raggio sia
positivo e maggiore di zero

Multipli
– Valutare se dati due numeri naturali in ingresso, essi
sono multipli l’uno dell’altro

Dato un numero scrivere se è positivo, negativo o nullo, se è


pari o dispari e se è maggiore di -3

59
Selezione tra più scelte

if (cond 1°caso) switch(var)


{
{codice 1° caso} case valore1:
else if (cond 2°caso) {codice1}
break;
{codice 2° caso}
else if … case valore2:
{codice2};
break;
else //ultimo caso …
{codice ultimo caso} default:
{codice altri casi}
}

60
Esempi

• Dato un numero tra 1 e 12 scrivere a video il nome del


mese corrispondente
– Per esercizio: chiedere in input anche se vogliamo il
nome in inglese o in italiano
– E se volessimo ottenere il numero dato il mese?

61
Altri esempi

Scrivere un programma che riceva in ingresso due


numeri ed un’operazione aritmetica e stampi a
schermo il risultato dell’operazione
Iterazione: while-do

while (condizione)
{
Blocco di istruzioni
}

• Ripete il blocco di istruzioni 0 o più volte fin


quando la condizione è verificata

63
Iterazione: do-while

do
{
Blocco di istruzioni
}
while (condizione);

• A differenza del while-do, il blocco di istruzioni in


questo caso verrà ripetuto almeno una volta

64
Il problema della validazione dei dati in
input
• Ogni dato in input deve essere validato

• Modificare la validazione del valore del raggio della


circonferenza in modo da chiedere nuovamente il
valore nel caso in cui un valore non corretto fosse
immesso

66
Esempio DoWhile: Media voti

Scrivere un programma che calcoli il minimo, il


massimo e la media dei voti di un esame

Dopo ogni inserimento il programma chiede se tutti i


voti siano stati inseriti
Iterazione: for
for (prologo;condizione;continuazione)
{
Blocco di istruzioni
}

Equivalente a:

{
prologo;
while (condizione)
{
Blocco di istruzioni
continuazione;
}
}

68
Esempi

• Disegnare a video la tavola pitagorica

• Scrivere tutte le lettere dell’alfabeto in ordine inverso,


dalla zeta alla a

• Calcolare il massimo/minimo/media di n numeri

69
Esempio For: Fattoriale

Scrivere un programma che, dato un numero in


ingresso, calcoli il fattoriale del numero
Esempio For: elevazione a potenza

Scrivere un programma che riceva in ingresso un


numero intero x ed un numero intero y e calcoli z = xy
utilizzando un ciclo while
Esempio

• Implementare un piccolo gioco nel quale bisogna indovinare un


numero intero tra 1 e 100
– Ogni volta che il giocatore non indovina il numero, il
programma dice se il numero da indovinare è più alto o più
basso di quello provato

• Modifica
– Imporre che il numero debba essere indovinato al massimo
in 7 tentativi
– Per fare le prove è opportuno fissare un numero costante da indovinare
– Successivamente possiamo generare numeri casuali con la funzione rand()

72
Schema della soluzione

#include <time.h>
#include <cstdlib
#include <iostream>
using namespace std;
int main(){
int x; //x e' il numero da indovinare
x=42; //soluzione con valore costante
//SOLUZIONE CASUALE
//srand(time(NULL));
//x=1+rand()%100;

//DA COMPLETARE

return 0;}

73
Esempi di output
Dimmi un numero : 90
Dimmi un numero : 50 Di meno
Di meno Dimmi un numero : 80
Dimmi un numero : 15 Di meno
Di piu' Dimmi un numero : 70
Dimmi un numero : 38 Di meno
Di piu' Dimmi un numero : -5
Dimmi un numero : 42 Deve essere tra 1 e 100
Indovinato in 4 tentativi Dimmi un numero : 60
Premere un tasto per continuare . . . Di meno
Dimmi un numero : 50
Di meno
Dimmi un numero : 40
Di piu'
Dimmi un numero : 45
Di meno
Troppi tentativi
Premere un tasto per continuare . . .

74
Dati strutturati

• Sono composti da un insieme ordinato di dati semplici


– Ogni dato semplice è caratterizzato da tipo, valore,
attributo
• Il più semplice esempio di dato strutturato è dato dagli
array

76
Array

• Un array è una collezione ordinata di dati omogenei


– Tipo dei dati dell’array
– Meccanismo di memorizzazione degli array
– Dichiarazione/definizione di un array
Tipo nome [CARD] = {elem1,elem2,…};

– Accesso ai singoli elementi dell’array


nome[indice]

– Aritmetica degli indici

77
Esempi

• Calcolare la media di un insieme di 5 numeri


(versione con array)

• Calcolare il massimo di un insieme di n numeri (con


0<n<5, chiesto in input) – utilizzo di array

• Calcolare il massimo di un insieme di n numeri (con


n>0 senza limiti, chiesto in input) – utilizzo di array

78
Dimensione di un array

• La dimensione dichiarata di un array corrisponde allo


spazio ad esso dedicato in memoria
• int v[10] alloca un vettore di 10 interi in memoria
• E’ possibile anche utilizzare un array per allocare meno elementi
di quanti dichiarati
• Ad esempio, possiamo utilizzare v per memorizzare da 1 a 10
elementi
• Il C++ non ha però tecniche interne per poter gestire la
dimensione effettiva di un vettore
• Dobbiamo creare e utilizzare noi una variabile intera che ci
indica l’effettivo riempimento del vettore
• Ad esempio scriviamo int riemp=5 per ricordarci che il vettore in un
certo istante ha effettivamente 5 elementi

79
Operazioni sui vettori

• Dato un vettore di numeri interi


– Visualizzare il contenuto del vettore
– Trovare in che posizione si trova un dato valore
– Cancellare un elemento dal vettore
– Inserire un valore nel vettore in una determinata
posizione
– Risolvere i problemi precedenti anche nel caso in cui
il vettore è ordinato (ad esempio in ordine crescente)
– L’inserimento verrà effettuato in modo da rispettare l’ordine

80
Visualizzazione di un vettore

#define MAXV 7
int v[MAXV]={1,32,5,32,12,65,20};
int riemp=7;

cout<<"Vettore :";
for (int i=0;i<riemp;i++)
cout<<v[i]<<", ";
cout<<endl;

81
Ricerca di un elemento in un vettore
int v[MAXV]={1,32,5,32,12,65,20};
int riemp=MAXV;
cout<<"Valore da cercare? ";cin>>num;

bool trovato=false;
int i=0;
while (!trovato && i<riemp)
{
if (v[i]==num)
trovato=true;
else
i++;
}
if (trovato)
cout<<"Trovato in posizione "<<i<<endl;
else
cout<<"Non trovato"<<endl;

82
Ricerca di un elemento in un vettore
ordinato
int v[MAXV]={1,2,5,8,12,15,20};
int riemp=MAXV;
cout<<"Valore da cercare? ";cin>>num;

bool trovato=false, superato=false;


int i=0;
while (!trovato && i<riemp && !superato)
{
if (v[i]==num)
trovato=true;
else if (v[i]>num)
superato=true;
else
i++;
}
if (trovato)
cout<<"Trovato in posizione "<<i<<endl;
else
cout<<"Non trovato"<<endl;

83
Ricerca di tutte le occorrenze di un
elemento in un vettore ordinato
int v[7]={1,32,5,32,12,65,20};
int riemp=7;

int elemento;
cout<<"Valore dell'elemento da cercare :";
cin>>elemento;

bool trovato=false;
for(int i=0;i<riemp;i++)
if (v[i]==elemento){
cout<<"Trovato in posizione "<<i<<endl;
trovato=true;
}
if (!trovato)
cout<<"Non l'ho trovato"<<endl;

84
Ricerca di tutte le occorrenze di un
elemento in un vettore ordinato
Soluzione con while
int v[7]={1,32,5,32,12,65,20};
int riemp=7;

int elemento;
cout<<"Valore dell'elemento da cercare :";
cin>>elemento;

bool trovato=false;
int i=0;
while (i<riemp){
if (v[i]==elemento){
cout<<"Trovato in posizione "<<i<<endl;
trovato=true;
}
i++;
}
if (!trovato)
cout<<"Non l'ho trovato"<<endl;

85
Eliminazione di un elemento da un vettore
cout<<"Numero da eliminare? ";cin>>num;

bool trovato=false;
//utilizziamo l’algoritmo di ricerca per trovare la posizione i

if (trovato) Aggiorna il riempimento


{
riemp--;
for (int j=i;j<riemp;j++) Sposta verso sinistra
v[j]=v[j+1]; tutti i successivi
}
else
cout<<"Non trovato"<<endl;

L’algoritmo di eliminazione è indipendente dall’eventuale ordinamento degli


elementi del vettore (la parte di ricerca no, invece)

86
Eliminazione di un elemento da un vettore
(altra soluzione equivalente)
cout<<"Numero da eliminare? ";cin>>num;

bool trovato=false;
//utilizziamo l’algoritmo di ricerca per trovare la posizione i

if (trovato)
{
for (int j=i+1;j<riemp;j++)
v[j-1]=v[j]; Sposta verso sinistra
riemp--; tutti i successivi
}
else Aggiorna il riempimento
cout<<"Non trovato"<<endl;

L’algoritmo di eliminazione è indipendente dall’eventuale ordinamento degli


elementi del vettore (la parte di ricerca no, invece)

87
Inserimento di un elemento in un vettore
if (riemp<MAXV){
int num, pos;
cout<<"Numero da inserire? "; cin>>num;
cout<<“Posizione ? “; cin>>pos;

// Sposta verso destra tutti gli elementi a destra di pos


for (int j=riemp;j>pos;j--)
v[j]=v[j-1];

//Inserisci num in posizione pos


v[pos]=num;

//Aggiorna il riempimento
riemp++;
}

88
Inserimento di un elemento in un vettore ordinato
if (riemp<MAXV){
int num,pos=0;
cout<<"Numero da inserire? "; cin>>num;
//trova pos, indice del primo elemento uguale o superiore
//a quello da inserire
while (!trovato && pos<riemp && !superato){
if (v[pos]==num)
trovato=true;
else if (v[pos]>num)
superato=true;
else
pos++;
}
//Inserisci
for (j=riemp;j>pos;j--)
v[j]=v[j-1];
v[pos]=num;
riemp++;
}

89
Tipo char

• Il tipo char codifica i simboli del codice ASCII a 8 bit


• Per assegnare un valore ad un char
• char x=‘a’; //assegna al char che si chiama x il valore della lettera a del
codice ASCII
• char x=‘5’; //assegna al char che si chiama x il valore del numero 5 del
codice ASCII
• Il char è memorizzato come una sequenza di 8 bit che rappresenta un
valore tra 0 e 255 del codice ASCII
• I char possono essere convertiti ad int
• int n=x;
• Sui char possono essere eseguite delle operazioni aritmetiche
• Il risultato di ‘a’ + 1 è la lettera ‘b’
• Il risultato di ‘d’ – ‘a’ è il numero 3, poichè la differenza tra il codice ASCII di
‘d’ è quello di ‘a’ è 3

90
Tipo char

• Alcuni caratteri “speciali” possono essere assegnati


ad un char solo tramite speciali combinazioni. Ad
esempio:
• ‘\n’ è il carattere invio (da capo)
• ‘\t’ è il carattere tabulazione (alcuni spazi)
• ‘\’’ è il carattere ‘
• ‘\\’ è il carattere \
• ‘\0’ è il carattere numero zero del codice ASCII
• …

91
Stringhe

• Una parola può essere dichiarata come un array di


char (unica soluzione prevista in linguaggio C)
char parola[10];
• Particolarità
– Può essere assegnata tutta la stringa con un solo
comando, ma solo nell’istruzione di dichiarazione
char parola[]=“parola”;
char parola[10]=“parola”;
– Il settimo carattere di “parola” è un valore ASCII zero (‘\0’),
che indica la fine della stringa

92
Operazioni su stringhe

• Accesso a una lettera


parola[3] → accede alla quarta lettera della parola
• Lunghezza della stringa
int l = strlen(parola);
• Confronto tra stringhe
cfr=strcmp(parola1, parola2);
• Vale 0 se sono uguali, un numero negativo se parola1 precede parola2 in ordine
alfabetico, un numero positivo altrimenti
• Assegnazione
strcpy(parola2, parola1);
• Equivale a parola2=parola1 (ma questa sintassi sarebbe scorretta!)
• Concatenamento
strcat(parola1,parola2)
• Equivale a parola1=parola1+parola2 (ma anche questa sintassi sarebbe
scorretta)
• Input e output di stringhe
– Attenzione all’uso di cin !

– Le funzioni qui scritte sono nella libreria <cstring>

93
Esempio stringhe
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
char s[50]="parola1";
char t[50]="parola2";

cout<<strlen(s)<<endl;
cout<<strcat(s,t)<<endl;
cout<<strcmp(s,t)<<endl;

system("pause");
return 0;
}
94
Algoritmi con le stringhe

• Implementare funzioni equivalenti a quelle appena


viste:
– strlen
– strcmp
– strcpy
– strcat

95
strlen

char s[]=“parola1”;

int i=0;
Nota:
while (s[i]!=0) 0 (inteso come codice ASCII numero 0)
i++; è equivalente a ‘\0’
ma non è equivalente a ‘0’ (carattere 0
della tastiera, che ha codice ASCII 48)
cout<<i<<endl;

Oppure, in una sola riga


for (i=0;s[i]!=0;i++);
Oppure
for (i=0;s[i];i++);
96
strcpy
Oppure:
char s[10]; int i;
for (i=0;t[i]!=0;s[i]=t[i],i++);
char t[]=“parola”; s[i]='\0';

int i=0;
while (t[i]!=0){
s[i]=t[i];
i++;
}
s[i]=0;
cout<<s<<endl;

97
strcat

int i,j;
char s[]="parola1";
char t[]="parola2";
char u[100]; // oppure char u[strlen(s)+strlen(t)-1]

for (i = 0; s[i]!=0; i++)


u[i]=s[i];
for (j = 0; t[j]!=0; j++)
u[i+j] = t[j];
u[i+j]=0;

cout<<u<<endl;

98
Strcat
(soluzione alternativa con 3 contatori)
int i,j,k;
char s[]="parola1";
char t[]="parola2";
char u[100]; // oppure char u[strlen(s)+strlen(t)-1]

for (i = 0; s[i]!=0; i++)


u[i]=s[i];
for (j = 0,k=i; t[j]!=0; j++,k++)
u[k] = t[j];
u[k]=0;

cout<<u<<endl;

99
strcmp
char s[50];
char t[50];
int res;
cout<<"Prima parola : ";cin>>s;
cout<<"Seconda parola : ";cin>>t;

int cont=0;
while (s[cont]==t[cont] && s[cont]!=0)
cont++;

if (s[cont]==0 && t[cont]==0)


cout<<"Sono uguali"<<endl;
else if (s[cont]<t[cont])
cout<<s<<" precede "<<t<<endl;
else
cout<<t<<" precede "<<s<<endl;

100
Stringhe in C++ (cenno)

• Una implementazione del tipo di dato strutturato


parola disponibile in c++ è string
– Reperibile nella libreria string
• La classe string consente all’utente di operare con
le stringhe nascondendone l’effettiva realizzazione in
forma di array di char
• Assegnazione: parola2=parola1
• Confronto: parola2==parola1 (oppure <,<=,>,>=)
• Lunghezza: parola.size()
• Concatenazione: parola3=parola1+parola2

101
Esercizi

• Modificare la morra cinese per supportare l’utilizzo


delle stringhe (“sasso”, “forbici”, “carta”) e chiedere se
fare un’altra partita (“si”, “no”)

• Data una stringa in input visualizzare una stringa


contenente tutte le sue vocali e un’altra stringa
contenente tutte le sue consonanti

102
Matrici

• Con il termine matrice si intende, genericamente, un vettore a


due o più dimensioni
• Dichiarazione di una matrice quadrata
int a[10] [10];
• Dichiarazione di una matrice rettangolare con 2 righe e 4
colonne
int a[2][4];
• Dichiarazione di una matrice tridimensionale
int a[3][4][3];
• Memorizzazione per righe di una matrice bidimensionale
• Accesso ad un elemento di una matrice

103
Esercizi

• Data una matrice con 4 righe e 3 colonne, calcolare


la somma dei valori per ogni riga e per ogni colonna
• Data una matrice con 4 righe e 3 colonne, calcolare il
valore massimo per ogni riga e per ogni colonna

• Risolvere il problema dei mesi senza l’uso dello


switch
– Dato un numero tra 1 e 12, scrivere il nome del mese
corrispondente

104
Esercizio: Insiemi

• Si realizzi un programma che realizzi alcune operazioni insiemistiche.


• Il programma riceve in input la composizione di due insiemi, i cui elementi sono
numeri interi. A tale scopo, l’utente immette prima la dimensione dell’insieme (al
massimo 10 elementi), poi inserisce i valori degli elementi. Il programma
controlla che i valori inseriti nell’insieme siano tutti diversi tra loro.
• Il programma deve stampare a video il contenuto degli insiemi (non
necessariamente in ordine).
• Il programma deve realizzare alcune operazioni:
- unione dei due insiemi
- intersezione dei due insiemi
- differenza dei due insiemi.
• Ognuno degli insiemi calcolati deve essere memorizzato in un array e
visualizzato a video.
Suggerimento: conviene dichiarare queste variabili: int a[DIM]; int b[DIM];
int unione[DIM*2]; int inters[DIM]; int diff[DIM]; int riempa; int riempb;
int riempunione; int riempinters; int riempdiff; bool trovato;

105
Esercizio: Insiemi

Primo insieme
Secondo insieme
Inserisci il numero di elementi dell’insieme
Inserisci il numero di elementi dell’insieme
-1
3
Inserisci il numero di elementi dell’insieme
Inserisci il valore dell'elemento in posizione 0 : 4
15
Inserisci il valore dell'elemento in posizione 1 : 6
Inserisci il numero di elementi dell’insieme
Inserisci il valore dell'elemento in posizione 2 : 3
5
Insieme 1: 2, 3, 1, 5, 4,
Inserisci il valore dell'elemento in posizione 0 : 2
Insieme 2: 4, 6, 3,
Inserisci il valore dell'elemento in posizione 1 : 3
Unione : 2, 3, 1, 5, 4, 6,
Inserisci il valore dell'elemento in posizione 2 : 1
Intersezione : 3, 4,
Inserisci il valore dell'elemento in posizione 3 : 3
Differenza primo insieme - secondo insieme : 2, 1, 5,
L'elemento gia' esiste: non va bene
Differenza secondo insieme - primo insieme : 6,
Inserisci il valore dell'elemento in posizione 3 : 5
Premere un tasto per continuare . . .
Inserisci il valore dell'elemento in posizione 4 : 4

106
Struct

• Una struct (o record) può tenere insieme un insieme


limitato di dati eventualmente non omogenei

• Una struct è formata di campi, ognuno dei quali può


assumere un qualsiasi tipo
– E’ possibile che un campo di una struct sia a sua volta
una struct
– La struct è, quindi, un nuovo tipo strutturato definito
all’interno di un programma
– E’ possibile quindi dichiarare anche array o matrici
di struct

107
Struct

• Dichiarazione della struct


• struct nome_struct {
Tipo1 nome_campo_1;
..
tipoN nome_campo_N;
}
• Dichiarazione di una variabile di tipo struct:
• struct nome_struct nome_variabile;

• Definizione di una variabile di tipo struct:


• struct nome_struct nome_variabile={valore1,…valoreN}

• Accesso ad un campo in lettura o scrittura


• Nome_variabile.nome_campo=valore;
• Valore=nome_variabile.nome_campo

108
Struct

• Assegnazione
• Ad un campo alla volta
• Ad eccezione della definizione con = e parentesi graffe
• L’utilizzo di = per l’assegnazione potrebbe dare risultati inattesi
• Input, output
• Ad un campo alla volta
• Confronto
• Ad un campo alla volta
• L’operatore == Ha lo stesso comportamento del caso array
• Non esistono operatori come < o >
Esempi
//dichiaro il tipo struct data e una
//dichiaro il tipo struct persona ma non
//variabile oggi e assegno un valore ad oggi //la variabile struct persona
struct data struct persona
{ {
char nome [20];
int anno; char cognome [20];
int giorno; int peso;
char mese[20]; };
} oggi = {2012,7,”novembre”};
//dichiaro la variabile p
struct persona p;
//dichiaro la variabile domani e assegno un strcpy(p.nome,”Pinco”);
// valore strcpy(p.cognome,”Pallino”);
struct data domani = {2016, p.peso=75;
16,”novembre”}; cout<<p.nome<<p.cognome<<p.p
oggi.giorno=21; eso;
strcpy(oggi.mese,”dicembre”);
oggi.anno=2012;

110
Altro esempio

//dichiaro una struct che ha un campo che a sua


// volta è una struct (che deve essere stata già dichiarata
struct persona
{
char [20] nome;
char [20] cognome;
int peso;
struct data nascita;
};

struct persona p;
strcpy(p.nome,”Pinco”);
strcpy(p.cognome,”Pallino”);
p.peso=75
p.nascita.giorno=10;
strcpy(p.nascita.mese,”novembre”);
p.nascita.anno=2009;

111
Typedef

• Un modo più generale per creare nuovi tipi è dato dalla parola
typedef

typedef tipo_esistente nuovo_tipo

• Ad esempio:
typedef float reale;
reale x=3.5;
typedef struct persona individuo;
individuo x; x.peso=90;
typedef char[35] parola;
parola p=“parola”;

112
Puntatori

• Data una qualsiasi variabile, l’operatore & ci


restituisce l’indirizzo di memoria nel quale la variabile
viene memorizzata
– &x → indirizzo di x (puntatore ad x)

• L’operatore inverso è l’operatore di puntamento * che,


dato un puntatore restituisce il valore della variabile

113
Dichiarazione di un puntatore
int y;
int* x = &y;

• Dichiara una variabile y e un puntatore x che punta a valori interi e che


assume il valore dell’indirizzo di y
• Conseguenze:
cout<<x
• Stampa il valore dell’indirizzo di x
cout<<*x
• Stampa il valore puntato da x, ovvero il valore di y
*x=10
• Modifica il valore di y;
– in pratica ci sono due modi equivalenti per accedere al valore di y: con y o
con *x
*&y == &(*y) == y
• * e & sono operazioni inverse tra loro
• Per azzerare un puntatore:
• x=0; oppure x=NULL;

114
Puntatori a struct

• Nel caso di una struttura:


struct elemento {
int riga;
int colonna;
float valore;
} e;
• Un puntatore può essere definito come
struct elemento* ptr; ptr = &e;

• Per accedere alla riga dovremmo scrivere


(*ptr).riga
• Dove le parentesi servono per precedenza. Ma si può abbreviare con:
ptr->riga

• Se ptr1 e ptr2 sono due puntatori dello stesso tipo,scrivendo:


ptr1=ptr2
• Abbiamo due variabili che puntano alla stessa variabile (due alias)

115
Puntatori ad array

• Gli array rappresentano un caso particolare di puntatore


int v[3];
• v (senza indici) è esso stesso un puntatore
– v è anche l’indirizzo del primo elemento del vettore stesso

• Esempio:
int v[3]={25,28,34};
int* pzero=&v[0];
int* puno=&v[1];
cout<<v<<endl<<pzero<<endl<<puno<<endl<<&v<<endl;

116
Esercitazione: gioco dell’impiccato

Si realizzi un programma che simuli il gioco dell’ “impiccato”.


L’esecuzione deve svolgersi secondo le seguenti fasi:
• In input si chiede all'utente di inserire una parola da indovinare di
al più 20 caratteri, tutti alfabetici e minuscoli (il sistema chiederà
di re-immettere la parola se è troppo lunga e se contiene simboli
non ammessi);
• Il sistema visualizza a video la parola da indovinare, sostituendo
ad ogni lettera un segno -;
• Il sistema chiede ripetutamente all’utente una lettera e visualizza
la nuova stringa ottenuta sostituendo ai – la lettera immessa,
nelle posizioni in cui essa e’ presente nella stringa da indovinare;
• Il gioco termina quando l’intera stringa iniziale e’ stata
“scoperta”; in questo caso il sistema visualizza un messaggio di
congratulazioni.

Si utilizzino, eventualmente, le funzioni strlen, strcpy, strcmp

117
Esempio di output
Inserisci la stringa : Che lettera devo cercare? q
FraseConMaiuscole quara--a-u-
Contiene simboli non ammessi Che lettera devo cercare? s
Inserisci la stringa : quara--a-u-
unafrasedecisamentetroppolunga Che lettera devo cercare? t
Troppo lunga quara-ta-u-
Inserisci la stringa : Che lettera devo cercare? d
quarantadue quara-tadu-
Inizia il gioco: Che lettera devo cercare? e
----------- quara-tadue
Che lettera devo cercare? a Che lettera devo cercare? n
--a-a--a--- quarantadue
Che lettera devo cercare? r Bravo: hai indovinato in 9 tentativi!
--ara--a---
Che lettera devo cercare? u
-uara--a-u-

118
Esercizio: confronti tra date
Si realizzi un programma che legge una data scritta sotto forma di stringa,
nel formato gg-mm-aaaa.

Il programma dovrà:
• Leggere in input la data, controllando che sia esattamente nel formato
gg-mm-aaaa, ovvero con due cifre per il giorno seguite da un trattino,
seguito da due cifre per il mese seguite da un altro trattino e da quattro
cifre per l’anno;
• Effettuare alcuni controlli di validità, e precisamente che il giorno sia
compreso tra 1 e 31 e il mese tra 1 e 12. Inoltre si controlli che i valori in
ingresso non corrispondono a giorni inesistenti come il 31 aprile, 31
giugno, 31 settembre o 31 novembre o il 29, 30, 31 febbraio (per
semplicità si ignorino gli anni bisestili);
• Scrivere in output se la data è precedente o successiva al 17-12-2019 o
se coincida con quella data.

120
Esempio di output

Inserisci data Inserisci data


Inserisci la data nel formato gg-mm-aaaa : troppolunga Inserisci la data nel formato gg-mm-aaaa : 31-04-2019
Stringa troppo lunga Questo mese ha solo 30 giorni
Inserisci la data nel formato gg-mm-aaaa : 7-11-2019 Inserisci la data nel formato gg-mm-aaaa : 30-02-2019
La stringa non e' nel formato gg-mm-aaaa Questo mese ha solo 28 giorni
Inserisci la data nel formato gg-mm-aaaa : 07-11-19 Inserisci la data nel formato gg-mm-aaaa : 17-12-2019
La stringa non e' nel formato gg-mm-aaaa Le due date coincidono
Inserisci la data nel formato gg-mm-aaaa : 07-11-2019
7-11-2019 cade prima di 17-12-2019

121
Esercizio: istogramma
Si realizzi un programma che riceve in input un vettore di 5 numeri interi
compresi tra 0 e 9 ciascuno, che rappresentano i valori di una
grandezza, misurata ogni secondo.

Si realizzino un programma che:


- chiede in input i 5 valori e li memorizza in un vettore, controllando che
ogni valore sia compreso tra 0 e 9;
- mostra a video il vettore in forma di istogramma;

122
Esempio di output

Inserisci il primo vettore : Inserisci il primo vettore :


Inserisci il valore in posizione 0 :1 Inserisci il valore in posizione 0 :4
Inserisci il valore in posizione 1 :2 Inserisci il valore in posizione 1 :7
Inserisci il valore in posizione 2 :3 Inserisci il valore in posizione 2 :2
Inserisci il valore in posizione 3 :4 Inserisci il valore in posizione 3 :5
Inserisci il valore in posizione 4 :5 Inserisci il valore in posizione 4 :1
Istogramma: Istogramma:
9 9
8 8
7 7 *
6 6 *
5 * 5 * *
4 ** 4** *
3 *** 3** *
2 **** 2****
1***** 1*****

123
Sottoprogrammi e Funzioni

• Per risolvere un problema complesso, è opportuno scomporlo in


sottoproblemi
• Un sottoproblema può essere risolto da un sottoprogramma
• Analogamente ad un programma, un sottoprogramma ha un
insieme di dati in input e restituisce un insieme di dati in output
• Un sottoprogramma viene chiamato da un altro sottoprogramma,
il quale gli fornisce I valori di input e al quale vengono restituiti I
valori di output
– main è un caso particolare di sottoprogramma, che deve
essere presente in ogni programma C++
– In C++ i sottoprogrammi sono sempre codificati come
funzioni

124
Dichiarazione e definizione di una funzione

tipo restituito nomefunzione (lista di parametri)


{
Corpo della funzione
return valore;
}

• Il nome della funzione deve essere univoco


– È possibile dichiarare due funzioni omonime (overloading): lo si
studierà nei prossimi corsi di programmazione
• Ogni funzione restituisce un valore di un tipo scalare (anche un
puntatore). Valore deve appartenere a questo tipo
– Es.: main ha come tipo restituito int (e nei nostri esempi aveva
valore di ritorno 0)
• Così come le variabili, e’ possibile dichiarare una funzione senza
definirla subito
– La definizione deve essere però presente in un punto successivo
del programma

125
Chiamata di funzione

Variabile = nomefunzione (lista di parametri)

• Una funzione può essere chiamata da una qualsiasi altra


funzione dello stesso programma
• Variabile deve essere una variabile dello stesso tipo del valore
ritornato dalla funzione
– Variabile è opzionale: si può anche scrivere
nomefunzione (lista di parametri)
In questo caso il valore ritornato viene perduto
– Es.:
int l=strlen(s);
cout<<strlen(“parola”);

126
Passaggio dei parametri
• Concettualmente, si può distinguere tra
– Parametri di ingresso (in)
– Che possono essere letti ma non modificati
– Parametri di uscita (out)
– Che possono essere definiti e modificati
– Parametri di ingresso e uscita (inout)
– Che possono essere letti, definiti e modificati
• Tecnicamente, esistono tre modalità di passaggio dei
parametri, in C++
– Per valore
– Per puntatore
– Per riferimento
127
Passaggio per valore

• Con il passaggio per valore è possibile trasmettere il valore di un


dato ad una funzione
• Fondamentale per il passaggio dei dati di input

• Dichiarazione
int somma (int addendo1, int addendo2)

• Definizione
int somma (int addendo1, int addendo2)
{ return addendo1+addendo2;}

• Chiamata
int z=somma(3,5);
int z=somma(x,y);

128
Passaggio per valore

• Non è possibile (per le nostre attuali conoscenze) passare per


valore un dato strutturato (al più si può passare un puntatore)
• Le variabili addendo1 e addendo2 nell’esempio precedente
devono essere considerate come se fossero delle variabili locali
– il cui scope si limita al corpo della definizione della funzione
– Il cui valore iniziale è quello che gli giunge dalla chiamata
• Nell’esempio, le variabili x e y, il cui valore veniva trasferito ad
addendo1 e addendo2 non possono venire in nessun modo
toccate dall’esecuzione della funzione

• Il passaggio per valore è adatto al trasferimento delle sole


variabili di input

129
Passaggio per puntatore

• Come trasmettere dei parametri di output o di input e


output ad una funzione?
– E’ necessario che la funzione abbia la possibilità di
manipolare I valori delle variabili trasmesse
• L’unico meccanismo di scambio tecnicamente
possibile è quello per valore

• Soluzione:
Trasmettiamo per valore il puntatore alla variabile

130
Passaggio per puntatore

• Dichiarazione e definizione
void dimezza (float* x)
{
*x = *x / 2.0;
return;
}

• Chiamata:
float a=3.14;
dimezza(&a);
float* p=&a;
dimezza(p);
131
Passaggio per puntatore

• Nella chiamata viene effettivamente passato per valore


l’indirizzo (&a)
• Nella dichiarazione si dice che la funzione attende una variabile
x di tipo puntatore a float
• Nella definizione si sfrutta la conoscenza del puntatore x per
andare a modificare la variabile *x da esso puntato
– Ma *x coincide proprio con la variabile a definita nella
funzione chiamante

– Al momento della chiamata della funzione è come se avvenisse:


float *x=&a;
cioè parametro della funzione=valore inviato dal programma

132
Passaggio di una struct

• E’ consigliabile passare les struct per puntatore e non per valore


void visualizza (struct elemento* e)
{
cout<<e->valore;
return;
}

• Chiamata:
struct elemento s;

visualizza (&s);

Nella chiamata è come se avvenisse:


struct elemento* e=&s;

133
Passaggio di un array
• Un array può essere passato solo per puntatore
– Ma il nome dell’array è anche un puntatore ad esso

void visualizza (int e[]) //la dimensione non è necessaria,


// dato che era stato già definito altrove
// e in pratica è l’alias di un array definito altrove
{
cout<<e[0]<<e[1]<<e[2];
return;
}

• Chiamata:
int a[3];

visualizza (a);

• La dimensione dell’array può essere omessa dalla dichiarazione della funzione


– Ma bisogna essere attenti a non andare oltre la sua dimensione, nella funzione

Nella chiamata è come se avvenisse:


int e[]=a;

134
Passaggio per RiferimentoRi

Il passaggio per riferimento permette, così come il passaggio


per puntatore, di trasmettere variabili di output o input/output
all’interno di una funzione
– A differenza del passaggio per puntatore, bisogna
tenerne conto solo nella dichiarazione della funzione,
non nella chiamata, nè nella definizione

Dichiarazione
– tipo nomefunzione (tipo &nomevariabile)
Definizione
– { … nomevariabile = … }
Chiamata
– nomefunzione (variabile)

135
Passaggio per Riferimento

Dichiarazione e definizione
void dimezza (float &x)
{
x = x / 2.0;
return;
}

Chiamata:
float a=3.14;
dimezza(a);
cout<<a;

136
Esempi

Individuazione e trasformazioni di programmi già scritti in


funzioni:

– Circonferenza
– strcpy
– Ricerca in un vettore
- Insiemi
a

137
Confronti

• Il passaggio per puntatore e quello per riferimento sono


meccanismi diversi entrambi in grado di consentire il
passaggio di variabili verso una funzione
• Il passaggio per riferimento ha una sintassi più
semplice per cui è usato più spesso
• Il passaggio per riferimento diventa fondamentale
nel passaggio di oggetti in C++ (ci si occuperà di ciò solo
nel corso di Programmazione)
• – Il passaggio per puntatore è utilizzato di solito nel
passaggio di array
• – Il passaggio per puntatore è l’unico dei due previsto dal
linguaggio C

138
Allocazione statica, automatica e
dinamica
• Per allocazione statica si intende una allocazione in posizione
costante, per tutta la durata dell’esecuzione del programma (ad
es. allocazione di costanti o variabili globali)
• Per allocazione automatica si intende l’allocazione di variabili
locali ad un blocco
• E’ detta automatica perchè l’esecutore decide
automaticamente la posizione (nella memoria stack) dove
allocare le variabili e le distrugge automaticamente alla fine
dell’esecuzione del blocco (termine dello scope della
variabile)
– L’allocazione statica e quella automatica non ci consentono
di risolvere problemi come quello di gestire una lista di
elementi di lunghezza variabile, allocando, istante per
istante, solo lo spazio di memoria minimo indispensabile

139
Allocazione dinamica
• Per allocare una variabile da una istruzione
int* p = new int;
– p è un puntatore, ed è allocato automaticamente nella memoria
stack (è dichiarato come ogni altra variabile locale vista finora)
– *p, valore puntato da p è un int allocato dinamicamente nella
memoria heap all’atto dell’esecuzione dell’istruzione new
• Per distruggere la variabile dinamica puntata da p
delete p;
– Con questa istruzione viene distrutta *p, non il puntatore p, che
viceversa scompare quando termina naturalmente il proprio scope
– Le variabili allocate dinamicamente devono essere distrutte
esplicitamente perchè non vengono distrutte automaticamente
• Per allocare un array di n elementi
int* p=new int[n];
• Per distruggere un array
– delete [] p;

140
Esercizio

• Dato un numero intero, trasformarlo nel corrispondente numero


binario:
– Input:
• Numero intero positivo da convertire (al massimo 65535)
– Output:
• Array di 16 int al massimo (uno per ogni cifra binaria)

• Si realizzi un main che si occupi dell’input e una funzione dec2bin che


prende in input un intero e restituisce una stringa (con return oppure
come parametro di output)

141
Soluzione

//conversione da decimale a binario


void dec2bin(int d,char b[]){
int cont=0;
while(d>0){
b[cont]=d%2+'0';
d/=2;
cont++;
}
b[cont]=0;

//per invertire
for (int i=0;i<cont/2;i++){
char temp=b[i];
b[i]=b[cont-1-i];
b[cont-1-i]=temp;
}
return;
}

142
Soluzione

int main()
{
char bin[17];
int dec;

do{
cout<<"Inserisci il numero decimale :";
cin>>dec;
if (dec<0 || dec>65535)
cout<<"Deve essere un numero positivo compreso tra 0 e
65535"<<endl;
} while (dec<0 || dec>65535);

dec2bin(dec,bin);

cout<<"La conversione di "<<dec<<" e'"<< bin<<endl;


system("pause");
return 0;
}

143
Esercitazione

• Sia data una matrice di 3 righe e 4 colonne di numeri interi


positivi
• Si calcolino:
– Il prodotto di tutti i numeri della prima riga
– Il massimo tra tutti i numeri della seconda e della terza
colonna
– L’ultimo numero pari della seconda riga
– Il più piccolo numero sulla diagonale principale (cioè per il
quale il numero di riga è uguale al numero di colonna)
• Avvertenza:
– Controllare che i valori della matrice siano effettivamente
positivi

144
Esempio

Valore dell'elemento in riga 0 e colonna 0 :3


Valore dell'elemento in riga 0 e colonna 1 :4
Valore dell'elemento in riga 0 e colonna 2 :9
Valore dell'elemento in riga 0 e colonna 3 :2

3 4 9 2
Valore dell'elemento in riga 1 e colonna 0 :5
Valore dell'elemento in riga 1 e colonna 1 :8
Valore dell'elemento in riga 1 e colonna 2 :11
Valore dell'elemento in riga 1 e colonna 3 :6

5 8 11 6
Valore dell'elemento in riga 2 e colonna 0 :7
Valore dell'elemento in riga 2 e colonna 1 :1
Valore dell'elemento in riga 2 e colonna 2 :12
Valore dell'elemento in riga 2 e colonna 3 :10
Prodotto dei numeri della prima riga = 216
Massimo di seconda e terza colonna = 12
L'ultimo numero pari della seconda riga e' 6
7 1 12 10
Il minimo sulla diagonale principale vale 3
Premere un tasto per continuare . . .

145
Soluzione proposta 1/3
# include <iostream>

using namespace std;

int main()
{
int m[3][4];
int i,j;
for (i=0;i<3;i++)
for (j=0;j<4;j++)
{
do
{
cout<<"Valore dell'elemento in riga "<<i<<" e colonna "<<j<<" :";
cin>>m[i][j];
}while (!(m[i][j]>=0));
}
int prodotto=1;
for (i=0;i<4;i++)
prodotto*=m[0][i];
cout<<"Prodotto dei numeri della prima riga = "<<prodotto<<endl;

146
Soluzione proposta 2/3
int massimo=0;
for (i=0;i<3;i++)
{
if (m[i][1]>massimo)
massimo=m[i][1];
if (m[i][2]>massimo)
massimo=m[i][2];
}
cout<<"Massimo di seconda riga e terza colonna = "<<massimo<<endl;

i=3;
int pari=1;
do
{
if (m[1][i]%2==0)
pari=m[1][i];
else
i--;
} while ((pari==1)&&(i>=0));
if (pari==1)
cout<<"Non ci sono numeri pari"<<endl;
else
cout<<"L'ultimo numero pari della seconda riga e' "<<pari<<endl;

147
Soluzione proposta 3/3

int minimo=m[0][0];
for (i=1;i<3;i++)
if (m[i][i]<minimo)
minimo=m[i][i];
cout<<"Il minimo sulla diagonale
principale vale "<<minimo<<endl;

system("pause");
return 0;
}

148
Lista

• Una Lista, a differenza di un array, ha un numero variabile di elementi


ed è tale da allocare, in ogni istante, un quantitativo di memoria pari a
quella minima necessaria

struct Elemento {
int valore;
struct Elemento* prossimo;
};
typedef struct Elemento* Lista;

• La definizione di lista coincide con quella di un indirizzo di un elemento


(il primo elemento della lista stessa, collegato al secondo, che è
collegato al terzo e così via)
• prossimo è un puntatore all’elemento successivo della lista
• L’ultimo elemento della lista punterà ad un elemento inesistente,
rappresentato dalla costante NULL (solitamente pari a 0, valore
sicuramente non utilizzabile per un indirizzo)

149
Esempio: riempimento (con i valori 4,5,7)

struct Elemento {
int valore;
struct Elemento* prossimo
};
typedef struct Elemento* Lista;

Lista l=new struct Elemento;


l->valore=4;
l->prossimo = new struct Elemento;
l->prossimo->valore=5;
l->prossimo->prossimo= new struct Elemento;
l->prossimo->prossimo->valore=7;
l->prossimo->prossimo->prossimo=NULL;

150
Riempimento di una lista con i valori di un
vettore
//trasforma un array in una lista

void riempiLista(Lista& l,int v[],int riemp){


l=new struct Elemento;
Lista p=l;
for (int i=0;i<riemp;i++){
p->valore=v[i];
if (i<riemp-1)
p->prossimo=new struct Elemento;
else
p->prossimo=NULL;
p=p->prossimo;
}
return;
}

151
Esempio: visualizzazione della lista

void visualizza (Lista l){


//p e' un puntatore che viene utilizzato
transitoriamente
Lista p=l;
while (p!=NULL)
{
cout<<p->valore<<endl;
p=p->prossimo;
}
}
In realta', grazie al passaggio per valore, potevo anche utilizzare l invece di p

153
Soluzione errata

while (l!=NULL)
{
cout<<l->valore<<endl;
l=l->prossimo;
}
Questa soluzione non va bene perchè durante il ciclo viene modificato il
puntatore l alla lista, che alla fine vale NULL.
Quindi, dopo questo ciclo, non abbiamo più modo di ricordare dove
cominciasse la nostra lista.
Bisogna sempre operare su di una copia (alias) del puntatore!

154
Esempio: cerca un elemento in una lista

bool cerca (Lista l, int valore){


Lista p=l;
while (p!=NULL)
{
if (p->valore==valore)
return true;
p=p->prossimo;
}
return false;
}

155
Esempio: inserimento in testa

//inserisci un elemento all'inizio di una lista

void inserisciInTesta(Lista &l,int valore){


//Crea un nuovo elemento
Lista e=new struct Elemento;
e->valore=valore;
e->prossimo=l;
//attenzione all’ordine delle operazioni!
l=e;
return;
}

156
Eliminazione del primo elemento della
lista
//elimina il primo elemento della lista (se c'è)
void eliminaPrimo(Lista &l){
Lista p=l;
if (l!=NULL){
l=l->prossimo;
delete p;
}
return;
}

160
Eliminazione: caso generale (1)

//elimina l'elemento valore dalla lista (se c'è)


void elimina(Lista &l,int valore){
Lista p=l;
struct Elemento* precedente=NULL;
bool trovato=false;
while (p!=NULL && !trovato)
if (p->valore!=valore){
precedente=p;
p=p->prossimo;
} else
trovato=true;

161
Eliminazione: caso generale (2)

if (trovato){
//l'elemento trovato è p, preceduto da precedente
if (precedente==NULL){
//E' il primo elemento
l=p->prossimo;
} else {
precedente->prossimo=p->prossimo;
}
delete p;
}
return;
}

162
Esempio: Deallocazione di tutta la lista

void dealloca(Lista &l){


while (l!=NULL)
{
Lista q=l;
l=l->prossimo;
delete q;
}
l=NULL;
}
166
Librerie di funzioni

Una funzione può risultare utile in più di un programma:

• E’ utile raccogliere un insieme di funzioni che hanno


qualcosa in comune all’interno di una stessa libreria

• Una libreria può essere vista come un file con la


dichiarazione e la definizione di un insieme di funzioni, tra
le quali non c’è, però, la funzione main

167
Esempio di Progetto

• Obiettivo: separare le funzioni di Insiemi in modo da poterle utilizzare in


un programma qualsiasi

• In un file header insiemi.h sistemiamo tutte le dichiarazioni delle


funzioni
• In un file Insiemi.cpp sistemiamo le definizioni di tutte le funzioni
sugli insiemi
• In un programma qualsiasi UsaInsiemi.cpp definiamo una
funzione main che utilizza liberamente tutte le funzioni sugli
insiemi
• UsaInsiemi.cpp deve avere anche una riga #include
“Insiemi.h”
• Tutti i file creati (insiemi.h, Insiemi.cpp, UsaInsiemi.cpp) fanno parte
dello stesso progetto (sono aperti nello stesso DevCpp)

168
Compilazione separata

• Molto spesso è utile separare la dichiarazione delle


funzioni dalla loro definizione
– Vantaggi:
• E’ possibile distribuire, in una libreria, direttamente
la versione compilata della definizione delle funzioni
(il file oggetto)
• consentendo agli utenti di utilizzarla, ma non di
modificarla
• Risparmiando la fatica di doverla ricompilare ad ogni
programma che la utilizza

• I file contenenti tutte le dichiarazioni sono detti header files


e, di solito, hanno estensione .h

169
Definizione ed uso di funzioni di libreria

• Nel file Lista.cpp definisce le funzioni della libreria (che sono


solo dichiarate in lista.h) dovrà esserci
#include ".\lista.h“
• Che fa includere fisicamente il contenuto di lista.h (che si trova
nella stessa cartella di lista.cpp) all’interno di lista.cpp stesso al
momento della compilazione
•lista.cpp ha bisogno di conoscere le strutture dati dichiarate
in lista.h
• Allo stesso modo, anche Main.cpp deve includere lista.h
•Main.cpp ha bisogno di conoscere le strutture dati e le
funzioni dichiarate in lista.h (e definite in lista.cpp)

170
Esercizio Insiemi.h

• Trasformare l’esercizio con le funzioni sugli insiemi in un


progetto composto di più file cpp e h

171
Esercitazione: Tombola

• Si realizzi un programma che simuli le estrazioni dei numeri nel gioco della
“tombola”.
• Il sistema deve estrarre un numero casuale tra 1 e 90 e lo scrive a video.
Bisogna controllare che il numero estratto non sia stato già estratto in
precedenza. Dopo ogni estrazione, vengono visualizzati tutti i numeri estratti, in
ordine a partire dal più recente. Le estrazioni continuano fino a quando non
siano stati estratti tutti e 90 i numeri.

• Vincoli di progetto:
• - Si realizzi il programma organizzandolo su più file, uno con tutte le funzioni,
uno con tutte le dichiarazioni e uno con il main;
• - Per memorizzare i numeri estratti si utilizzi una lista;
• - Si realizzino, in particolare, le funzioni visualizza (che visualizza tutti i
numeri estratti), estrai (che estrae un numero a caso, controllando che non sia
già uscito, e lo aggiunge all’inizio della lista) e cerca (che cerca un numero tra
quelli già estratti), scegliendo opportunamente i parametri.
• Per generare numeri casuali, si può utilizzare la funzione rand() contenuta in
<cstdlib>

173
Tombola: Output
Gioco della tombola
Ho estratto il numero 42
Numeri usciti fino ad ora: 42 –
Ho estratto il numero 18
Numeri usciti fino ad ora: 18 - 42 –
Ho estratto il numero 35
Numeri usciti fino ad ora: 35 - 18 - 42 –
Ho estratto il numero 41
Numeri usciti fino ad ora: 41 - 35 - 18 - 42 –
Ho estratto il numero 90
Numeri usciti fino ad ora: 90 - 41 - 35 - 18 - 42 –

Ho estratto il numero 45
Numeri usciti fino ad ora: 45 - 21 - 86 - 73 - 27 - 16 - 89 - 69 - 14 - 30 – 44 - 38 - 54 - 2 - 79 - 32 -
56 - 60 - 1 - 68 - 8 - 11 - 81 - 76 - 7 - 61 - 28 - 31 - 50 - 13 - 74 - 57 - 40 - 72 - 17 - 55 - 24 - 10 -
39 - 67 - 71 - 46 - 5 – 47 - 43 - 51 - 77 - 59 - 80 - 83 - 15 - 78 - 29 - 22 - 34 - 4 - 3 - 84 - 85 - 20 -
70 - 12 - 48 - 6 - 9 - 87 - 52 - 23 - 64 - 33 - 25 - 82 - 37 - 58 - 63 - 26 – 88 - 62 - 66 - 36 - 75 - 53 -
19 - 49 - 65 - 90 - 41 - 35 - 18 - 42 –
Premere un tasto per continuare . . .

174
Tombola: FunzioniTombola.h

struct Elemento{
int valore;
struct Elemento* successivo;
};
typedef struct Elemento* Lista;

int estrai(Lista& primo);


void visualizza(Lista primo);
bool cerca(Lista primo, int numero);

175
Tombola: FunzioniTombola.cpp
int estrai(Lista &primo){
int estratto;
#include <iostream>
do {
#include <cstdlib>
estratto=1+rand()%90;
#include ".\FunzioniTombola.h"
} while (cerca(primo,estratto));
using namespace std;
Elemento *e=new Elemento;
e->valore=estratto;
e->successivo=primo;
bool cerca(Lista primo,int estratto){
primo=e;
Lista p=primo;
return estratto;
while (p!=NULL){
}
if (p->valore==estratto)
return true;
void visualizza(Lista primo){
p=p->successivo;
Lista p=primo;
}
while (p!=NULL)
return false;
{
} cout<<p->valore<<" - ";
p=p->successivo;
}
return;
}

176
Tombola: Main.cpp
#include <iostream>
#include ".\FunzioniTombola.h"

using namespace std;

int main()
{
Lista l=NULL;
cout<<"Gioco della tombola";
int estratti=0;
do{
cout<<endl<<"Ho estratto il numero "<<estrai(l)<<endl;
cout<<"Numeri usciti fino ad ora: ";
visualizza(l);
estratti++;
} while (estratti<90);

//cout<<"Deallocazione di tutta la Lista"<<endl;


//dealloca(Lista);
system("pause");
return 0;
}

177
Esercizio

• Dato un numero intero codificato in binario, trasformarlo nel


corrispondente numero intero
– Input:
• Numero in binario (da inserire come stringa, ovvero array di
char)
– Output:
• Numero intero corrispondente

• Avvertenze:
– Controllare se il numero di cifre è un intero compreso
tra 1 e 16
– Controllare se l’input è un numero binario valido

178
Esempio di esecuzione

• Suggerimenti;
– Per calcolare il valore si ricordi che:
– Ma é anche possibile scrivere:

Esempio di esecuzione

Immetti il numero binario : 101x1011


Deve essere 0 o 1
Immetti il numero binario : 10231021
Deve essere 0 o 1
Immetti il numero binario : 10110101
Risultato = 181
Premere un tasto per continuare . . .

179
Soluzione proposta
# include <iostream> if (!ok)
cout<<"NUmero binario non valido"<<endl;
using namespace std; else if (i>16)
//conversione da binario a decimale cout<<"Troppo lungo"<<endl;

int main() }while(!ok || i>16);


{
char bin[17];
bool ok;
int i; int dec=0;
do{ for (int j=0;bin[j]!=0;j++)
ok=true; dec=dec*2+(bin[j]-'0');
cout<<"Inserisci numero binario:";
cin>>bin; cout<<"Il numero decimale e' :
i=0; "<<dec<<endl;
while (bin[i]!=0 && ok){
if (bin[i]!='0' && bin[i]!='1') system("pause");
ok=false; return 0;
else }
i++;
}

180
File

• Sono utilizzati per memorizzare dati in maniera persistente su disco

• I file sono sequenze di elementi omogenei (come gli array o le stringhe)

• Un file termina con un apposito carattere EOF (end of file, come le


stringhe)

• Un file può essere letto o scritto come uno stream (come cin e cout),
tramite gli operatori << e >>

• Per poter operare su di un file è necessario prima aprirlo. Per rendere


definitive tutte le operazioni fatte sul file, è necessario chiuderlo.

181
Operazioni sui file

• Dichiarazione tipi ifstream e of ofstream


• #include <fstream.h>
• ifstream in_file;
• ofstream out_file;
• Apertura dei file
• file.open(“c:\testo.txt”)
• Chiusura dei file
• file.close()
• Lettura
• in_file>>x;
• Scrittura
• out_file<<x;
• Controlli
• Avvenuta apertura: file.is_open()
• Fallimento di un’operazione file.fail()
• Raggiunta terminazione in_file.eof()

182
Esercizio con i file

• Dato in input un qualsiasi file di testo


• – Ad esempio lo stesso che si sta scrivendo
• Fare una statistica della frequenza con la quale occorrono i caratteri del
codice ASCII e scrivere in output la classifica dei dieci caratteri piu‘
comuni
• L’output deve essere scritto sia a video che in un file
“classifica.txt”
• • Suggerimenti:
• Si ricordi che un char è rappresentato sia dal carattere che dal
suo codice ASCII, a seconda del contesto
• Conviene utilizzare un vettore per memorizzare la frequenza dei
caratteri
• Si può sviluppare una funzione
void cercamax(int n[256],int &max,int &posmax)

183
Ricorsione

• Può una funzione richiamare sé stessa?


• Tecnica della ricorsione

• La ricorsione risolve un problema in termini di sé stesso, fino a


ricondursi ad un caso base di cui è nota la soluzione
• Analogo al principio di induzione utilizzato per dimostrare
teoremi

• Il caso «base» deve avere una soluzione banale


• I casi «generali» devono ricondurre la soluzione ad una esecuzione del
problema stesso
• I casi «generali» devono portare, prima o poi, al caso «base»

184
Ricorsione

• Esempio
• Calcolo della potenza con base e esponenti interi
• be = b * be-1
• b0 = 1

• int potenza(int base,int esp){


if (esp==0)
return 1;
else
return base*potenza(base,esp-1);
}

185
Esempi di problemi risolvibili con la
ricorsione
• Calcolo del fattoriale
• Caso base: 1!=1
• Caso generale: n! = n* (n-1)!

• Ricerca di un valore in un vettore ordinato (crescente)


• Partiamo dal centro del vettore
• Caso base: abbiamo trovato il valore
• Casi generali:
• Abbiamo trovato un valore più grande: ripetiamo la ricerca su
un vettore che va dall’inizio all’elemento appena provato
• Abbiamo trovato un valore più piccolo: ripetiamo la ricerca su
un vettore che va dall’elemento appena provato alla fine
• Osservazione: al massimo facciamo un numero di tentativi uguale
a log2 (dimensione vettore)

186
Esempi di problemi risolvibili con la
ricorsione
bool ricercaBin(int x, int v[], int start, int end, int &dove)
{
if (start > end) //non possiamo trovarlo
return false;
else {
int mid = (start + end) / 2; //punto centrale
if (x==v[mid]){
dove=mid; //trovato nel punto centrale
return true;
}
else if (x < v[mid])
//cerca nella prima meta’
return ricercaBin(x,v,start,mid-1,dove);
else
// cerca nella seconda meta’
return ricercaBin(x,v,mid+1,end,dove);
}
}

187
Break, return, exit

• Ricapitolando, esistono tre diverse istruzioni di uscita:

• break ;
• causa l’uscita da un blocco di codice (parentesi graffe)
• Non ci sono parametri associati a break;
• E’ sconsigliata (tranne che nello switch) poiché una piccola modifica del
programma (introduzione di un altro blocco) potrebbe cambiarne l’esito;
• return valore ;
• Causa l’uscita immediata da una funzione
• Può essere seguita da un parametro (scalare) del tipo specificato nella
dichiarazione della funzione (a meno che non sia specificato il tipo void, vuoto)
• exit(valore);
• Causa l’uscita immediata da tutto il programma
• Ha un parametro tra parentesi del tipo corrispondente a quello restituito dalla
funzione main (di solito int). Il valore di questo parametro sarà restituito al sistema
operativo

188
Debugging

• Il Debugging è la tecnica necessaria a tempo di esecuzione per


• trovare i difetti dei programmi
• Correggerli

• Una volta trovato il malfunzionamento (ad esempio risultato sbagliato


oppure uscita dal programma con errore) bisogna trovarne la causa
esaminando nel dettaglio il funzionamento del programma

• Due soluzioni:
• Inserire all’interno del programma una o più sonde (probe), ovvero
istruzioni cout che ci scrivono a video qualcosa riguardo l’attuale stato
del programma
• Sfruttare le funzioni messe a disposizione dal programma gdb integrato
in Dev-Cpp

189
Strumenti per il Debugging

• Breakpoint
• Un breakpoint può essere inserito nel codice semplicemente
con un click sul numero di linea di codice. Un cerchio rosso
verrà disegnato
• Esecuzione in modalità debug
• Scegliendo di eseguire in modalità debug, l’applicazione verrà
eseguita normalmente fino a quando non si incontra un
breakpoint: a quel punto è possibile:
• Eseguire una sola istruzione (Step Succ.)
• Eseguire solo fino al termine di quella funzione (Step Est.)
• Eseguire una istruzione alla volta la funzione che si sta per chiamare
(Step Interno)
• Osservare il valore di una qualsiasi variabile in quel momento oppure
chiedere il risultato di una qualsiasi operazione (Nuova Osservazione)
• I valori delle variabili e delle espressioni controllate sono visibili
nella scheda Debug (accanto a quella Progetto)

190
Esempio Debugging
Algoritmi di ordinamento

• Dato un array di elementi con valori ordinabili, riordinarlo


in ordine crescente (o decrescente)
• Tra le soluzioni possibili, vedremo:
• Select Sort
• Ordinamento per selezione iterativa dei minimi
(massimi)
• Bubble Sort
• Ordinamento per “bolle”

192
Selection Sort

• Detto anche “selezione per minimi (massimi) successivi”

• Al primo passo si cerca il minimo (massimo) tra tutti


gli elementi e lo si mette in prima posizione
• Ad ogni passo i successivo si cerca il minimo
(massimo) degli elementi non ancora ordinati (dall’i-
esimo all’ultimo) e lo si mette in posizione i, fino al
completamento del vettore ordinato

193
Selection Sort
void SelectSort(int a[], int n){
int i,j,posmin;
for (i=0;i<n-1;i++){ //dal primo al penultimo
// trova il minimo tra i e l’ultimo
posmin=i;
for (j=i+1;j<n;j++)
if (a[j] < a[posmin])
posmin = j;
// se non e’ già al posto giusto mettilo in posizione i
if (posmin!=i){
int temp = a[i];
a[i]=a[posmin];
a[posmin]=temp;
}
}
}
194
Bubble Sort

• Detto anche ordinamento “a bolle” perchè si cerca di


scambiare elementi più “leggeri” con elementi più
“pesanti” al fine di metterli complessivamente in ordine

• Si innestano due cicli, uno esterno su tutti gli elementi,


uno più interno sugli ultimi elementi
• Ogni volta che si trova una coppia di elementi “in
disordine” li si scambia nel caso di ordinamento crescente,
tali che il più grande preceda il più piccolo

195
Bubble Sort

void BubbleSort0 (int a[], int n) {


int i,j;
for(i=0;i<n-1;i++)
for (j=0;j<n-1-i;j++)
if (a[j] > a[j+1]){
temp = a[j+1];
a[j+1]=a[j];
a[j]=temp;
}
}

196
Bubble Sort

void BubbleSortVeloce (int a[], int n){


bool bubble=true;
int i=0;

while(i<n-1 && bubble) {


bubble = false;
for (j=0;j<n-1-i;j++)
if (a[j] > a[j+1]) {
scambia(a,j,j+1);
bubble = true;
}
i++;
}
}

197
Calcolabilità e Trattabilità

- Alle Radici dell’Informatica, capitolo 3, paragrafi 3.3 4 3.4

198
Macchina di Turing

Una Macchina di Turing:


– Ha un nastro illimitato
– Sa leggere e scrivere una casella per volta
– Ricorda lo stato in cui si trova
– Legge dal nastro
– Scrive sul nastro
– Può cambiare stato
– Può cambiare direzione

199
Macchina di Turing

Formalmente:

Macchina di Turing (A,S,fm,fs,fd)


– Simboli A (ingressi e uscite sono simboli ∈ A)
– Stati S
– Funzione di macchina fm: AxS→A
– Funzione di stato fs: AxS→S
– Funzione di direzione fd: AxS→{Sinistra, Destra, Nessuna}

200
Macchina di Turing e Calcolabilità

• E’ il prototipo di macchina universale perchè potrebbe leggere


l’algoritmo da eseguire se codificato con simboli sul nastro

• La Macchina di Turing può essere teoricamente in grado di riprodurre


il comportamento di ogni macchina reale

• Tesi di Church-Turing
• Non esiste alcun formalismo in grado di risolvere problemi che
non siano risolvibili tramite una macchina di Turing

• I problemi risolvibili con una macchina di Turing sono detti decidibili


(altrimenti sono indecidibili)

• La macchina di Von Neumann è una riduzione della macchina di


Turing (ha una memoria limitata) che però è stata alla base delle
realizzazioni pratiche di calcolatori

201
Trattabilità

• Un problema calcolabile (secondo Turing) potrebbe non


essere trattabile, nel senso che le risorse (in termini di
spazio e tempo) necessarie per risolverlo potrebbero
essere nettamente superiori di quelle a disposizione

• Complessità computazionale
• Spaziale
• Temporale

• Ordini di grandezza
• Complessità asintotica
• Confronto con algoritmi di complessità nota

202
Complessità computazionale

• Dato un problema che coinvolge n dati, quale è


l’ordine di grandezza del tempo necessario a
risolverlo con algoritmi che hanno complessità
temporale dell’ordine di n, n2, n3, n5, 2n, 3n ?

203
Esempi di complessità computazionale

• La ricerca di un elemento in un array di n elementi ha bisogno al


massimo di:
• n confronti con l’algoritmo di ricerca lineare
• log2n confronti con l’algoritmo di ricerca binaria

• Per inserire un elemento in una lista ordinata sono necessari al


massimo:
• 1 inserimento e n spostamenti con un array
• 1 inserimento e 1 modifica di puntatore con una lista
concatenata

• Per calcolare l’unione di 2 insiemi di m ed n (con m<n) elementi


sono necessari al massimo n+m inserimenti ed m confronti

204

Potrebbero piacerti anche