Pointer to Pointer (Puntatore a Puntatore) in C

Ciao a tutti, aspiranti programmatori! Oggi ci imbarciamo in un viaggio avventuroso nel mondo dei puntatori - in particolare, dei puntatori a puntatori. So cosa potreste pensare: "Puntatori? Puntatori doppi? Sembra un incubo!" Ma non preoccupatevi, prometto di rendere questa spiegazione divertente e facile da capire. Allora, prendete il vostro bevanda preferita, fatevi comodi e... immergetevi!

C - Pointer to Pointer

Cos'è un Puntatore Doppio in C?

Immagina di essere in una caccia al tesoro. Hai una mappa (chiamiamola un puntatore) che ti porta a una cassa. Ma sorpresa! Dentro la cassa, c'è un'altra mappa (un altro puntatore) che ti porta al vero tesoro. Questo è essenzialmente quello che è un puntatore doppio - un puntatore che punta a un altro puntatore.

In programmazione C, un puntatore doppio è esattamente quello che sembra - un puntatore a un puntatore. È una variabile che memorizza l'indirizzo di un altro puntatore. Questo potrebbe sembrare un po' confuso all'inizio, ma non preoccupatevi, lo spiegheremo passo per passo.

Dichiarazione di un Puntatore a un Puntatore

Cominciamo con come dichiarare un puntatore doppio. La sintassi è abbastanza semplice:

int **ptr;

Qui, ptr è un puntatore a un puntatore a un intero. Il primo asterisco * lo rende un puntatore, e il secondo * lo rende un puntatore a un puntatore.

Esempio di Puntatore a Puntatore (Puntatore Doppio)

Guardiamo un esempio semplice per capirlo meglio:

#include <stdio.h>

int main() {
int x = 5;
int *p = &x;
int **pp = &p;

printf("Valore di x: %d\n", x);
printf("Valore di x usando p: %d\n", *p);
printf("Valore di x usando pp: %d\n", **pp);

return 0;
}

Output:

Valore di x: 5
Valore di x usando p: 5
Valore di x usando pp: 5

Spiegazione:

  1. Dichiariamo un intero x e lo inizializziamo con 5.
  2. Creiamo un puntatore p che punta a x.
  3. Creiamo un puntatore doppio pp che punta a p.
  4. Poi stampiamo il valore di x in tre modi diversi:
  • Direttamente usando x
  • Usando il puntatore singolo p (lo dereferenziamo una volta con *p)
  • Usando il puntatore doppio pp (lo dereferenziamo due volte con **pp)

Tutti e tre i metodi ci danno lo stesso valore: 5. Come raggiungere il tesoro usando mappe diverse!

Come Funziona un Puntatore Normale in C?

Prima di immergerci più a fondo nei puntatori doppi, spieghiamo rapidamente come funzionano i puntatori normali:

int y = 10;
int *q = &y;

printf("Valore di y: %d\n", y);
printf("Indirizzo di y: %p\n", (void*)&y);
printf("Valore di q: %p\n", (void*)q);
printf("Valore puntato da q: %d\n", *q);

Output:

Valore di y: 10
Indirizzo di y: 0x7ffd5e8e9f44
Valore di q: 0x7ffd5e8e9f44
Valore puntato da q: 10

Qui, q è un puntatore che memorizza l'indirizzo di y. Quando usiamo *q, accediamo al valore memorizzato a quell'indirizzo.

Come Funziona un Puntatore Doppio?

Ora, estendiamo questo ai puntatori doppi:

int z = 15;
int *r = &z;
int **rr = &r;

printf("Valore di z: %d\n", z);
printf("Indirizzo di z: %p\n", (void*)&z);
printf("Valore di r: %p\n", (void*)r);
printf("Indirizzo di r: %p\n", (void*)&r);
printf("Valore di rr: %p\n", (void*)rr);
printf("Valore puntato da r: %d\n", *r);
printf("Valore puntato da rr: %p\n", (void*)*rr);
printf("Valore puntato dal valore puntato di rr: %d\n", **rr);

Output:

Valore di z: 15
Indirizzo di z: 0x7ffd5e8e9f48
Valore di r: 0x7ffd5e8e9f48
Indirizzo di r: 0x7ffd5e8e9f50
Valore di rr: 0x7ffd5e8e9f50
Valore puntato da r: 15
Valore puntato da rr: 0x7ffd5e8e9f48
Valore puntato dal valore puntato di rr: 15

Questo potrebbe sembrare un po' ingestibile, ma spiegiamolo:

  1. z è un intero con valore 15.
  2. r è un puntatore che memorizza l'indirizzo di z.
  3. rr è un puntatore doppio che memorizza l'indirizzo di r.
  4. *r ci dà il valore di z (15).
  5. *rr ci dà il valore di r (che è l'indirizzo di z).
  6. **rr ci dà il valore di z (15).

Pensa a questo come a un gioco di indirizzi: rr punta a r, che punta a z. Quindi **rr è come dire "segui il primo puntatore, poi il secondo puntatore, e dammi il valore lì".

Un Puntatore Doppio Si Comporta Come un Puntatore Normale

Ecco un piccolo segreto: un puntatore doppio è solo un puntatore, ma invece di puntare a un intero o a un float, punta a un altro puntatore. Questo significa che possiamo fare tutto ciò che facciamo con i puntatori normali anche con i puntatori doppi.

Per esempio, possiamo usare i puntatori doppi con gli array:

int main() {
char *fruits[] = {"Mela", "Banana", "Ciliegia"};
char **ptr = fruits;

for(int i = 0; i < 3; i++) {
printf("%s\n", *ptr);
ptr++;
}

return 0;
}

Output:

Mela
Banana
Ciliegia

In questo esempio, fruits è un array di puntatori (ognuno dei quali punta a una stringa), e ptr è un puntatore a un puntatore a un char (che può puntare agli elementi di fruits).

Puntatori a Più Livelli in C (È Possibile un Puntatore Triple?)

Sì, puoi avere puntatori tripli, quadrupli e così via! Non c'è un limite teorico ai livelli di indirezione che puoi avere. Tuttavia, nella pratica, è raro vedere più di puntatori doppi.

Ecco un esempio di un puntatore triplo:

int x = 5;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;

printf("Valore di x: %d\n", ***ppp);

Output:

Valore di x: 5

Ma ricorda, solo perché puoi, non significa che dovresti. Livelli multipli di indirezione possono rendere il codice più difficile da leggere e mantenere. Come dice il vecchio adagio della programmazione, "Tutti i problemi nell'informatica possono essere risolti con un altro livello di indirezione... tranne il problema di troppi livelli di indirezione!"

Conclusione

Congratulazioni! Avete appena navigato le acque intricanti dei puntatori doppi in C. Ricorda, come molte concettiniella programmazione, i puntatori a puntatori potrebbero sembrare confusi all'inizio, ma con la pratica, diventeranno seconda natura.

Ecco una tabella che riassume i punti chiave che abbiamo coperto:

Concetto Sintassi Descrizione
Puntatore Normale int *p; Punta a un intero
Puntatore Doppio int **pp; Punta a un puntatore che punta a un intero
Dereferenziazione *p Accede al valore puntato da p
Doppia Dereferenziazione **pp Accede al valore puntato dal puntatore che pp punta
Operatore di Indirizzo &x Ottiene l'indirizzo di x

Continuate a praticare, mantente curioso e ricorda - ogni esperto era una volta un principiante. Buon coding!

Credits: Image by storyset