Esplora E-book
Categorie
Esplora Audiolibri
Categorie
Esplora Riviste
Categorie
Esplora Documenti
Categorie
Puntatore
I puntatori
Ver. 2.4
E una variabile che contiene lindirizzo di memoria di un oggetto (variabile o costante) Esempio p un puntatore e punta 12 A55 x alla variabile x che in ... A54 questo esempio ha indirizzo ... di memoria A55) A53 int x=12; x:A55 ... A52
12
p
A55
2010 - Claudio Fornaro - Corso di programmazione in C
A51 A50
... A55
Definizione
Assegnazione
Sintassi tipo *nomeVariabile; Esempi int *punt; char x, *p, *q, y; x e y sono variabili di tipo char p e q sono variabili di tipo puntatore-a-char
Ad un puntatore si possono assegnare solo indirizzi di memoria o il valore NULL che indica che il puntatore non punta a nulla La costante 0 in un contesto dove atteso un puntatore (inizializzazione e assegnamento di un puntatore, confronto con un puntatore) viene convertita in NULL dal compilatore Loperatore di indirizzo & (ampersand) calcola lindirizzo di memoria di una variabile (si dice che ne restituisce il puntatore) int *p = &x; inizializza p con lindirizzo di x assegna a p lindirizzo di y p = &y;
Utilizzo
Utilizzo
Si accede alloggetto puntato da un puntatore per mezzo delloperatore * detto operatore di deriferimento (dereference) o di indirezione (indirection) int x=12, *p = &x; p punta a x *p = 24; x ora vale 24 *p += 6; x ora vale 30 p il puntatore *p loggetto puntato (qui x) Si noti che la sintassi per indicare loggetto puntato e quella della definizione del puntatore sono identiche: *p
Sia p sia *p sono L-value modificabili, ossia sono un qualcosa (variabile, elemento di vettore, ) a cui si pu assegnare un valore Loperatore * ha priorit superiore a quella degli operatori matematici x = 6 * *p; equivale a: x = 6 * (*p); Per visualizzare il valore di un puntatore si pu utilizzare la direttiva %p in una printf
Tipi e puntatori
Puntatori a void
Linformazione relativa al tipo necessaria per permettere ai puntatori di conoscere la dimensione delloggetto puntato (usata nellaritmetica dei puntatori) Poich oggetti di tipo diverso possono avere dimensione diversa, lassegnazione tra puntatori di tipo diverso in genere errata e il compilatore genera un warning int *p, x=12; long *q, y=26; p = &x; OK! q = &y; OK! q = p; NO! Warning q = &x; NO! Warning
Sono puntatori generici e non possono essere dereferenziati (non si pu scrivere *p), possono essere utilizzati solo come contenitori temporanei di valori di tipo puntatore (a qualsiasi tipo) void *h; Non serve il cast (void *) per copiare un puntatore non-void in un puntatore void h = p; (supponendo ad esempio int *p) Qualsiasi tipo di puntatore pu essere confrontato con un puntatore a void NULL definito come: (void *)0
10
Puntatori a void
Puntatori e vettori
Per dereferenziare il valore di un puntatore a void necessario assegnarlo ad un puntatore al tipo appropriato (non void) per poter conoscere la dimensione delloggetto puntato Pu essere necessario il cast (tipo *) per copiare un puntatore void in un puntatore non-void (i compilatori C non lo richiedono, i compilatori C++ s). In riferimento allesempio precedente: int *q; q = h; OK, compilatore C q = (int *)h; OK, compilatore C++ *q = 23; x ora contiene 23
Il nome (senza parentesi) di un vettore-di-T un valore costante di tipo puntatore-a-T, corrisponde allindirizzo di memoria del primo elemento di vettore int vett[100]; int *p; p = vett; lindirizzo di memoria di vett viene messo in p, equivale a scrivere: p = &vett[0] (le parentesi hanno priorit maggiore di &)
11
12
Puntatori e vettori
Attenzione: vett = p; NO! Non si pu assegnare un valore a vett in quanto NON una variabile puntatore, ma un sinonimo di un indirizzo di memoria Gli indirizzi di memoria sono valori costanti stabiliti dal compilatore, non sono variabili e quindi non hanno uno spazio in memoria modificabile per contenere un valore Il termine puntatore viene comunemente (e impropriamente) usato al posto di indirizzo di memoria (es. &a d il puntatore ad a)
Una variabile di tipo puntatore-a-T, assegnata in modo che punti a (cio contenga lindirizzo di) un oggetto di tipo vettore-di-T , pu essere utilizzata come se fosse un vettore-di-T int vett[25]; int *p = vett; vett: p Ad esempio, qui p[3] equivale a vett[3] Il compilatore internamente trasforma le espressioni con notazione vettoriale [] in espressioni con i puntatori
13
14
Quando un puntatore punta ad un vettore, gli pu essere sommato un valore intero N, il risultato lindirizzo di memoria dellelemento di posizione N del vettore vett:
32 15 23 55 32 11 27
Listruzione p++ porta p a puntare a vett[1] (ne contiene lindirizzo) quindi ora p punta a vett[1] e p[3] corrisponde a vett[4] vett:
32 15 23 55 32 11 27
p p+3 p punta a (contiene l'indirizzo di) vett p+3 punta a (produce l'indir. di) vett[3] *(p+3) equivale a vett[3]
p p+3 E lecito sottrarre un valore N ad un puntatore se lelemento puntato risultante fa ancora parte del vettore (nellesempio precedente p-1 punta a vett[0])
15
16
Il nome di un vettore pu essere utilizzato come puntatore costante : *(vett+2) equivale a: vett[2] vett++ un errore (vett costante!) Il nome di un vettore di T (es. int) che dovrebbe essere una costante di tipo puntatore-a-vettore-di-T (o meglio indirizzo-di-vettore-di-T ), in C decade al tipo puntatore-a-T Grazie al decadimento vett visto come puntatore (costante) a int e punta al suo primo elemento e vett+1 punta a vett[1]
Senza il decadimento, vett+1 punterebbe al primo byte dopo la fine di vett Eccezioni:
vett in un sizeof (descritto altrove) non decade e per questo d la dimensione dellintero vettore, non quella del primo elemento &vett inibisce il decadimento e produce una quantit costante di tipo puntatore-a-vettore-di-int
17
18
Una variabile di tipo puntatore-a-T non sa se il valore (scalare) a cui punta singolo o lelemento di un vettore, lo sa solo il programmatore e sta a questi utilizzarlo in modo coerente int x = 10, vett[10], *p, *q; p = &x; p++; NO! Non esiste loggetto puntato da p+1 q = p+1; NO! Idem p = vett; p++; SI! Ora p punta a vett[1] q = p+1; SI! Ora q punta a vett[2]
Attenzione che il puntatore non sfori i limiti del vettore (lo standard non richiede che il compilatore faccia controlli, molti compilatori lo offrono opzionalmente, ma ci riduce le prestazioni) E lecito che un puntatore punti a quello che sarebbe lelemento del vettore successivo allultimo, ma questo puntatore pu essere utilizzato solo per calcolare la differenza tra puntatori (vedere prossima slide)
19
20
Priorit delloperatore *
Due puntatori a elementi dello stesso vettore possono essere sottratti, il valore ottenuto + 1 il numero di elementi del vettore compresi tra quelli puntati dai due puntatori (inclusi): p = &vett[4]; q = &vett[10]; d = q-p+1; 7: numero degli elementi dalla posizione 4 alla posizione 10 inclusi Due puntatori possono essere confrontati solo se fanno parte dello stesso vettore oppure uno dei due NULL (o 0)
Dalla tabella delle priorit si vede che loperatore di deriferimento * ha priorit quasi massima, inferiore solo alle parentesi (e a -> e a .) e associativit da destra a sinistra Quindi, considerando che gli operatori * e ++ hanno stessa priorit e associativit da D a S: *p++ equivale a *(p++) incrementa p *++p equivale a *(++p) incrementa p ++*p equivale a ++(*p) incrementa *p inoltre: (*p)++ incrementa *p *p+1 equivale a (*p)+1 e non a *(p+1)
Copia di stringhe
1a versione
21
Copia di stringhe
2a versione
22
La stringa y viene copiata in x char x[30], y[30], *t=x, *s=y; int i=0; gets(y); while (s[i] != '\0') { t[i] = s[i]; i++; } t[i] = '\0'; printf("%s\n", x); Il '\0' viene copiato fuori dal ciclo Qui s e t vengono inutilmente usati come semplici sinonimi di x e y, non come puntatori
La stringa y viene copiata in x char x[30], y[30], *t=x, *s=y; int i=0; gets(y); while ((t[i] = s[i]) != '\0') i++; printf("%s\n", x); Il '\0' viene copiato nel ciclo stesso Qui s e t vengono inutilmente usati come semplici sinonimi di x e y, non come puntatori Nota: non si pu scrivere t[i] = s[i++] nella condizione del while (side effect)
Copia di stringhe
3a versione
23
Copia di stringhe
4a versione
24
La stringa y viene copiata in x char x[30], y[30], *t=x, *s=y; gets(y); while ( (*t = *s) != '\0') { t++; s++; } printf("%s\n", x); Il '\0' viene copiato nel ciclo stesso Nota: != '\0' pu essere omesso
La stringa y viene copiata in x char x[30], y[30], *t=x, *s=y; gets(y); while (*t++ = *s++) ; printf("%s\n", x); Il '\0' viene copiato nel ciclo stesso
25
26
Puntatori e stringhe
Puntatori e stringhe
char str[100]; RISERVA spazio per contenere i caratteri, una variabile e il suo contenuto pu essere modificato char *s; NON RISERVA spazio per contenere i caratteri, quindi per essere utilizzata come stringa le si deve assegnare una stringa vera:
char str[] = "ciao"; E linizializzazione di una variabile stringa: il compilatore riserva memoria per str e vi copia i caratteri di "ciao"; la stringa costante "ciao" non esiste in memoria: stata usata dal compilatore per inizializzare la stringa str, ma esiste in memoria la stringa variabile "ciao" str[0]='m'; SI char *s = "hello"; E linizializzazione di una variabile puntatore: il compilatore determina lindirizzo della stringa costante "hello" (che esiste in memoria) e lo assegna alla variabile puntatore s s[0]='b'; NO! "hello" costante!
27
28
Puntatori e stringhe
Puntatori e stringhe
s = "salve"; E lassegnazione ad una variabile puntatore: il compilatore determina lindirizzo della stringa costante "salve" (che esiste in memoria) e lo assegna alla variabile puntatore s s[4]='o'; NO! "salve" costante! s = str; E lassegnazione ad una variabile puntatore: il compilatore determina lindirizzo della stringa variabile str (che esiste in memoria) e lo assegna alla variabile puntatore s s[0]='m'; SI
Noti i puntatori, si possono completare le informazioni gi date sulle funzioni relative alle stringhe aggiungendo quanto segue:
le funzioni di copia di stringhe strcpy, strncpy, strcat e strncat restituiscono il puntatore alla stringa di destinazione le funzioni di parsing strchr, strrchr, strstr, strpbrk e strtok restituiscono il puntatore alloggetto cercato o NULL se non lo trovano
29
30
qualsiasi, il carattere di codice ASCII 0 ('\0') un carattere normale e non viene utilizzato come terminatore (non c alcun terminatore) Una porzione di memoria (allocata in qualsiasi modo) viene trattata come generico blocco di byte da alcune funzioni contenute in <string.h> Per riservare una porzione di memoria si pu definire una variabile o una stringa o utilizzare la funzione malloc (trattata in altre slide) Nelle seguenti funzioni, s (source) e t (target ) sono puntatori a blocchi di byte
memcpy(t,s,n) copia n byte da s a t memmove(t,s,n) copia n byte da s a t (i blocchi possono anche essere sovrapposti) memcmp(s,t,n) confronta byte per byte secondo il codice ASCII i primi n byte di s e di t, il valore restituito un int come quello della strcmp memchr(s,c,n) cerca il byte c tra i primi n byte di s, d NULL se non lo trova o il puntatore ad esso se trova memset(s,c,n) copia il byte c in tutti i primi n byte di s
Puntatori const
31
Puntatori const
32
Puntatori const
33
34
Esercizi
1.
Si scriva un programma che chieda una stringa allutente e conti quanti siano i caratteri che la costituiscono, NON si usi la funzione strlen della libreria standard. Scrivere un programma che verifichi se la stringa data in input palindroma o no Scrivere un programma che chieda n valori interi (massimo 100), li collochi in un vettore e inverta il vettore (scambiando il primo elemento con l'ultimo, il secondo con il penultimo, etc.). Si usi la notazione vettoriale. Come il precedente, ma si usino i puntatori.
2.
3.
4.
35
36
Esercizi
5.
Esercizi
Scrivere un programma che chieda N valori (massimo 100), li collochi in un vettore e li ordini in senso crescente (senza usare vettori ausiliari). 9. Scrivere un programma che verifichi se la stringa data composta di due parti uguali, trascurando il carattere centrale se la lunghezza dispari (es. CiaoCiao, CiaoXCiao). 10. Scrivere un programma che date 2 stringhe in input indichi quante volte la pi corta contenuta nella pi lunga.
8.
6.
Scrivere un programma che data una stringa in input dica se la stessa contiene almeno una A tra i primi 10 caratteri. Si scriva un programma che chieda allutente 2 stringhe e concateni la seconda alla fine della prima, NON si usi la funzione strcat della libreria standard, si usino i puntatori e non la notazione vettoriale. Attenzione a terminarla con il carattere \0. Si scriva un programma che chieda allutente 2 stringhe e indichi se la seconda uguale alla parte terminale della prima.
7.
37
38
Esercizi
11. Scrivere
Vettori di puntatori
un programma che legga da un file un testo e spezzi su pi righe quelle pi lunghe di N caratteri (N chiesto allutente). Le righe si possono spezzare solo dove c uno spazio (che non va riportato nella riga successiva). Loutput deve essere salvato in un altro file.
Gli elementi di questi vettori sono puntatori int *vett[10]; definisce un vettore di 10 puntatori a int (le [ ] hanno priorit maggiore delloperatore *) Esempio di inizializzazione int a, b, c; int *vett[10]={NULL}; vett[0]=&a; vett[1]=&b; vett[2]=&c; I valori da vett[3] a vett[9] sono tutti NULL in quanto stato inizializzato il primo elemento (i successivi sono automaticamente a 0 e 0 viene convertito in NULL)
39
40
Vettori di puntatori
Puntatori a puntatori
Esempio di inizializzazione errata int a, b, c; int *vett[10]={&a, &b, &c}; E errato perch questi inizializzatori sono indirizzi di variabili automatiche (descritto in altre slide). Se le variabili fossero invece di classe static sarebbe stato corretto, per queste variabili i valori di vett si devono assegnare come visto nellesempio precedente
Variabili che contengono l'indirizzo di memoria di un puntatore Esempio int a, *b, **c; a = 12; b = &a; c = &b; Il puntatore c punta ad una variabile (b) che punta ad un int (a) a:B4A b:A5B c:5D6
A5B B4A 12
41
42
Puntatori a puntatori
Puntatori e matrici
Esempio int a=10, b=20, c=30; int *v[3], int **w; v[0]=&a; v[1]=&b; v[2]=&c; w = v; w++; v un vettore di puntatori, cio l'indirizzo di memoria (puntatore) di un puntatore, quindi v+1 l'indirizzo del secondo puntatore del vettore v (ossia pari a &v[1], punta a b) Anche w un puntatore ad un puntatore, quando viene incrementato, punta al puntatore successivo, come v
Una matrice un vettore di vettori e quindi, considerando che lassociativit di [] da sinistra a destra, si ha che: int Mx[7][5]; definisce un vettore di 7 elementi ciascuno delle quali un vettore di 5 int Gli elementi del vettore Mx sono i 7 vettori identificati da Mx[i]; quindi Mx[i] lindirizzo di memoria di ciascuno dei 7 vettori di 5 int Poich in seguito al decadimento il nome di un vettore-di-T ha tipo puntatore-a-T, anche gli Mx[i] non sono di tipo vettore-di-int ma decadono al tipo puntatore-a-int, (essendo indirizzi, non si pu assegnare loro un valore)
43
44
Puntatori e matrici
Puntatori e matrici
Il decadimento del tipo del nome di un vettore a puntatore pu avvenire 1 sola volta Allora il tipo di una matrice decade a puntatore-a-vettore-di-T e non a puntatore-aT (quindi incrementando questo puntatore si punta al vettore di 5 int successivo) Mx non di tipo puntatore-a-int come: int *p Mx non di tipo puntatore-a-puntatore-a-int: int **p Mx di tipo puntatore-a-vettore-di-5-int come: int (*p)[5] quindi Mx+1 punta al secondo vettore di 5 int
Nella definizione di puntatore seguente int (*p)[5] necessario che la dimensione delle colonne (5) sia specificata perch definisce un
45
46
Puntatori e matrici
Puntatori e matrici
Ricapitolando:
Ricapitolando (continuazione):
Mx[1][2] un valore scalare di tipo int modificabile Mx[1][2]+1 somma 1 al contenuto di Mx[1][2] Mx[1] lindirizzo di un vettore di 5 int di tipo puntatore-a-int (decade)
Mx
lindirizzo di un vettore di 7 vettori di 5 int di tipo puntatore-a-vettore-di-int (decade una volta sola), pi precisamente un puntatore-a-vettore-di-5-int (senza il 5 nel tipo il compilatore non saprebbe di quanti byte spostarsi per puntare al vettore-di-5-int successivo quando si scrive Mx+1) non modificabile Mx+1 lindirizzo di memoria di Mx[1]
47
48
Puntatori e matrici
Vettori di stringhe
Si notino le differenze tra: int *p; puntatore-a-int int (*q)[5]; puntatore-a-vett-di-5-int Poich Mx un puntatore-a-vettore-di-5-int:
p = Mx; errata q = Mx; corretta e q+1 lindirizzo del secondo vettore di 5 elementi (equivale a Mx[1]) p = &Mx[0][0]; corretta e p lindirizzo del primo elemento dei Mx[0]
Essendo una stringa un vettore di caratteri, un vettore di stringhe in realt un vettore di vettori di caratteri, cio una matrice di char char str[4][20]={"uno", "due"}; definisce un vettore di 4 stringhe di 20 char Le 4 stringhe sono identificate da str[i]
str: str[0] u n o \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 str[1] d u e \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 str[2]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 str[3]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
49
50
a[2] = "hello"; ERRORE: a[2]non un puntatore strcpy(a[2], "hello"); CORRETTO b[2] = "hello"; CORRETTO: b[2] una variabile puntatore a cui viene assegnato lindirizzo di memoria di una stringa
costante
a[i] lindirizzo costante della riga i di a, tale riga una stringa variabile di 8 char b[i] una variabile puntatore con lindirizzo della riga i, stringa costante di 4 caratteri: ad esempio b[2] contiene lindirizzo di "sei\0"
strcpy(b[2], "hello"); ERRORE: b[2] punta a una stringa costante Entrambi a[1][0] e b[1][0] sono il carattere 'c'
stringa variabile
stringa costante
51
52
2.
3.
int** x; x una variabile di tipo puntatore a un puntatore variabile a un oggetto variabile di tipo int const int** x; x una variabile di tipo puntatore a un puntatore variabile a un oggetto costante di tipo int int ** const x; x una costante di tipo puntatore a un puntatore variabile a un oggetto variabile di tipo int
5.
6.
int * const * x x una variabile di tipo puntatore a un puntatore costante a un oggetto variabile di tipo int const int * const * x x una variabile di tipo puntatore a un puntatore costante a un oggetto costante di tipo int const int * const * const x x una costante di tipo puntatore a un puntatore costante a un oggetto costante di tipo int
53
54
Homework 7
Scrivere un programma che legga da un file al massimo un certo numero MAXRIGHE di righe di testo e le memorizzi in una matrice di caratteri (MAXRIGHE x 100), una riga del file per ciascuna riga della matrice. Si definisca un vettore di puntatori a carattere e lo si inizializzi in modo che il primo puntatore punti alla prima riga, il secondo alla seconda, ecc. Si ordinino le stringhe scambiando tra di loro solo i puntatori e le si visualizzino ordinate. Facoltativo: misurare il rapporto tra il tempo necessario per ordinare scambiando le stringhe e scambiando i puntatori.
Homework 7 (esempio)
prima Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura ch la diritta via era smarrita. Ahi quanto a dir qual era cosa dura esta selva selvaggia e aspra e forte dopo Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura ch la diritta via era smarrita. Ahi quanto a dir qual era cosa dura esta selva selvaggia e aspra e forte