- Programmazione » Programmazione » Guida C - Manuale programmazione con articoli e risorse interessanti
Le variabili register
L'ACCESSIBILITÀ' E LA DURATA DELLE VARIABILI
Dal momento che il compilatore colloca le variabili automatic
nella RAM del calcolatore, i valori in esse contenuti devono
spesso essere copiati nei registri della CPU per poter essere
elaborati e, se modificati dall'elaborazione subita,
copiati nuovamente nelle locazioni di memoria di provenienza.
Tali operazioni sono svolte in modo trasparente per il
programmatore, ma possono deteriorare notevolmente la
performance di un programma, soprattutto se ripetute
più e più volte (ad esempio all'interno di
un ciclo con molte iterazioni).
Dichiarando una variabile automatic con la parola chiave
register si forza il compilatore ad allocarla
direttamente in un registro della CPU, con notevole
incremento di efficienza nell'elaborazione del valore in
essa contenuto. Ecco un esempio:
register int i = 10;
do {
printf("%2d
" ,i);
} while(i--);
Il ciclo visualizza, incolonnati[24], i numeri da
10 a 0; la variabile i si comporta
come una qualsiasi variabile automatic, ma essendo
probabilmente gestita in un registro consente
un'elaborazione più veloce. E' d'obbligo
scrivere "probabilmente gestita" in quanto non si
può essere assolutamente certi che il compilatore
collochi una variabile dichiarata con register
proprio in un registro della CPU: in alcune situazioni
potrebbe gestirla come una variabile automatic qualsiasi,
allocandola in memoria. I principali motivi sono due: la
variabile potrebbe occupare più byte di quanti
compongono un registro della CPU[25], o potrebbero non
esserci registri disponibili allo scopo[26].
Già che ci siamo, diamo un'occhiata più
approfondita all'esempio di poco fa. Innanzitutto va
rilevato che nella dichiarazione di i potrebbe
essere omessa la parola chiave int:
register i = 10;
Abbiamo poi utilizzato un costrutto nuovo: il ciclo
do...while. Esso consente di identificare un blocco di codice
(quello compreso tra le graffe) che viene eseguito
finché la condizione specificata tra parentesi dopo la
parola chiave while continua ad essere vera. Il
ciclo viene sempre eseguito almeno una volta, perché
il test è effettuato al termine del medesimo. Nel
nostro caso, quale test viene effettuato? Dal momento che non
è utilizzato alcun operatore di confronto esplicito,
viene controllato se il risultato dell'espressione nelle
tonde è diverso da 0. L'operatore
--, detto di autodecremento, è specificato
dopo la variabile a cui è applicato. Ciò
assicura che i sia decrementata dopo
l'effettuazione del test. Perciò il ciclo è
eseguito 11 volte, con i che varia da 10 a
0 inclusi. Se l'espressione fosse --i,
il decremento sarebbe eseguito prima del test, con la
conseguenza che per i pari a 0 il ciclo non
verrebbe più eseguito.
Come per le variabili automatic, non è possibile
conoscere il contenuto di una variabile register
prima della sua esplicita inizializzazione mediante un
operazione di assegnamento. In questo caso non si tratta di
utilizzo e riutilizzo di un'area di memoria, ma di un
registro macchina: non possiamo conoscerne a priori il
contenuto nel momento in cui esso è destinato alla
gestione della variabile (dichiarazione della variabile).
Inoltre, analogamente alle variabili automatic, anche quelle
register cessano di esistere all'uscita del
blocco di codice (solitamente una funzione) nel quale sono
dichiarate e il registro macchina viene utilizzato per altri
scopi.
Le variabili register, a differenza delle
automatic, non hanno indirizzo: ciò appare ovvio se si
pensa che i registri macchina si trovano nella CPU e non
nella RAM. La conseguenza immediata è che una
variabile register non può mai essere
referenziata tramite un puntatore. Nel nostro esempio, il
tentativo di assegnare ad un puntatore l'indirizzo di
i provocherebbe accorate proteste da parte del
compilatore.
register i;
int *iPtr = &i; // errore! i non ha indirizzo
Pur non avendo indirizzo, le variabili register possono contenere un indirizzo, cioè un puntatore: la dichiarazione
register char *ptr_1, char *ptr_2;
non solo è perfettamente lecita, ma anzi genera, se possibile, due puntatori (a carattere) particolarmente efficienti.