Dichiarazioni
Ad eccezione delle etichette, ogni identificatore che il programmatore intende utilizzare in un programma C++, sia esso per una
variabile, una costante simbolica, di tipo o di funzione, va dichiarato
prima di essere utilizzato.
Ci sono diversi motivi che giustificano la necessita` di una dichiarazione; nel caso di variabili, costanti o tipi:
- consente di stabilire la quantita` di memoria necessaria alla
memorizzazione di un oggetto;
- determina l'interpretazione da attribuire ai vari bit che compongono
la regione di memoria utilizzata per memorizzare l'oggetto, l'insieme
dei valori che puo` assumere e le operazioni che possono essere fatte
su di esso;
- permette l'esecuzione di opportuni controlli per determinare errori
semantici;
- fornisce eventuali suggerimenti al compilatore;
nel caso di funzioni, invece una dichiarazione:
- determina numero e tipo dei parametri e il tipo del valore restituito;
- consente controlli per determinare errori semantici;
Le dichiarazioni hanno anche altri compiti che saranno chiariti in seguito.
Tipi primitivi
Un tipo e` una coppia < V, O >,
dove V e` un insieme di valori e O e` un insieme di
operazioni per la creazione e la manipolazione di elementi di V.
In un linguaggio di programmazione i tipi rappresentano le categorie di informazioni
che il linguaggio consente di manipolare. Il C++ fornisce sei tipi fondamentali (o primitivi):
- bool
- char
- wchar_t
- int
- float
- double
Abbiamo gia visto (vedi Vero e falso)
il tipo bool e sappiamo che esso serve a rappresentare i valori di
verita`; su di esso sono definite sostanzialmente le usuali operazioni
logiche (&& per l'AND, || per l'OR, ! per
la negazione...) e non ci soffermeremo oltre su di esse, solo si faccia
attenzione a distinguerle dalle operazioni logiche su bit (rispettivamente
&, |, ~...).
Il tipo char e` utilizzato per rappresentare piccoli interi
(e quindi su di esso possiamo eseguire le usuali operazioni
aritmetiche) e singoli caratteri; accanto ad esso troviamo anche il
tipo wchar_t che serve a memorizzare caratteri non rappresentabili
con char (ad esempio i caratteri unicode).
int e` utilizzato per rappresentare interi in un intervallo piu`
grande di char.
Infine float e double rappresentano
entrambi valori in virgola mobile, float per valori in precisione semplice e double per quelli in doppia precisione.
Ai tipi fondamentali e` possibile applicare i qualificatori signed
(con segno), unsigned (senza segno), short (piccolo) e long (lungo) per selezionare differenti intervalli
di valori; essi tuttavia non sono liberamente applicabili a tutti i tipi:
short si applica solo a int, signed e unsigned
solo a char e int e infine long solo a int e
double. In definitiva sono disponibili i tipi:
- bool
- char
- wchar_t
- short int
- int
- long int
- signed char
- signed short int
- signed int
- signed long int
- unsigned char
- unsigned short int
- unsigned int
- unsigned long int
- float
- double
- long double
Il tipo int e` per default signed e quindi e` equivalente
a tipo signed int, invece i tipi char,
signed char e unsigned char sono
considerate categorie distinte.
I vari tipi sopra elencati, oltre a differire per l'intervallo dei valori
rappresentabili, differiscono anche per la quantita` di memoria richiesta per rappresentare un valore di quel tipo (che pero` puo` variare
da implementazione a implementazione). Il seguente programma permette di
conoscere la dimensione di alcuni tipi come multiplo di char (di solito rappresentato su 8 bit), modificare il codice per trovare la
dimensione degli altri tipi e` molto semplice e viene lasciato per
esercizio:
|
#include < iostream >
using namespace std;
int main(int, char* []) {
cout << "bool: " << sizeof(bool) << endl;
cout << "char: " << sizeof(char) << endl;
cout << "short int: " << sizeof(short int) << endl;
cout << "int: " << sizeof(int) << endl;
cout << "float:" << sizeof(float) << endl;
cout << "double: " << sizeof(double) << endl;
return 0;
}
|
Una veloce spiegazione sul listato:
le prime due righe permettono di utilizzare una libreria (standard) per
eseguire l'output su video; la libreria iostream dichiara l'oggetto
cout il cui compito e` quello di visualizzare l'output che gli viene
inviato tramite l'operatore di inserimento <<.
L'operatore sizeof(<Tipo>) restituisce la dimensione di
Tipo, mentre endl inserisce un ritorno a capo e forza
la visualizzazione dell'output. L'ultima istruzione serve a terminare il
programma. Infine main e` il nome che
identifica la funzione principale, ovvero il corpo del programma,
parleremo in seguito e piu` in dettaglio
di main().
Tra i tipi fondamentali sono definiti gli operatori di conversione,
il loro compito e` quello di trasformare un valore di un tipo in un valore
di un altro tipo.
Non esamineremo per adesso l'argomento, esso verra` ripreso in
una apposita appendice.
Variabili e costanti
Siamo ora in grado di dichiarare variabili e costanti.
La sintassi per la dichiarazione delle variabili e`
< Tipo > < Lista Di Identificatori > ;
Ad esempio:
|
int a, b, B, c;
signed char Pippo;
unsigned short Pluto; // se omesso si intende int
|
Innanzi tutto ricordo che il C++ e` case sensitive, cioe` distingue le lettere
maiuscole da quelle minuscole, infine si noti il punto e virgola che segue sempre
ogni dichiarazione.
La prima riga dichiara quattro variabili di tipo int, mentre la seconda una
di tipo signed char. La terza dichiarazione e` un po'
particolare in quanto apparentemente manca la keyword int, in realta` poiche`
il default e` proprio int essa puo` essere omessa; in conclusione la terza
dichiarazione introduce una variabile di tipo unsigned short int.
Gli identificatori che seguono il tipo sono i nomi delle variabili, se piu` di un
nome viene specificato essi devono essere separati da una virgola.
E` possibile specificare un valore con cui inizializzare ciascuna variabile facendo
seguire il nome dall'operatore di assegnamento = e da un valore o una
espressione che produca un valore del corrispondente tipo:
|
int a = -5, b = 3+7, B = 2, c = 1;
signed char Pippo = 'a';
unsigned short Pluto = 3;
|
La dichiarazione delle costanti e` identica a quella delle variabili
eccetto che deve sempre essere specificato un valore e la dichiarazione
inizia con la keyword const:
|
const a = 5, c = -3; // int e` sottointeso
const unsigned char d = 'a', f = 1;
const float g = 1.3;
|
Scope e lifetime
La dichiarazione di una variabile o di un qualsiasi
altro identificatore si estende dal punto immediatamente successivo la dichiarazione
(e prima dell'eventuale inizializzazione) fino alla fine del blocco di istruzioni in
cui e` inserita (un blocco di istruzioni e` racchiuso sempre tra una coppia di
parentesi graffe). Cio` vuol dire che quella dichiarazione non e` visibile
all'esterno di quel blocco, mentre e` visibile in eventuali blocchi annidati dentro
quello dove la variabile e` dichiarata.
Il seguente schema chiarisce la situazione:
|
// Qui X non e` visibile
{
... // Qui X non e` visibile
int X = 5; // Da ora in poi esiste una variabile X
... // X e` visibile gia` prima di =
{ // X e` visibile anche in questo blocco
...
}
...
} // X ora non e` piu` visibile
|
All'interno di uno stesso blocco non e` possibile dichiarare piu` volte lo stesso
identificatore, ma e` possibile ridichiararlo in un blocco annidato; in tal caso la
nuova dichiarazione nasconde quella piu` esterna che ritorna visibile non appena si
esce dal blocco ove l'identificatore viene ridichiarato:
|
{
... // qui X non e` ancora visibile
int X = 5;
... // qui e` visibile int X
{
... // qui e` visibile int X
char X = 'a'; // ora e` visibile char X
... // qui e` visibile char X
} // qui e` visibile int X
...
} // X ora non piu` visibile
|
All'uscita dal blocco piu` interno l'identificatore ridichiarato assume il valore che aveva prima
di essere ridichiarato:
|
{
...
int X = 5;
cout << X << endl; // stampa 5
while (--X) { // riferisce a int X
cout << X << ' '; // stampa int X
char X = '-';
cout << X << ' '; // ora stampa char X
}
cout << X << endl; // stampa di nuovo int X
}
|
Una dichiarazione eseguita fuori da ogni blocco introduce un identificatore globale
a cui ci si puo` riferire anche con la notazione ::<ID>. Ad esempio:
|
int X = 4; // dichiarazione esterna ad ogni blocco
int main(int, char* []) {
int X = -5, y = 0;
/* ... */
y = ::X; // a y viene assegnato 4
y = X; // assegna il valore -5
return 0;
}
|
Abbiamo appena visto che per assegnare un valore ad una variabile si usa lo stesso
metodo con cui la si inizializza quando viene dichiarata. L'operatore :: e`
detto risolutore di scope e, utilizzato nel modo appena visto, permette di
riferirsi alla dichiarazione globale di un identificatore.
Ogni variabile oltre a possedere uno scope, ha anche un propria durata
(lifetime), viene creata subito dopo la dichiarazione (e prima
dell'inizializzazione! ndr) e viene distrutta alla fine del blocco dove e` posta la
dichiarazione; fanno eccezione le variabili globali che vengono distrutte alla fine
dell'esecuzione del programma. Da cio` si deduce che le variabili locali (ovvero
quelle dichiarate all'interno di un blocco) vengono create ogni volta che si giunge
alla dichiarazione, e distrutte ogni volta che si esce dal blocco; e` tuttavia
possibile evitare che una variabile locale (dette anche automatiche) venga distrutta
all'uscita dal blocco facendo precedere la dichiarazione dalla keyword static:
|
void func() {
int x = 5; // x e` creata e
// distrutta ogni volta
static int c = 3; // c si comporta in modo diverso
/* ... */
}
|
La variabile x viene creata e inizializzata a 5 ogni volta che func() viene eseguita e viene distrutta alla fine
dell'esecuzione della funzione; la variabile c invece viene
creata e inizializzata una sola volta, quando la funzione viene chiamata la
prima volta, e distrutta solo alla fine del programma.
Le variabili statiche conservano sempre l'ultimo valore che viene assegnato
ad esse e servono per realizzare funzioni il cui comportamento e` legato a
computazioni precedenti (all'interno della stessa esecuzione del programma) oppure per ragioni di efficenza.
Infine la keyword static non modifica lo scope.
Pagina precedente - Pagina successiva
C++, una panoramica sul linguaggio - seconda edizione © Copyright 1996-1999, Paolo Marotta
|