Salta al contenuto
Note per Studenti Puntatori in C

Puntatori in C

In questa pagina 8

Indirizzi e puntatori

Ogni variabile occupa celle di memoria con un indirizzo (vedi Struttura di un elaboratoreModello di von Neumann: CPU, memoria centrale, bus e periferiche; ciclo fetch-decode-execute e gerarchia di memoria.Struttura di un elaboratore →). Un puntatore è una variabile che contiene un indirizzo; il suo tipo dice a quale tipo di dato punta.

c
int x = 10;
int *p;          /* p è un puntatore a int */
p = &x;          /* & = indirizzo di: ora p punta a x */
printf("%d\n", *p);   /* * = dereferenziazione: il valore puntato, 10 */
*p = 20;         /* modifica x tramite p */
printf("%d %p\n", x, (void *)p);   /* 20 e l'indirizzo, es. 0x7ffd5e8c */
   indirizzo   contenuto
   0x1000      20          <- x
   ...
   0x2000      0x1000      <- p (punta a x)
  • &x: indirizzo di x. Si applica a variabili (non a costanti o espressioni).
  • *p: l'oggetto puntato da p, utilizzabile sia in lettura sia a sinistra di =.
  • Nella dichiarazione int *p, q; solo p è un puntatore: q è un int.
  • Un puntatore non inizializzato contiene un indirizzo casuale: dereferenziarlo è comportamento indefinito.
  • NULL (da <stdlib.h> o <stdio.h>) è il puntatore "che non punta a niente": va controllato prima di dereferenziare.
c
int *q = NULL;
if (q != NULL) {
    *q = 5;
}

Puntatori come parametri

Poiché il C passa per valore (Funzioni in CDefinizione e prototipo di una funzione C, tipo di ritorno e void, passaggio dei parametri sempre per valore, variabili locali, globali e static, file header e compilazione separata, funzioni ricorsive.Funzioni in C →), per far modificare a una funzione una variabile del chiamante si passa il suo indirizzo. È anche il modo di "restituire" più valori.

c
void divisione(int a, int b, int *quoziente, int *resto) {
    *quoziente = a / b;
    *resto = a % b;
}

int q, r;
divisione(17, 5, &q, &r);    /* q = 3, r = 2 */

È esattamente ciò che fa scanf("%d", &n) (Input e output in Cprintf con larghezza e precisione, scanf con indirizzi e valore di ritorno, lettura di righe con fgets, file con FILE*, fopen, fclose, fprintf, fscanf, controllo degli errori e della fine del file.Input e output in C →).

Aritmetica dei puntatori

Se p punta a un elemento di un array, p + k punta al k-esimo elemento successivo: l'indirizzo aumenta di k * sizeof(*p) byte, non di k.

c
int v[5] = {10, 20, 30, 40, 50};
int *p = v;              /* = &v[0] */
*(p + 2)                 /* 30, come v[2] */
p++;                     /* ora punta a v[1] */
int d = &v[4] - &v[1];   /* 3: differenza in numero di elementi */

Operazioni lecite: puntatore ± intero, differenza tra puntatori allo stesso array, confronti (<, ==) tra puntatori allo stesso array. Ci si può spostare fino a una posizione oltre l'ultimo elemento (senza dereferenziarla).

Array e puntatori

c
/* scorrimento con un puntatore */
int somma(const int *v, int n) {
    int s = 0;
    for (const int *p = v; p < v + n; p++)
        s += *p;
    return s;
}

/* lunghezza di una stringa con i puntatori */
int lunghezza(const char *s) {
    const char *p = s;
    while (*p != '\0')
        p++;
    return p - s;
}

const e puntatori

c
const int *p;        /* puntatore a int costante: *p = 1 vietato, p = &y permesso */
int *const q = &x;   /* puntatore costante: q non può cambiare, *q sì */

Nei parametri const T * documenta (e fa verificare al compilatore) che la funzione non modifica i dati puntati.

Puntatori a puntatori e a struct

Confronto con Python

In Python ogni variabile è implicitamente un riferimento (Variabili, oggetti e riferimentiIn Python una variabile è un nome legato a un oggetto; tipo, identità e valore; assegnamento, aliasing, oggetti mutabili e immutabili, None.Variabili, oggetti e riferimenti →), ma non si possono vedere né manipolare gli indirizzi: niente aritmetica, niente puntatori non validi. In C il controllo è totale e gli errori non vengono intercettati.

Errori tipici

Teoria collegata