Vai al contenuto
Topos Uranos

Assegnamento, effetti collaterali e comportamento indefinito

Riepilogo

Un’espressione C++ può fare due cose distinte: dare un valore e cambiare lo stato del programma. x + 1 dà solo un valore; x = 7 dà un valore e, inoltre, cambia x; x++ dà il valore che x aveva e lascia in x un altro valore. Questa lezione studia tale differenza tra il valore e l’effetto di un’espressione: l’assegnamento come espressione, l’assegnamento composto, l’incremento e il decremento nelle loro forme prefissa e postfissa, e l’ordine in cui si valutano le parti di un’espressione, che lo standard fissa solo in parte. Ne segue la domanda che ordina il resto della lezione: che cosa promette lo standard riguardo a un programma. La risposta è un catalogo di quattro gradi, dal comportamento definito a quello indefinito, che riunisce tutti i casi visti finora e dice, per ciascuno, che cosa lo rileva (il compilatore, il sanitizer, la libreria) e che cosa non lo rileva nessuno.

Obiettivi di apprendimento

  1. Distinguere il valore di un’espressione dal suo effetto collaterale in assegnamenti, incrementi e chiamate.
  2. Prevedere il risultato di x++ e ++x usati come valore, e seguire a mano un programma con assegnamenti composti.
  3. Riconoscere espressioni che modificano due volte una variabile, o che la leggono e la modificano, senza un ordine fissato dallo standard, e riscriverle in istruzioni separate.
  4. Classificare un costrutto come definito, definito dall’implementazione, non specificato o indefinito, con un esempio di ciascuna categoria.
  5. Dire, per ciascun comportamento indefinito del catalogo, se il compilatore lo segnala con un avviso, se il sanitizer o la libreria lo fermano, o se non lo rileva nessuno.

Il valore e l’effetto

La lezione Aritmetica ha studiato l’espressione come un albero che si riduce a un valore: 2 + 3 * 4 vale 14, e calcolarla non cambia nulla al di fuori dell’espressione stessa. La lezione Relazioni e logica ha fatto lo stesso con le condizioni, il cui valore è true o false. Ora, ci sono espressioni che, oltre a dare un valore, lasciano un segno nello stato del programma: cambiano il valore di una variabile, scrivono sullo schermo o leggono dall’input. Questo segno si chiama effetto collaterale, o semplicemente effetto, e la parola «collaterale» non vuol dire che conti di meno: in x = 7 l’effetto, che x passi a valere 7, è proprio ciò che si cerca.

Se è così, ogni espressione va letta con due domande: che cosa dà e che cosa fa. Tre esempi fissano la distinzione. count + 1 dà il successivo di count e non fa nulla: count conserva il suo valore. count = 9 dà 9 e fa sì che count valga 9. std::println("ciao") è anch’essa un’espressione, una chiamata a una funzione, e ciò che conta di essa è il suo effetto: scrivere una riga. Le funzioni proprie si studieranno nella lezione Funzioni, e lì la distinzione ricomparirà: una funzione che si limita a calcolare il suo risultato è più facile da capire e da testare di una che, inoltre, cambia qualcosa al di fuori di sé.

Un’istruzione come count + 1; è C++ valido: il valore si calcola e si scarta, così che non serve a nulla, e GCC lo segnala con -Wunused-value. Un’istruzione espressione ha senso per il suo effetto. Ne segue che le espressioni con effetto sono quelle che fanno avanzare il programma, e anche quelle che richiedono attenzione, perché l’effetto avviene in un certo momento, e quel momento non è sempre quello che il lettore suppone.