Puntatori in C: cos'è il puntatore nella programmazione C? Tipi
⚡ Riepilogo intelligente
In C, i puntatori sono variabili che memorizzano l'indirizzo di memoria di un'altra variabile. Permettono l'allocazione dinamica della memoria, la gestione efficiente di array e stringhe e la creazione di strutture dati complesse, utilizzando gli operatori di dereferenziazione e di accesso per leggere o modificare i valori.
Che cos'è un puntatore in C?
A pointer In C, un puntatore è una variabile che memorizza l'indirizzo di un'altra variabile. Un puntatore può anche fare riferimento a un altro puntatore o a una funzione. Un puntatore può essere incrementato o decrementato per puntare alla posizione di memoria successiva o precedente. Lo scopo di un puntatore è quello di risparmiare spazio di memoria e ottenere tempi di esecuzione più rapidi.
Come utilizzare i puntatori in C
Se dichiariamo una variabile v di tipo int, v memorizza un valore. Ogni variabile, oltre al suo valore, ha anche un indirizzo, ovvero la sua posizione in memoria. L'indirizzo può essere recuperato anteponendo una e commerciale (&) al nome della variabile. Se si stampa l'indirizzo di una variabile, questo apparirà come un numero casuale che può variare da un'esecuzione all'altra.
Anziché memorizzare un valore, un puntatore memorizza l'indirizzo di una variabile. Ad esempio:
int *y = &v;
| VARIABILE | PUNTATORE |
|---|---|
| A APPREZZIAMO memorizzato in a detto indirizzo di archiviazione/memoria. | A variabile che punta a l'indirizzo di archiviazione/memoria di un altro variabile. |
Dichiarazione di un puntatore
Come le variabili, i puntatori in Programmazione C. Devono essere dichiarati prima di poter essere utilizzati. La dichiarazione di un puntatore ha la seguente forma:
data_type *pointer_variable_name;
Qui, tipo di dati è il tipo base del puntatore e indica il tipo della variabile a cui il puntatore punta. L'asterisco (*), lo stesso simbolo usato per la moltiplicazione, è l'operatore di indirezione che dichiara un puntatore. Alcune dichiarazioni di puntatore valide sono:
int *ptr_thing; /* pointer to an integer */ int *ptr1, thing; /* ptr1 points to an int; thing is an int */ double *ptr2; /* pointer to a double */ float *ptr3; /* pointer to a float */ char *ch1; /* pointer to a character */
Inizializza un puntatore
Dopo aver dichiarato un puntatore, lo inizializziamo con l'indirizzo di una variabile. Se un puntatore non è inizializzato e viene utilizzato, i risultati possono essere imprevedibili e potenzialmente disastrosi. Per ottenere l'indirizzo di una variabile, si utilizza l'operatore e commerciale (&) prima del nome della variabile:
pointer = &variable;
Di seguito è riportato un semplice programma che illustra il funzionamento di un puntatore:
#include <stdio.h> int main() { int a = 10; // variable declaration int *p; // pointer variable declaration p = &a; // store address of a in pointer p printf("Address stored in p is: %x\n", p); // access the address printf("Value stored in p is: %d\n", *p); // access the value return 0; }
Produzione:
Address stored in p is: 60ff08 Value stored in p is: 10
| Operator | Significato |
|---|---|
| * | Serve a due scopi: dichiara un puntatore e restituisce il valore della variabile a cui fa riferimento (dereferenziazione). |
| & | Serve a un solo scopo: restituisce l'indirizzo di una variabile. |
Tipi di puntatori in C
Puntatore nullo
Creiamo un puntatore nullo assegnandogli il valore NULL durante la dichiarazione. Questo è utile quando al puntatore non è ancora stato assegnato alcun indirizzo. Un puntatore nullo contiene sempre il valore 0:
#include <stdio.h> int main() { int *p = NULL; // null pointer printf("The value inside variable p is: %x", p); return 0; }
Puntatore del Vuoto
Un puntatore void, detto anche puntatore generico, non ha un tipo di dato standard. Viene creato utilizzando la parola chiave void e può memorizzare l'indirizzo di qualsiasi variabile:
#include <stdio.h> int main() { void *p = NULL; // void pointer printf("The size of pointer is: %d\n", sizeof(p)); return 0; }
Puntatore selvatico
Un puntatore viene definito "puntatore selvaggio" se non è inizializzato con alcun valore. Questi puntatori non sono efficienti perché potrebbero puntare a una posizione di memoria sconosciuta, causando problemi e l'arresto anomalo del programma. È fondamentale prestare sempre attenzione ai puntatori selvaggi.
#include <stdio.h> int main() { int *p; // wild pointer printf("%d", *p); return 0; }
Altri tipi di puntatori in C includono il puntatore pendente, il puntatore complesso, il puntatore vicino, il puntatore lontano e il puntatore enorme.
Puntatori di accesso diretto e indiretto
In C, esistono due modi equivalenti per accedere e manipolare il contenuto di una variabile. L'accesso diretto utilizza direttamente il nome della variabile, mentre l'accesso indiretto utilizza un puntatore alla variabile:
#include <stdio.h> int var = 1; /* declare and initialize an int variable */ int *ptr; /* declare a pointer to int */ int main(void) { ptr = &var; /* point ptr to var */ printf("\nDirect access, var = %d", var); printf("\nIndirect access, var = %d", *ptr); *ptr = 48; /* change var through the pointer */ printf("\nIndirect access, var = %d", *ptr); return 0; }
Aritmetica dei puntatori in C
Quando si lavora con i puntatori, è necessario osservare le seguenti regole di priorità. Gli operatori * e & hanno la stessa priorità degli operatori unari (negazione !, incremento ++, decremento –). Nella stessa espressione, gli operatori unari *, &, !, ++ e — vengono valutati da destra a sinistra. Se un puntatore P punta a una variabile X, allora *P può essere utilizzato ovunque sia possibile scrivere X. Le seguenti espressioni sono equivalenti:
| Espressione | Espressione equivalente |
|---|---|
| Y = *P + 1 | Y = X + 1 |
| *P = *P + 10 | X = X + 10 |
| *P += 2 | X += 2 |
| ++*P | ++X |
| (*P)++ | X++ |
Nell'ultimo caso, sono necessarie le parentesi: poiché gli operatori unari * e ++ vengono valutati da destra a sinistra, senza parentesi verrebbe incrementato il puntatore P, non l'oggetto a cui punta. Le operazioni aritmetiche disponibili sui puntatori sono assegnazione, incremento e decremento, e addizione o sottrazione.tracimpostando un offset intero, che sposta il puntatore di un numero di elementi pari a tale valore (ogni passo corrisponde alla dimensione in byte del tipo base).
Puntatori e array in C con esempi
Tradizionalmente, si accede agli elementi di un array tramite un indice, ma i puntatori semplificano ulteriormente questa operazione. Un puntatore impostato sul nome dell'array punta al primo elemento:
#include <stdio.h> int main() { int a[5] = {1, 2, 3, 4, 5}; // array initialization int *p; // pointer declaration p = a; // p points to the first element; same as &a[0] for (int i = 0; i < 5; i++) { printf("%d\n", *p); // print element p++; // move to the next element } return 0; }
Aggiungere un numero a un puntatore lo sposta di tanti elementi in avanti. Se p punta all'indirizzo di memoria 0, allora p + 1 fa sì che il puntatore punti all'indirizzo di memoria 1.
Puntatori e stringhe C con esempi
Una stringa è un array di oggetti char che termina con un carattere nullo '\0'. Possiamo manipolare le stringhe usando i puntatori:
#include <stdio.h> #include <string.h> int main() { char str[] = "Hello Guru99!"; char *p; p = str; printf("First character is: %c\n", *p); p = p + 1; printf("Next character is: %c\n", *p); p = str; // reset the pointer for (int i = 0; i < strlen(str); i++) { printf("%c\n", *p); p++; } return 0; }
Un altro modo per gestire le stringhe è tramite un array di puntatori:
#include <stdio.h> int main() { char *materials[] = {"iron", "copper", "gold"}; for (int i = 0; i < 3; i++) printf("%s\n", materials[i]); return 0; }
Vantaggi dei puntatori in C
- I puntatori sono utili per accedere direttamente alle posizioni di memoria.
- Forniscono un modo efficiente per accedere agli elementi di un array.
- Vengono utilizzati per l'allocazione e la deallocazione dinamica della memoria.
- Vengono utilizzati per formare strutture dati complesse come liste concatenate, grafi e alberi.
Svantaggi dei puntatori in C
- I puntatori sono un po' complessi da capire.
- Possono causare errori come guasti di segmentazione o accesso a una posizione di memoria non prevista.
- Assegnare un valore errato a un puntatore può causare il danneggiamento della memoria.
- I puntatori sono anche responsabili di perdite di memoria se la memoria non viene liberata.
- È responsabilità del programmatore gestire i puntatori con attenzione.

