Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 41 di 93
  • livello avanzato
Indice lezioni

If, else: le istruzioni condizionali

Il costrutto if-else è una istruzione condizionale, permette cioè di eseguire istruzioni o blocchi codice a seconda del verificarsi di una condizione.
Il costrutto if-else è una istruzione condizionale, permette cioè di eseguire istruzioni o blocchi codice a seconda del verificarsi di una condizione.
Link copiato negli appunti

Il paradigma di programmazione strutturata consente di redirigere il flusso delle istruzioni a tempo di esecuzione, in base allo stato dinamico del programma.

Ciò significa che il programmatore può predisporre molteplici blocchi di istruzioni da eseguire alternativamente, la cui esecuzione è condizionata al soddisfacimento di alcune precondizioni.

In C++, la programmazione strutturata è implementata mediante le istruzioni per il controllo di flusso. In questa lezione C++ tratteremo nel dettaglio l'istruzione condizionale if, la cui sintassi estesa è la seguente:

if (<espressione1>)
{
// istruzioni da eseguire se la espressione1 = true
}
else if (<espressione2>)
{
// istruzioni da eseguire se espressione1 = false e espressione2 = true
}
// ...
else if (<espressioneN>)
{
// istruzioni da eseguire se espressioneX = false (per X=1,2,..N-1) e espressioneN = true
}
else
{
// istruzioni da eseguire se nessuna delle espressioni è verificata
}

Ogni blocco delimitato dall'istruzione condizionale if può essere seguito da un numero arbitrario di blocchi delimitati dalle istruzioni else if e/o da un solo blocco else. Queste ultime sono opzionali, per cui nella sua forma più semplice il costrutto consta di un solo blocco associato all'istruzione if.

Le parentesi graffe hanno la funzione di delimitare il blocco di istruzioni da associare ad ogni condizione, e al contempo definiscono un ambito di visibilità separato dal contesto esterno per le variabili ivi dichiarate. Esse possono essere omesse solo nel caso di blocchi composti da un'unica istruzione.

Come clausola per la determinazione del blocco di istruzioni da eseguire è ammessa una qualsiasi espressione che restituisca un valore di tipo bool o di un altro tipo per il quale esista una catena di conversione al tipo booleano, ad esempio i tipo interi o puntatori in genere.

Anche la definizione o l'assegnamento di variabili sono espressioni valide, e nel caso specifico della dichirazione di variabili, il loro contesto coincide con l'unione dei blocchi if-else if-else congiunti. Ad esempio nel caso seguente, a è visibile in entrambe le ramificazioni del costrutto.

if (int a = <espressione>)
{
// a != 0, un intero diverso da 0 è implicitamente convertito nel valore booleano true
}
else
{
// a = 0, 0 è implicitamente convertito nel valore booleano false
}

Tuttavia, è bene ricordare che nel caso di tipi complessi, come quelli definiti dall'utente, il risultato restituito da un operatore di assegnamento dipende dalla particolare implementazione. Pertanto, l'uso diretto di una dichiarazione o assegnamento come espressione per un'istruzione condizionale può avere effetti negativi sulla leggibilità del codice.

Istruzioni if-else annidate

Le istruzioni condizionali if possono essere annidate per creare strutture di controllo più raffinate. Un esempio è mostrato nel listato seguente, dove la valutazione ed esecuzione delle istruzioni contenute all'interno del blocco if annidato è condizionato dalla valutazione della clausola del blocco if più esterno.

if (<espressione1>)
{
if (<espressione2>)
{
// istruzioni da eseguire se espressione1 = true e espressione2 = true
}
else
{
// istruzioni da eseguire se espressione1 = true e espressione2 = false
}
}

Ogni blocco if annidato può essere corredatato dai corrispondenti blocchi else if ed else. Lo standard C++ raccomanda una limite teorico pari a 256 livelli di annidamento per le strutture di controllo di flusso, cha va ben oltre il limite imposto dal buon senso.

Valutazione a corto circuito

La valutazione a corto cicuito è una caratteristica propria degli operatori booleani, che consiste nel valutare solo il minimo numero di operandi sufficiente per il calcolo del risultato totale, procedendo da sinistra verso destra.

Ad esempio, nel frammento seguente, per valutare il valore da assegnare a c si procede esaminando il valore di a, che è sufficiente ad attribuire il risultato totale senza la necessità di valutare b.

bool a = true;
bool b = false;
bool c = a || b;

Tale caratteristica si rivela molto utile quando il contesto è la valutazione di un'espressione associata ad una istruzione di controllo di flusso come if, poichè in alcuni casi consente di compattare livelli di controllo annidati.

Un esempio della valutazione a corto circuito è illustrato nei due listati seguenti, che sono equivalenti sia sotto il profilo sintattico che semantico:

// Listato con blocchi annidati
int* ptr = nullptr;
if (ptr) /* blocco esterno */
{
if (*ptr > 0) /* blocco annidato */
{
// istruzioni da eseguire solo se il valore puntato da ptr è positivo.
}
}

// Listato senza blocchi annidati
int* ptr = nullptr;
if (ptr && *ptr > 0) /* blocchi collassati */
{
// istruzioni da eseguire solo se il valore puntato da ptr è positivo.
}

Poichè infatti l'operazione di dereferenziazione di ptr è eseguita solo nel caso in cui l'indirizzo di memoria puntato non sia nullo, è perfettamente lecito collassare le due clausole mediante l'uso dell'operatore &&.

Espansione C++17: if con istruzione di inizializzazione

Lo standard C++17 prevede un'espansione del costrutto if-else tradizionale che è specificamente pensata per alcuni casi d'uso.

In questa nuova forma sintattica, la clausola associata al blocco di istruzioni può essere corredata dall'inizializzazione di una o più variabili seguita da ';' come illustrato nel seguente frammento:

if (<inizializzazione>; <espressione>)
{
// istruzioni
}

La parte di inizializzazione può essere una qualsiasi espressione o la definizione di una o più variabili dello stesso tipo, separate da virgole.

L'utilità di questa versione consiste nel poter delimitare l'ambito di visibilità della variabile inizializzata al solo contesto costituito dal'unione di blocchi if-else if-else congiunti, senza "inquinare" il contesto esterno.

Il frammento seguente mostra, a titolo di esempio, un possibile caso d'uso in cui l'inizializzazione avviene per mezzo dell'invocazione di una funzione definita dall'utente di nome login(), che restituisce un codice di stato che viene immagazzianato nella variabile code.

// funzione che effettua il login da remoto
// presso una piattaforma che eroga un servizio
int login(const char* name, const char* password);
// frammento di codice per l'accesso al servizio
if (int code = login("paperino", "$quack!"); code == 0)
{
// istruzioni da eseguire solo se il login è stato effettuato
}
else if (code == -1)
{
// username o password non validi
}
else
{
// errore generico, timeout...
std::cout << "codice di errore: " << code << std::endl;
}

La particolarità consiste nel fatto che il contesto di visibilità di code è interamente contenuto nei blocchi if-else if-else, e non ha interserzione con l'ambiente esterno.

In pratica, l'uso di questo costrutto è equivalente alla definizione di un contesto esplicito mendiante l'uso di parentesi graffe che contenga sia la definizione della variabile code, sia le istruzioni per il controllo di flusso ad essa associate, ma gode di una sintassi più concisa.

Ti consigliamo anche