9 aprile 2024
La memoria è divisa in celle della dimensione di un byte e ogni byte ha un proprio indirizzo.
Ogni variabile è composta da un numero specifico di byte contigui (sizeof
serve proprio a conoscere lo spazio in memoria di una variabile).
Per conoscere l’indirizzo nella memoria di una variabile è possibile utilizzare l’operatore &
:
Nota
Si noti che %p
che permette di visualizzare l’indirizzo di una locazione in memoria. &n
può essere letteralmente tradotto come “l’indirizzo di n”. Eseguendo questo codice si otterrà un indirizzo di memoria che inizia con 0x
.
Un puntatore è una variabile che contiene l’informazione per accedere ad un’altra variabile, ossia il suo indirizzo.
Riassunto:
Ad ogni variabile è associato un indirizzo di memoria. Questo indirizzo è un numero che identifica la posizione della variabile in memoria. Un puntatore contiene questo numero.
È possibile compiere operazioni con questi puntatori, di fatto sono un nuovo tipo di variabile detta variabile puntatore.
Per dichiarare un puntatore si usa il simbolo *
:
int *p;
in questo caso p
è un puntatore che punta ad una variabile di tipo int
.
Esistono due operatori che si possono usare con i puntatori:
&
restituisce l’indirizzo di memoria di una variabile*
restituisce il valore puntato da un puntatoreSi possono dichiarare insieme ad altre variabili:
int *p, x, *y, a[10];
Qualsiasi tipo può essere usato.
*
ad un puntatore non inizializzato è un errore:In C non è possibile restituire più di un valore da una funzione.
void incrementa(int a, int b) {
a++;
b++;
// alla fine della funzione i valori di a e b vengono persi
}
I puntatori permettono di aggirare questa limitazione. Possono essere passati come argomenti di funzioni:
Ci si basa unicamente sui side effects, è possibile quindi restituire più di un valore.
La chiamata di funzione avviene così:
int i = 1;
int j = 10;
incrementa(&i, &j);
printf("%d\n", i); // stampa 2
printf("%d\n", j); // stampa 11
Abbiamo già visto questa sintassi con scanf
.
Scrivere una funzione che scambi il valore di due variabili.
Il prototipo della funzione è:
void swap(int *a, int *b);
In realtà, in C, un array è un puntatore al primo elemento dell’array.
a
è un puntatore al primo elemento dell’array.
L’operatore []
permette di scorrere la memoria a partire dal primo elemento dell’array.
Gli array possono quindi essere trattati come puntatori:
p
punta al primo elemento dell’array.
Gli elementi dell’array si trovano in posizioni di memoria contigue.
Quindi l’operazione p + 1
punta al secondo elemento dell’array.
Nota
C capisce da solo che p
è un puntatore ad un array di interi, quindi p + 1
punta al secondo elemento dell’array che si trova dopo un salto di 4 byte (la dimensione di un intero).
La sintassi p[i]
è uno zucchero sintattico per *(p + i)
.
Scrivere una funzione che trovi il minimo e il massimo in un array di interi.
Il prototipo della funzione è:
void minmax(const int *a, int n, int *min, int *max);
Un puntatore può puntare ad un altro puntatore.
Può sembrare strano, ma è una pratica molto comune.
Le stringhe in C sono array di caratteri terminati da un carattere nullo.
s
è un puntatore al primo carattere della stringa.
Il caso più comune di puntatore a puntatore è quello delle stringhe.
Quando si deve memorizzare un array di stringhe si usa un array di puntatori a caratteri.
int main(int argc, char **argv) {
// argv è un array di puntatori a caratteri
}
// oppure
int main(int argc, char *argv[]) {
// argv è un array di puntatori a caratteri
}
Dati e Algoritmi