Indirizzi delle variabili A ogni variabile sono associati tre concetti fondamentali: il valore memorizzato; il tipo dati di appartenenza; lindirizzo. Il valore memorizzato in una variabile è detto anche il suo contenuto, o rvalue. Il tipo dati di appartenenza è dichiarato usando unistruzione di dicharazione. Lindirizzo, detto anche lvalue, è quello della prima locazione di memoria usata per la variabile. Come abbiamo visto, il numero delle locazioni di memoria effettivamente usate da una variabile dipende dal suo tipo dati. Quindi, quando si dichiara una variabile con una istruzione quale int numero il compilatore tiene traccia di ciò creando (o aggiornando) in memoria una tabella del tipo:
Il campo INDIRIZZO viene riempito automaticamente dal compilatore, il campo VALORE viene riempito dallutente con una istruzione di assegnazione. Come indica la figura, lindirizzo di una variabile è scritto di solito alla sua sinistra, il suo contenuto o valore alla destra.
Operatore di indirizzo &. Di solito ci si interessa più al valore assegnato a una variabile (il suo contenuto, o rvalue), e meno a dove il valore sia memorizzato (il suo indrizzo, o lvalue). Tuttavia il C consente di visualizzare in modo semplice lindirizzo di una variabile (ossia quello del primo byte che le viene riservato nella memoria del computer). Per indicare tale indirizzo si usa loperatore di indirizzo &, che si legge lindirizzo di, subito prima del nome della variabile (senza spazi intermedi). Ad es.: Loperatore & è un operatore unario, che restituisce lindirizzo del suo operando.
#include void main(void) { int numero; numero = 22; printf(numero = %d Lindirizzo di numero è %p., numero, &numero); } Esso produce la seguente uscita Sequenza di controllo di conversione %p. Per visualizzare con printf() lindirizzo di una variabile preceduta dalloperatore di indirizzo & si usa la sequenza di controllo di conversione %p, come nel programma seguente. Ogni volta che il programma viene eseguito, esso visualizza (in formato esadecimale) lindirizzo del primo byte usato per memorizzare la variabile numero. Tale indirizzo può cambiare, ovviamente, a ogni esecuzione del programma. numero = 22 Lindirizzo di numero è 0012FF7C.
La figura seguente illustra linformazione dindirizzo aggiuntiva fornita dalluscita del programma Come vedremo, usare gli indirizzi anziché semplicemente visualizzarli fornisce un potente strumento di programmazione: gli indirizzi consentono di entrare direttamente nella operatività interna del computer e di accedere alla sua struttura fondamentale di memoria. Ciò fornisce al programmatore C una possibilità e una potenza di programmazione non disponibili nella maggior parte degli altri linguaggi per computer.
Puntatori Oltre a visualizzare lindirizzo di una variabile, come fa il programma precedente, è anche possibile memorizzare tale indirizzo in una variabile precedentemente dichiarata. Ad es., listruzione di assegnazione ind_numero = № memorizza nella variabile ind_numero lindirizzo della variabile numero, come indicato in figura. La variabile ind_numero, in quanto contiene lindirizzo di unaltra variabile, è detta variabile puntatore o puntatore. Pertanto:
Lesecuzione della precedente istruzione di dichiarazione ind_numero = № aggiornerebbe la precedente tabella come indica la figura: i puntatori sono variabili usate per memorizzare gli indirizzi di altre variabili.
Analogamente, come indica la seguente tabella: Anche le variabili d, punt_tab e punt_car sono puntatori.
Operatore di indirezione *. Per usare un indirizzo memorizzato, C fornisce loperatore di indirezione * (detto anche di deriferimento o di risoluzione del riferimento). Il simbolo *, seguito immediatamente da un puntatore (senza spazi intermedi), significa la variabile il cui indirizzo è memorizzato in Così, se ind_numero è un puntatore, la scrittura Anche * è un operatore unario, che restituisce il valore delloggetto puntato dal suo operando (ossia da un puntatore). Quindi, per visualizzare il contenuto della variabile numero (ossia lintero 22), si può scrivere indifferentemente:
Usando un puntatore ( ind_numero ), il contenuto di numero si ottiene leggendo dapprima il contenuto del puntatore, che è lindirizzo di numero, quindi usando tale indirizzo per ottenere il contenuto di numero. Pertanto luso di un puntatore fa eseguire al computer un doppio passaggio: dapprima richiama lindirizzo di un dato, quindi lo usa per richiamare il dato effettivo. Questo è un modo indiretto per ottenere il valore finale, e in effetti questa procedura è detta indirizzamento indiretto. La sua utilità sarà vista più avanti. printf(%d, numero); stampa il contenuto della variabile numero che si legge oppure la coppia di istruzioni che si leggono stampa il contenuto della variabile il cui indirizzo è memorizzato nella variabile ind_numero ind_numero = № printf(%d, *ind_numero); listruzione
Dichiarazione dei puntatori. Come tutte le variabili, anche i puntatori devono essere dichiarati prima di essere usati. Nel dichiarare un puntatore, il C richiede che sia indicato il tipo dati della variabile puntata. Ad es., se lindirizzo memorizzato nel puntatore ind_numero è quello di un intero, la dichiarazione corretta del puntatore è: (quindi la dichiarazione viene letta allindietro). Si osservi che la precedente dichiarazione specifica anche che ind_numero deve essere un puntatore (dato che è usato con loperatore di indirezione *).
Consideriamo il programma seguente:
La sua uscita è: La sola utilità di questo programma è di aiutarci a capire cosa è memorizzato dove. Sarebbe stato certamente più semplice se il puntatore del programma avesse potuto essere dichiarato come point ind_num;. Tuttavia, tale dichiarazione non conterrebbe informazioni sulla memoria usata dalla variabile il cui indirizzo è memorizzato in ind_num. Questa informazione è essenziale quando il puntatore è usato con loperatore di indirezione, come avviene nella seconda chiamata a printf() nel programma precedente. Infatti Lindirizzo memorizzato in ind_num è 0012FF78 Il valore puntato da ind_num è 22 Lindirizzo ora memorizzato in ind_num è 002FF7C Il valore ora puntato da ind_num è 158
se in ind_num è memorizzato lindirizzo di un carattere, quando si usa lindirizzo viene richiamato 1 byte di memoria; se in ind_num è memorizzato lindirizzo di un intero, vengono richiamati 2 byte di memoria, mentre se in ind_num è memorizzato lindirizzo di un numero in virgola mobile, vengono richiamati 4 byte. È per questa ragione che la dichiarazione di un puntatore deve comprendere il tipo di variabile che viene puntata.
&a = 0012FF7C Prova che * e & sono complementari: &*ind_a = 0012FF7C *&ind_a = 0012FF7C I due operatori & e * sono luno il complemento dellaltro; qualora fossero applicati consecutivamente a ind_a, in qualsiasi ordine, il loro effetto si annullerebbe, come mostra il seguente programma: #include void main(void) { int a, *ind_a; a = 7; ind_a = &a; printf("\n &a = %p\n", &a); printf("Prova che * e & sono complementari:\n &*ind_a = %p\n *&ind_a = %p\n", &*ind_a, *&ind_a); } Esso produce la seguente uscita:
Come si chiamava il padre della protagonista di questa famosa tragedia?
Codice ASCII di un carattere ( %o, %x ). Dato che il C non possiede un operatore specifico che fornisca il codice ASCII di un carattere, esso si può ottenere semplicemente: memorizzando il carattere in una variabile; stampando il valore della variabile, ossia stampando la variabile tramite la sequenza di controllo di formato %d. Ciò si può ottenere con il programma seguente: #include void main(void) { char var = 'a'; printf("\nIl codice ASCII decimale di %c è %d.",var,var); } Il codice ASCII decimale di a è 97. che produce luscita
Naturalmente il codice ASCII può essere ottenuto anche in formato ottale o esadecimale, sostituendo la sequenza di controllo di formato %d rispettivamente con %o o con %x.
Ecco luscita prodotta: Il valore memorizzato in ind_var è: 0012FF7C Il valore puntato da ind_var è: a Il codice ASCII di a è 61 #include void main(void) { char *ind_var; char var = 'a'; ind_var = &var; printf("Il valore memorizzato in ind_var è: %p", ind_var); printf("\nIl valore puntato da ind_var è: %c", *ind_var); printf("\nIl codice ASCII di %c è %x", var, *ind_var); } Il programma seguente stampa lindirizzo di una variabile di tipo carattere, il valore contenuto in tale indirizzo e il codice ASCII della variabile usando un puntatore.
Il programma determina lo stato della memoria illustrato nela figura seguente