Le variabili e le funzioni hanno un attributo che specifica una tra 4 possibili classi di memorizzazione. Una classe di memorizzazione può avere significati diversi, a seconda che sia applicata a una variabile locale/globale o a una funzione.
La classe di memorizzazione automatica è quella di default per le variabili locali, ma può essere specificata esplicitamente con la parola riservata auto
. Lo spazio per memorizzare una variabile automatica viene allocato all’interno del record di attivazione dalla funzione a cui appartiene la variabile. Di conseguenza, tale spazio rimane occupato solo per la durata dell’esecuzione della funzione, dopodichè viene rilasciato.
Ad esempio, il frammento di codice
void f(void) {
int tmp;
// ...
}
equivale a
void f(void) {
auto int tmp;
// ...
}
e specifica che la variabile tmp
viene memorizzata nel record di attivazione della procedura f
.
La classe di memorizzazione registro, specificata con la parola riservata register
, si applica a una variabile locale per suggerire al compilatore di memorizzare tale variabile in un registro del processore, invece che nel record di attivazione (nell’area di stack della memoria). Ciò può essere utile, ad esempio, per accelerare l’accesso a variabili che vengono lette e/o modificate di frequente, come ad esempio il contatore di un ciclo:
void f(void) {
for (register int i = 0; i < SIZE; i++) {
// ...
}
}
Se il compilatore determina che i registri sono già tutti occupati, oppure se la dimensione della variabile non è compatibile con quella dei registri disponibili — i registri di un processore sono pochi e hanno dimensioni molto limitate — allora la variabile viene gestita come se fosse automatica, ovvero memorizzata nel record di attivazione. Viceversa, il compilatore può decidere autonomamente di memorizzare alcune variabili automatiche nei registri del processore, al fine di ottimizzare le prestazioni del codice. Ad esempio, nel caso del ciclo for mostrato prima, la variabile i
sarebbe molto probabilmente memorizzata in un registro anche se non si specificasse la classe di memorizzazione register
.
Siccome una variabile di classe registro potrebbe non avere un indirizzo (i registri del processore non hanno appunto indirizzi), non è ammesso applicare a essa l’operatore di estrazione di indirizzo, a prescindere dal fatto che essa sia effettivamente memorizzata in un registro o meno. Ad esempio, il seguente frammento di codice dà un errore in compilazione:
void f(void) {
register int x;
int *pt = &x; // Errore in compilazione
// ...
}
La classe di memorizzazione esterna, indicata con la parola riservata extern
, si applica sia alle variabili che alle funzioni.
Nel caso di una variabile globale o di un prototipo di funzione, la classe di memorizzazione esterna indica al compilatore che la definizione completa di tale variabile o funzione è situata in un altro file (ad esempio, potrebbe essere una definizione di libreria). Ad esempio, nel frammento di codice
extern void f(void);
extern int x;
void g(void) {
// ...
x = /* ... */;
// ...
f();
// ...
}
si indica al compilatore che la procedura f
e la variabile globale x
, usate all’interno della procedura g
, sono definite in un altro file.
Anche nel caso di una variabile locale, la classe di memorizzazione esterna indica che tale variabile è in realtà una variabile globale definita in un altro file, e quindi non è memorizzata nel record di attivazione della funzione in cui compare la definizione. Tuttavia, una variabile esterna così dichiarata è visibile solo all’interno della funzione in cui compare la dichiarazione, e non nel resto del file. Ad esempio, nel frammento di codice