I puntatori in C servono a tre scopi principali:
Fake pass-by-reference
C passa tutti gli argomenti della funzione in base al valore; i parametri formali e i parametri attuali sono oggetti diversi nella memoria, quindi le modifiche al parametro formale non si riflettono nel parametro attuale. Se si desidera che una funzione modifichi il valore di qualcosa nel chiamante, è necessario passare il puntatore a tale oggetto:
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int x = 3, y = 4;
swap(&x, &y);
return 0;
}
Le espressioni *a
e *b
in swap
corrispondono agli oggetti x
e y
in main
, quindi scrivendo a *a
aggiorna x
e scrivendo su *b
updates y
.
Tracciamento memoria allocata dinamicamente
Le funzioni di allocazione della memoria C malloc
, calloc
e realloc
tutti i puntatori di ritorno al primo elemento del buffer allocato dinamicamente.
int *arr = malloc(sizeof *arr * N); // allocates a block of memory large
// enough to hold N ints
È possibile applicare l'operatore di pedice a un puntatore come se fosse un array (l'operazione di pedice a[i]
è equivalente a *(a + i)
, ovvero offset i
elementi da a
e dereferenzia il risultato).
Creazione di strutture dati auto-referenziali
Sebbene generazioni di programmatori di Fortran stessero costruendo liste, alberi, code, pile, ecc., senza di essi, i puntatori rendono molto facile la costruzione di strutture autoreferenziali, come questo nodo ad albero binario:
struct tnode {
K key; // for some arbitrary type K
T data; // for some arbitrary type T
struct tnode *left; // explicitly points to left subtree
struct tnode *right; // explicitly points to right subtree
};
Non ho familiarità con PHP per dire come faresti queste cose in quella lingua. Vorrei sottolineare che C precede PHP di almeno un paio di decenni e che PHP è piuttosto specifico per un particolare dominio.
Riguardo al perché C non gestisce tutto questo automaticamente ...
Una delle filosofie guida di C è mantenere la lingua il più semplice possibile, rendendola relativamente facile da implementare. La gestione automatica della memoria aggiungerebbe un po 'di complessità al linguaggio (certamente l'aggiunta della libreria di threading in C11). Senza contare che la gestione automatica della memoria può anche giocare all'inferno con un codice critico per le prestazioni.