Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
1
Il linguaggio C
Puntatori e dintorni
2
Puntatori : idea di base
• In C è possibile conoscere e denotarel’indirizzo della cella di memoria in cui èmemorizzata una variabile (il puntatore)
• es :int a = 50; /* una var intera */
int * b; /* una var puntatore a interi */
...b = &a; /* assegna a b l’indirizzo dellacella in cui è memorizzata a */
3
Puntatori : idea di base (2)
• In C è possibile conoscere e denotarel’indirizzo della cella di memoria in cui èmemorizzata una variabile (il puntatore)
• es :int a = 50;
int * b;
…b = &a;
50350
a è memorizzata nella cella 350
...450
4
Puntatori : idea di base (3)
• nometype *– è il tipo degli indirizzi delle variabili di tiponometype
• es :int a = 50;int * b;
…
b = &a;
50350
b è memorizzata nella cella 450 (&b)
...450
tipo dei puntatori a intero
5
Puntatori : idea di base (4)
• Operatore &– denota l’indirizzo della cella di memoria in cui
è memorizzata una variabile (il puntatore)• es :int a = 50;
int * b;
…b = &a;
50350
Dopo questo assegnamento in b è memorizzato l’indirizzo di a
350450
6
Puntatori : idea di base (5)
• Operatore di dereferenziazione ‘ * ’– è possibile conoscere e/o modificare il
contenuto di una variabile manipolandodirettamente il suo puntatore
• es :int a = 50;
int * b = &a; …
*b = *b + 4;
54350
Dopo questo assegnamento in a è memorizzato il valore 50 + 4
350450
Denota la variabilea indirizzo b
7
Puntatori : idea di base (6)• NULL
– costante predefinita (in stdio.h) che denotail puntatore nullo
• È possibile definire puntatori per tutti i tipibase e le strutture con (*)– double *a, *b; /* ripetere ‘*’ */– int *a, b, c[4], **d;– struct studente *t1;
• Segnaposto ( %p )– stampa il valore dell’indirizzo in notazione
esadecimale
8
Aritmetica dei puntatori
• È possibile scrivere espressioni puntatoreusando alcuni degli usuali operatoriaritmetici (+, -, --, ++)– int a[3], *p = &a[0]; ……IN+12
IN+8IN+4IN
a[2]a[1]a[0]
p contiene l’indirizzo IN
9
Aritmetica dei puntatori (2)
• È possibile scrivere espressioni puntatoreusando alcuni degli usuali operatoriaritmeticiint a[3], *p = &a[0];p = p + 1;
……IN+12IN+8IN+4IN
a[2]a[1]a[0]
p contiene l’indirizzo IN + 4
10
Aritmetica dei puntatori (3)
• È possibile scrivere espressioni puntatoreusando alcuni degli usuali operatoriaritmeticiint a[3], *p = &a[0];p = p + 1;p--;
……IN+12IN+8IN+4IN
a[2]a[1]a[0]
p contiene l’indirizzo IN
11
Aritmetica dei puntatori (4)
• È possibile scrivere espressioni puntatoreusando alcuni degli usuali operatoriaritmetici (+, -, --, ++)int a[3], *p = &a[0];p = p + 1;p--;p += 3;
……IN+12IN+8IN+4IN
a[2]a[1]a[0]
p contiene l’indirizzo IN + 12(sizeof(int) == 4…..)
12
Aritmetica dei puntatori (5)
• È possibile scrivere espressioni puntatoreusando alcuni degli usuali operatoriaritmetici (+, -, --, ++)int a[3],*p = &a[0],*q;p = p + 1;p--;q = p;p += 3;a[0] = p - q;
……IN+12IN+8IN+4IN
a[2]a[1]a[0]
a[0] contiene 3, numero di intmemorizzabili fra p e q
13
Puntatori e array…
• Il nome di un array è il puntatore (costante)al primo elemento dell’arrayint a[3], *p = &a[0], *q;q = a; ……IN+12
IN+8IN+4IN
a[2]a[1]a[0]
q contiene l’indirizzo INa == IN
14
Puntatori e array… (2)
• L’operatore [-] è una abbreviazione …int a[3], *p = &a[0], *q, tmp;/* i due stm che seguono sonoequivalenti */
tmp = a[2];tmp = *(a+2);
……a+3a+2a+1a
a[2]a[1]a[0]
15
Puntatori e array… (3)• L’operatore [–] è una abbreviazione … e
può essere usato con una qualsiasivariabile puntatoreint a[3],*p = a,*q,tmp;tmp = a[2];tmp = p[2];
……a+3a+2a+1a
a[2]a[1]a[0]
16
Puntatori e array… (4)• I seguenti frammenti di codice sono
equivalentiint a[N], *p = a, *q, tmp;int sum = 0;/* versione 1 */for(i = 0; i < N; i++) sum += a[i];/* versione 2 */for(i = 0; i < N; i++) sum += *(a+i);
17
Puntatori e array… (5)• I seguenti frammenti di codice sono
equivalenti (segue)int a[N], *p = &a[0], *q, tmp;int sum = 0;/* versione 3 */for(i = 0; i < N; i++) sum += p[i];/* versione 4 */for(p = a; p < (a+N); p++) sum += *p;
18
Puntatori e array… (6)• Una riflessione sulle stringhe
– le stringhe sono array di caratterichar a[7] = “ciao”;– le stringhe costanti possono essere definite
anche comeconst char * pp = “ciao”;char * pp = “ciao”;
• attenzione! Se a questo punto cercate di modificareun elemento di pp (es. pp[2] = `f`;) avete unerrore a run time
• mentre a[2] = `f`; è completamente corretto
19
Passaggio di parametri perriferimento
• Tutti i parametri delle funzioni C sonopassati per valore– il loro valore viene copiato sullo stack– ogni modifica al parametro nel corpo della
funzione non modifica l’originale• È possibile realizzare passaggi per
riferimento utilizzando i puntatori– i passaggi per riferimento permettono di
modificare il valore di una variabilenell’ambiente del chiamante
20
Passaggio di parametri perriferimento (2)
• Esempio : la funzione che scambia fra loro ivalori di due variabili– si potrebbe pensare di programmarla come ...
void scambia (int x, int y){ int tmp;
tmp = x; x = y; y = tmp;}
– e poi chiamare scambia(a,b)
21
Passaggio di parametri perriferimento (3)
• Esempio : la funzione che scambia fra loro ivalori di due variabili– si potrebbe pensare di programmarla come ...
void scambia (int x, int y){ int tmp;
tmp = x; x = y; y = tmp;}
– non funziona! Perché lo scambio viene fattosulle copie
22
Passaggio di parametri perriferimento (3.1)
• Esempio : la funzione che scambia fra loro ivalori di due variabili– esempio di chiamata
int a = 4, b = 5;scambia(a,b);
45
45
Framechiamante
&a&b
Framescambia
&x&y
stack
23
Passaggio di parametri perriferimento (3.2)
• Esempio : la funzione che scambia fra loro ivalori di due variabili– alla fine dell’esecuzione di scambia (prima di
ritornare al chiamante)int a = 4, b = 5;scambia(a,b); 4
5
54
Framechiamante
&a&b
Framescambia
&x&y
24
Passaggio di parametri perriferimento (3.3)
• Esempio : la funzione che scambia fra loro ivalori di due variabili– al momento di eseguire la printf (il frame di
scambia non è più significativo)int a = 4, b = 5;scambia(a,b);printf(“%d,%d”,a,b);
45
Framechiamante
&a&b
25
Passaggio di parametri perriferimento (4)
• Esempio : la funzione che scambia fra loro ivalori di due variabili– la versione corretta è ...
void scambia (int *x, int *y){ int tmp;
tmp = *x; *x = *y; *y = tmp;}
– con chiamata scambia(&a,&b)
26
Passaggio di parametri perriferimento (4.1)
• Versione corretta di scambia ...– esempio di chiamata
int a = 4, b = 5;scambia(&a,&b);
45
&a&b
Framechiamante
&a&b
Framescambia
&x&y
stack
Ogni modifica a *x modificail valore di a nell’ambiente del chiamante
27
Passaggio di parametri perriferimento (4.2)
• Esempio : versione corretta di scambia ...– alla fine dell’esecuzione di scambia (prima di
ritornare al chiamante)int a = 4, b = 5;scambia(&a,&b);
54
&a&b
Framechiamante
&a&b
Framescambia
&x&y
28
Passaggio di parametri perriferimento (4.3)
• Esempio : versione corretta di scambia ...– al momento di eseguire la printf (il frame di
scambia non è più significativo)int a = 4, b = 5;scambia(&a,&b);printf(“%d,%d”,a,b);
54
Framechiamante
&a&b
29
Passaggio di parametri perriferimento (5)
• ATTENZIONE : gli array sono passatisempre per riferimento perché quello che sipassa è il nome dell’array
void assegna (int x[ ]) { x[0] = 13;}
– con chiamataint a[10];assegna(a);/* qua a[0] vale 13 */
30
Passaggio di parametri perriferimento (6)
• Inoltre : le due scritturevoid assegna (int x[ ]){ x[0] = 13;}
evoid assegna (int *x){ x[0] = 13;}
– sono del tutto equivalenti– si preferisce usare la prima per leggibilità
31
Passaggio di parametri perriferimento (7)
• Tipicamente le funzioni che lavorano suarray hanno un secondo parametro chefornisce la lunghezza
int somma (int x[ ], int l){ int i, s = 0; for(i = 0; i < l; i++) s += x[i]; return s;}
– somma tutti gli elementi di un array intero dilunghezza l
32
Passaggio di parametri perriferimento (8)
• Per gli array multidimensionali la cosa è piùcomplessa!!!!
int somma (int x[ ][4], int l){ int i, j, s = 0; for(i = 0; i < l; i++) for(j = 0; j < 4; j++) s += x[i][j]; return s;}
– invece di 4 posso usare N costante
33
Passaggio di parametri perriferimento (9)
• Perché dobbiamo specificare l’ampiezza diuna seconda dimensione di un array ?– Dipende dalla strategia di memorizzazione per
gli array multidimensionali• es: int a[2][3]={{1,2,3},{4,5,6}};
123456
a&a[0][1]&a[0][2]&a[1][0]&a[1][1]&a[1][2]
a[i][j] ha come indirizzo a+i*3+j
100104108112116120
34
Passaggio di parametri perriferimento (10)
• Se non conosco la lunghezza della secondadimensione il compilatore non riesce agenerare codice corretto
int somma (int x[ ][4], int l){ int i, j, s = 0; for(i = 0; i < l; i++) for(j = 0; j < 4; j++) s += x[i][j]; return s;}
35
Passaggio di parametri perriferimento (11)
• Esiste un modo migliore di quello appenaesaminato per la rappresentazione degli arraymultidimensionali in C : lo vedremo in seguito
36
E le strutture ???
• Le strutture vengono sempre passate pervalore
• Se una struttura contiene un array, alloral’array viene copiato!– Attenzione quando si passano strutture con
campi array molto grandi!• Se voglio modificare una struttura devo
sempre utilizzare i puntatori!
37
E le strutture ??? (2)
• Esempio
typedef struct studente { char nom_cogn[40]; unsigned int matricola;} studente;
void scambia (studente * s1,studente * s2);
38
E le strutture ??? (3)
• Esempio
void scambia (studente * s1,studente * s2) {
…(*s1).matricola = 4; s1->matricola = 4;/*sono equivalenti */}
39
E le strutture ??? (4)
• Esempio : Come si dichiara una lista in C ?– Usando i puntatori
typedef struct nodo { struct nodo * next; int info;} nodo;
Mi serve il nome dellastruttura !
40
Allocazione dinamica dellamemoria
• La creazione di nuove variabili a run time(tipo new in Java) viene effettuata in Cutilizzando le funzioni di libreria standardche permettono l’allocazione dinamica diporzioni contigue di memoria– malloc, calloc, realloc– #include <stdlib.h>
Con queste primitive è possibile crearedinamicamente variabili e array di dimensionenon nota a priori
41
Array dinamici -- malloc( )
• Vediamo come creare dinamicamente un arraydi 10 posizioni
int * a; /*conterrà il puntatore al primo elemento dell’array*/a = malloc(10*sizeof(int));
heap
40 byte
Punta allʼindirizzo iniziale della nuovaarea allocata 100
96
42
Array dinamici -- malloc( ) (2)
• Vediamo come creare dinamicamente un arraydi 10 posizioni
int * a; /*conterrà il puntatore al primo elemento dell’array*/a = malloc(10*sizeof(int));if (a == NULL) printf(“fallimento!\n”);
heap
Se malloc non riesce ad allocarelʼarea di memoria richiesta restituisce NULL(verificare…)
43
Array dinamici -- malloc( ) (3)
• Vediamo come creare dinamicamente un arraydi 10 posizioni
int * a; /*conterrà il puntatore al primo elemento dell’array*/a = malloc(10*sizeof(int));if (a == NULL) printf(“fallimento!\n”);else { a[4] = 345;… }
heapLʼarray si può accedere con i consuetioperatori (come se fosse statico)
40 byte10096
44
Array dinamici -- malloc( ) (4)
• malloc non inizializza la memoria a 0!– Possiamo inizializzarla esplicitamente o– usare calloc
int * a; /*conterrà il puntatore al primo elemento dell’array*/a = calloc(10*sizeof(int));If (a == NULL) printf(“fallimento!\n”);else { a[4] = 345;… }
45
Array dinamici -- realloc( )
• realloc modifica la lunghezza di un’areaallocata precedentemente
int * a, * b; /*puntatori al primo elemento dell’array*/a = malloc(10*sizeof(int));…b = realloc(a,20*sizeof(int));/* adesso b punta ad un array di 20elementi */
46
Array dinamici -- realloc( ) (2)
• Meglio usare sempre due puntatori diversi (a,b) !– Altrimenti in caso di fallimento NULL sovrascrive il
vecchio puntatore
47
Array dinamici -- free( )
• Lo spazio allocato sullo heap non vienedeallocato all’uscita delle funzioni
• La deallocazione deve essere richiestaesplicitamente usando free
int * a;a = malloc(10*sizeof(int));…free(a);/* se qua accedo di nuovo ad apuò succedere di tutto */
48
tipo puntatore generico: void *
• non si può dereferenziare• è necessario un cast prima di manipolare la
variabile puntata– Es.
void * c;int a;c = &a;*c = 5; /* scorretto*/*(int *)c = 5; /* corretto*/
49
tipo puntatore generico: void * (2)
• Serve a scrivere funzioni ‘polimorfe’ in unamaniera un po’ brutale
• Esempio– il tipo della malloc è
void * malloc (unsigned int size);
– quando scrivoint * a;a = malloc(10*sizeof(int));
viene effettuato un cast implicito a (int *)
50
tipo puntatore generico: void * (3)
• Tipi delle altre funzioni di allocazione edeallocazione
void * calloc (unsigned int size);void * realloc (void * ptr, unsigned int size);void free (void * ptr);
51
I puntatori a funzione
• Consideriamo la funzioneint somma (int x, int y) { return x + y;}
– se proviamo ad eseguireprintf(“%p”, somma);
otteniamo un valore esadecimale cherappresenta un indirizzo legale del nostroprogramma
– ??????????????????????????
52
I puntatori a funzione (2)
• Consideriamo la funzioneint somma (int x, int y) { return x + y;}
Codice compilato di somma
IND
somma è un puntatore costante con valore pari a IND
53
I puntatori a funzione (3)
• Consideriamo la funzioneint somma (int x, int y) { return x + y;}
/* variabile di tipo funzione(int,int)->int */
int (*fun) (int,int);int a;
fun = somma;a = fun(3,5);
54
I puntatori a funzione (4)
• Consideriamo la funzioneint somma (int x, int y) { return x + y;}
/* variabile di tipo funzione(int,int)->int */
int (*fun) (int,int);int a;
fun = somma;a = fun(3,5);
Ma a che serve????????????
55
I puntatori a funzione (5)• Serve a definire funzioni che prendono
come argomenti altre funzioni (di ordinesuperiore)
void map (int (*fun) (int), int x[ ], int l) { for(i = 0; i < l; i++) x[i] = fun(x[i]);}
è un iteratore che applica la funzione fun atutti gli elementi dell’array x
56
I puntatori a funzione (6)• Esempio di uso della map
int piu_uno (int x) { return x+1;}int quad (int x) { return x*x;}…int a[3] = {3,4,5};map(piu_uno,a,3); /* somma uno atutti gli elementi */
map(quad,a,2); /* eleva al quadratoi primi due elementi */
57
Argomenti della linea di comando• Gli argomenti della linea di comando sono
accessibili all’interno della funzione main– il SO li inserisce sullo stack prima di attivare il
processo– il formato in cui sono resi disponibili è fisso
int main (int argc, char * argv[ ]) { …}
Numero di argomentinella linea di comando
Array di puntatoriagli argomenti(ciascuno è unastringa, tipo char*)
58
Argomenti della linea di comando (2)• Esempio
%> a.out una stringa per a.out
5
argc
argv p e r \O
a . o u t \O
s t r i n g
u n a \O
a \O
a . o u t \O
argv[0]
59
Argomenti della linea di comando (3)• Esempio : Schema di programma che stampa
gli argomenti sulla linea di comandoint main (int argc, char * argv[ ]) { … for(i = 0; i < argc; i++) printf(“arg %d: %s”, i, argv[i]);…
}
60
Array multidimensionali comearray di puntatori
• Vogliamo permettere la definizione difunzioni su array multidimensionali che nondipendono dal valore delle dimensionisuccessive alla prima
int sum_mat (int ** MAT, int n, int m) {
…
}
Nome della matriceNumero di righe, colonne
61
Array multidimensionali comearray di puntatori (2)
• Vogliamo accedere agli elementi dell’arrayusando la solita notazione [-][-]
int sum_mat (int ** MAT, int n, int m) { … MAT[i][j] = …;
}*(MAT + i*m + j)
Il compilatore dovrebbe conoscere il legame fra questidue oggetti
62
Array multidimensionali comearray di puntatori (3)
• Una alternativa: abbandonare l’allocazionecontigua per righe
MAT
7 2 6 2
1 2 3 4
4 6 1 2
4 5 1 2
MAT[0]
MAT[1]3 5 7 9
MAT[2]
MAT[3]
MAT[4]
MAT[2][2]
63
Array multidimensionali comearray di puntatori (4)
• Una alternativa: vediamo i tipi ...
MAT
7 2 6 2
1 2 3 4
4 6 1 2
4 5 1 2
MAT[0]
MAT[1]3 5 7 9
MAT[2]
MAT[3]
MAT[4]
MAT[2][2]int**
int*[5]
int[4]
64
Array multidimensionali comearray di puntatori (5)
• Una alternativa: in memorianon ho più aree contigue ...
MAT
1 2 3 4
4 6 1 2
MAT[0]
3 5 7 9
40 44 48 52
40 120140400 4
70 86827874
120 124 128 132 140 144 148 152
MAT[1] MAT[2]
indirizzo
valore
65
Array multidimensionali comearray di puntatori (6)
• Perché funziona?
int sum_mat (int ** MAT, int n, int m) { … MAT[i][j] = …;
}
*(*(MAT + i) + j)Questo legame non interessa più!
66
Array multidimensionali comearray di puntatori (7)
• Funzione di allocazioneint ** mat_new(unsigned int m, unsigned int n) {
int i, ** a, errore = FALSE;a = malloc(m*sizeof(int*));if (a == NULL) return NULL;for(i = 0; (i < m) && (!errore); i++) {
a[i] = malloc(n*sizeof(int)); if (a[i] == NULL) errore = TRUE; } if (errore) /* gestione errori */ else return a;}
67
Array multidimensionali comearray di puntatori (8)
• Funzione di deallocazione
void mat_free(int ** a, unsigned int m) { int i; /* dealloco tutte le righe */ for(i = 0; i < m; i++) free(a[i]); /* dealloco l’array dei puntatori alle
righe */ free(a);}
68
assert( )• Permettono di fare controlli di consistenza a
tempo di esecuzione– prevenire meglio che curare …– esempio: un indice i è davvero dentro i limiti
dell’array?int x, a[N], i;……assert(i < N);x = a[i];
69
assert( ) (2)#include <assert.h>
…assert(expr);– se expr è falsa il sistema stampa un messaggio
di errore e termina– se expr è vera non ha alcun effetto!
70
assert( ) (3)• Le assert costano!• Quando usarle
– per effettuare controlli di consistenza dopochiamate a codice complesso
• tipicamente non abbiamo i sorgenti (se è scritto daaltri)
• … e comunque anche se li abbiamo non è maigarantita la correttezza!
– si controlla se i valori risultanti sonorispondenti alle specifiche fornite
– si controllano invarianti noti dell’applicazioneche stiamo scrivendo