Inizieremo ad esaminare i costrutti del C++ partendo proprio dalle
istruzioni e dalle espressioni, perche` in questo modo sara` piu` semplice
esemplificare alcuni concetti che verranno analizzati nel seguito. Per adesso
comunque analizzaremo solo le istruzioni per il controllo del flusso e
l'assegnamento, le rimanenti (poche) istruzioni verranno discusse via via che
sara` necessario nei prossimi capitoli.
Assegnamento
Il C++ e` un linguaggio pesantemente basato sul
paradigma imperativo, questo vuol dire che un programma C++ e` sostanzialmente una
sequenza di assegnamenti di valori a variabili. E` quindi naturale iniziare parlando
proprio dell'assegnamento.
L'operatore di assegnamento e` denotato dal simbolo = (uguale) e viene
applicato con la sintassi:
lvalue = rvalue;Il termine lvalue indica una qualsiasi espressione che riferisca ad una regione di memoria (in generale un identificatore di variabile), mentre un rvalue e` una qualsiasi espressione la cui valutazione produca un valore. Ecco alcuni esempi:
Pippo = 5; Topolino = 'a'; Clarabella = Pippo; Pippo = Pippo + 7; Clarabella = 4 + 25;Il risultato dell'assegnamento e` il valore prodotto dalla valutazione della parte destra (rvalue) e ha come effetto collaterale l'assegnazione di tale valore alla regione di memoria denotato dalla parte sinistra (lvalue), cio` vuol dire che ad esempio che il primo assegnamento sopra produce come risultato il valore 5 e che dopo tale assegnamento la valutazione della variabile Pippo produrra` tale valore fino a che un nuovo assegnamento non verra` eseguito su di essa.
Clarabella = Pippo = 5;Essendo l'operatore di assegnamento associativo a destra, l'esempio visto sopra e` da interpretare come
Clarabella = (Pippo = 5);cioe` viene prima assegnato 5 alla variabile Pippo e il risultato di tale assegnamento (il valore 5) viene poi assegnato alla variabile Clarabella.
Pippo += 5; // equivale a Pippo = Pippo + 5; Pippo -= 10; // equivale a Pippo = Pippo - 10; Pippo *= 3; // equivale a Pippo = Pippo * 3;si tratta cioe` di operatori derivati dalla concatenazione dell'operatore di assegnamento con un altro operatore binario.
Pippo++; // cioe` Pippo += 1; ++Pippo; // sempre Pippo += 1; Pippo--; // Pippo -= 1; --Pippo; // Pippo -= 1;Questi due operatori possono essere utilizzati sia in forma prefissa (righe 2 e 4) che in forma postfissa (righe 1 e 3), il risultato comunque non e` proprio identico: la forma postfissa restituisce come risultato il valore della variabile e poi incrementa tale valore e lo assegna alla variabile, la forma prefissa invece prima modifica il valore associato alla variabile e poi restituisce tale valore:
Clarabella = ++Pippo; // equivale a: Pippo++; Clarabella = Pippo; // invece Clarabella = Pippo++; // equivale a: Clarabella = Pippo; Pippo++;
:: | risolutore di scope |
. -> [ ] ( ) ( ) ++ -- |
selettore di campi selettore di campi sottoscrizione chiamata di funzione costruttore di valori post incremento post decremento |
sizeof ++ -- ~ ! - + & * new delete delete[ ] ( ) |
dimensione di pre incremento pre decremento complemento negazione meno unario piu` unario indirizzo di dereferenzazione allocatore di oggetti deallocatore di oggetti deallocatore di array conversione di tipo |
.* ->* |
selettore di campi selettore di campi |
* / % |
moltiplicazione divisione modulo (resto) |
+ - | somma sottrazione |
<< >> |
shift a sinistra shift a destra |
< <= > >= |
minore di minore o uguale maggiore di maggiore o uguale |
== != |
uguale a diverso da |
& | AND di bit |
^ | OR ESCLUSIVO di bit |
| | OR INCLUSIVO di bit |
&& | AND logico |
|| | OR logico (inclusivo) |
? : | espressione condizionale |
= *= /= %= += -= <<= >>= &= |= ^= |
assegnamento semplice moltiplica e assegna divide e assegna modulo e assegna somma e assegna sottrae e assegna shift sinistro e assegna shift destro e assegna AND e assegna OR inclusivo e assegna OR esclusivo e assegna |
throw | lancio di eccezioni |
, | virgola |
<Cond> ? <Expr2> : <Expr3>Per definire la semantica di questo operatore e` necessario prima parlare di vero e falso in C++. A differenza di linguaggi quali il Pascal, il C++ non fornisce un tipo primitivo (vedi tipi primitivi) per codificare i valori booleani; essi sono rappresentati tramite valori interi: 0 (zero) indica falso e un valore diverso da 0 indica vero. Cio` implica che ovunque sia richiesta una condizione e` possibile mettere una qualsiasi espressione che possa produrre un valore intero (quindi anche una somma, ad esempio). Non solo, dato che l'applicazione di un operatore booleano o relazionale a due sottoespressioni produce 0 o 1 (a seconda del valore di verita` della formula), e` possibile mescolare operatori booleani, relazionali e aritmetici.
if ( <Condizione> ) <Istruzione1> ;oppure
if ( <Condizione> ) <Istruzione1> ; else <Istruzione2> ;L'else e` quindi opzionale, ma, se utilizzato, nessuna istruzione deve essere inserita tra il ramo if e il ramo else. Vediamo ora la semantica di tale istruzione.
if ( X==10 ) X--; else { Y++; Z*=Y; }Ancora alcune osservazioni: il linguaggio prevede che due istruzioni consecutive siano separate da ; (punto e virgola), in particolare si noti il punto e virgola tra il ramo if e l'else; l'unica eccezione alla regola e` data dalle istruzioni composte (cioe` sequenze di istruzioni racchiuse tra parentesi graffe) che non devono essere seguite dal punto e virgola (non serve, c'e` la parentesi graffa). Per risolvere eventuali ambiguita` il compilatore lega il ramo else con la prima occorrenza libera di if che incontra tornando indietro (si considerino Pippo, Pluto e Topolino variabili intere):
if (Pippo) if (Pluto) Topolino = 1; else Topolino =2;viene interpretata come
if (Pippo) if (Pluto) Topolino = 1; else Topolino =2;l'else viene cioe` legato al secondo if.
while ( <Condizione> ) <Istruzione> ;Al solito, Istruzione indica una istruzione singola, se e` necessaria una sequenza di istruzioni essa deve essere racchiusa tra parentesi graffe.
do <Istruzione> while ( <Condizione> ) ;Nuovamente, Istruzione indica una istruzione singola, se e` necessaria una sequenza di istruzioni essa deve essere racchiusa tra parentesi graffe; si noti inoltre che Istruzione non e` seguita da punto e virgola.
// Calcolo del fattoriale tramite while if (InteroPositivo) { Fattoriale = InteroPositivo; while (--InteroPositivo) Fattoriale *= InteroPositivo; } else Fattoriale = 1; // Calcolo del fattoriale tramite do-while Fattoriale = 1; if (InteroPositivo) do Fattoriale *= InteroPositivo while (--InteroPositivo);
for ( <Inizializzazione> ; <Condizione> ; <Iterazione> ) <Istruzione> ;Inizializzazione puo` essere una espressione che inizializza le variabili del ciclo o una dichiarazione di variabili (nel qual caso le veriabili dichiarate hanno scope e lifetime limitati a tutto il ciclo); Condizione e` una qualsiasi espressione a valori interi; e Iterazione e` una istruzione da eseguire dopo ogni iterazione (solitamente un incremento). Tutti e tre gli elementi appena descitti sono opzionali, in particolare se Condizione non viene specificata si assume che essa sia sempre verificata.
<Inizializzazione> ; while ( <Condizione> ) { <Istruzione> ; <Iterazione> ; }Una eventuale istruzione continue (vedi paragrafo successivo) in Istruzione causa un salto a Iterazione nel caso del ciclo for, nel while invece causa una uscita dal ciclo.
for (Fatt = IntPos ? IntPos : 1; IntPos > 1; /* NOP */) Fatt *= (--IntPos);
Fattoriale = 1; while (1) { // all'infinito... if (InteroPositivo > 1) { Fattoriale *=Interopositivo--; continue; } break; // se eseguita allora InteroPositivo <= 1 // continue provoca un salto in questo punto }
switch ( <Espressione> ) { case <Valore1> : <Istruzione> ; /* ... */ case <ValoreN> : <Istruzione> ; default : <Istruzione> ; }Espressione e` una qualunque espressione capace di produrre un valore intero; Valore1...ValoreN sono costanti diverse tra loro; Istruzione e` una qualunque sequenza di istruzioni (non racchiuse tra parentesi graffe).
switch (Pippo) { case 1 : Topolino = 5; case 4 : Topolino = 2; Clarabella = 7; default : Topolino = 0; } |
switch (Pluto) { case 5 : Pippo = 3; case 6 : Pippo = 5; case 10 : Orazio = 20; Tip = 7; } // niente caso default |
switch (Pippo) { case 1 : Topolino = 5; break; case 4 : Topolino = 2; Clarabella = 7; break default : Topolino = 0; break; }
<Etichetta> : <Istruzione>che serve anche a dichiarare Etichetta.
goto <Etichetta>ad esempio:
if (Pippo == 7) goto OK; Topolino = 5; /* ... */ OK : Pluto = 7;Si noti che una etichetta puo` essere utilizzata anche prima di essere dichiarata. Esiste una limitazione all'uso del goto: il bersaglio dell'istruzione (cioe` Etichetta) deve trovarsi all'interno della stessa funzione dove appare l'istruzione di salto.