Punto di sequenza
# Punto di sequenza

Un punto di sequenza (sequence point) è un punto nell'esecuzione di un programma informatico nel quale è garantito il fatto che tutti gli effetti collaterali delle istruzioni precedenti siano stati realizzati e nessun effetto collaterale delle istruzioni seguenti sia stato realizzato. I punti di sequenza sono un concetto chiave in linguaggi come C e C++, nei quali sono importanti per determinare la validità di una certa istruzione. L'aggiunta di ulteriori punti di sequenza nel codice può essere necessaria, ad esempio, per rendere un'espressione ben definita e garantire l'unicità dell'ordine di valutazione delle operazioni in essa contenute.

In C++11 l'espressione sequence point è stata sostituita da sequencing. Date due espressioni, vi sono tre possibili casi di sequencing:

* la valutazione di un'espressione può essere "sequenced before" rispetto ad un'altra espressione o, equivalentemente, l'altra espressione è "sequenced after".
* le espressioni sono "indeterminately sequenced", ovvero una delle due è "sequenced before" rispetto all'altra, ma l'ordine è indefinito.
* la valutazione è "unsequenced".

Mentre nel primo caso la semantica del programma è ben definita, gli altri due casi sono problematici. In particolare, la valutazione di espressioni unsequenced può sovrapporsi e, se le due espressioni dipendono da uno stato condiviso, il risultato è indefinito e può avere effetti catastrofici. Tale situazione è un problema che si può incontrare facilmente in calcolo parallelo (corsa critica), ma può verificarsi anche in programmi strettamente sequenziali. Ad esempio, nella valutazione dell'espressione (a = 1) + (b = a) parte dell'assegnamento a = 1 (es. metà dei bit) può essere completato prima di b = a e parte dopo, per cui al termine della valutazione b può contenere uno stato intermedio di a, totalmente privo di significato.

## Esempi di ambiguità

Date due funzioni f e g, poiché in C e C++ l'operatore + non genera un punto di sequenza, nella valutazione di f+g non è possibile sapere a priori quale chiamata tra f e g verrà valutata per prima. Al contrario, l'operatore virgola introduce un punto di sequenza, per cui nella valutazione dell'espressione f,g la chiamata f verrà sempre valutata (e i suoi effetti collaterali realizzati) prima della valutazione di g.

I punti di sequenza svolgono un ruolo importante nel definire il comportamento di espressioni che manipolano più volte una stessa variabile. Un esempio comune in C è l'espressione i=i++, che apparentemente assegna ad i il suo stesso valore ed incrementa la variabile i. In realtà il valore finale di i al termine della valutazione non è definito, poiché l'operatore di assegnamento non determina un punto di sequenza, per cui l'incremento potrebbe essere eseguito prima, durante, o dopo l'assegnamento. Altri linguaggi possono avere regole diverse: per esempio il C# definisce la precedenza degli operatori di assegnamento e di incremento in modo tale da garantire che il risultato dell'espressione i=i++ sia corretto e ben definito.

## Punti di sequenza in C e C++

In C e C++, punti di sequenza sono garantiti nei seguenti casi:

* Tra la valutazione degli operandi sinistro e destro degli operatori &&, || (come conseguenza della valutazione a corto circuito), e operatore virgola. Ad esempio, nella valutazione dell'espressione *p++ != 0 && *q++ != 0 tutti gli effetti collaterali dell'espressione *p++ != 0 sono completati prima di ogni tentativo di accedere alla variabile q.
* Tra la valutazione del primo operando di un operatore ternario ?: e i due rimanenti operandi. Ad esempio, nella valutazione dell'espressione a = (*p++) ? (*p++) : 0 c'è un punto di sequenza dopo il primo *p++ il che significa che l'incremento prescritto nel primo operando è stato già eseguito quando il secondo accesso a p dovesse avvenire.
* Al termine di un'espressione completa, ovvero un'espressione che non è parte di un'altra espressione. Questa categoria include expression statement (ad esempio l'istruzione di assegnamento a=b;), l'istruzione return, i costrutti di controllo dell'esecuzione tramite la valutazione di una condizione if, switch, while, e do-while, e tutte e tre le espressioni nel blocco di controllo di un ciclo for.
* Prima di eseguire il corpo di una funzione. L'ordine in cui gli argomenti sono valutati è indefinito, ma è garantito che tutti gli argomenti siano stati valutati (e gli effetti collaterali dovuti alla loro valutazione realizzati) prima di iniziare l'esecuzione del corpo della funzione. Ad esempio, nella valutazione dell'espressione f(i++) + g(j++) + h(k++), la funzione f è chiamata con un argomento il cui valore è uguale a i prima dell'incremento, ma la variabile i è stata già aumentata quando l'esecuzione del corpo di f ha inizio. Analogamente, j e k sono incrementate prima di valutare il corpo delle rispettive funzioni g e h. Tuttavia l'ordine in cui f, g e h sono eseguite non è definito, né è definito l'ordine in cui sono eseguiti gli incrementi delle tre variabili i, j, k. Se il corpo di f dovesse accedere alle variabili j and k, potrebbe verificarsi che nessuna, solo una, o entrambe le variabili siano state già incrementate. È importante notare che le virgole che compaiono nella chiamata f(a,b,c) non sono istanze dell'operatore virgola, ma sono token che formano la sintassi della chiamata di funzione, e non introducono punti di sequenza, per cui l'ordine di valutazione degli argomenti non è definito.
* Al termine della valutazione di una chiamata di funzione, dopo che il valore di ritorno è stato copiato nel contesto chiamante. Tale punto di sequenza è specificato esplicitamente solo in C++; è presente solo implicitamente in C.
* Al termine della valutazione di un inizializzatore, ad esempio dopo la valutazione di 5 nella dichiarazione int a = 5;.
* Tra i dichiaratori in una sequenza di dichiarazioni. Ad esempio, vi è un punto di sequenza tra le due consecutive valutazioni di a++ nell'istruzione code>int x = a++, y = a++. È importante sottolineare che, anche in questo caso, la virgola è parte della sintassi della dichiarazione, non è un operatore virgola.
* Dopo ogni conversione associata ad un specificatore di formato di input/output. Ad esempio, nell'espressione printf("foo %n %d", &a, 42) è presente un punto di sequenza dopo la valutazione di %n e prima della stampa di 42.

In C++, gli operatori soggetti ad overloading si comportano come normali funzioni per quanto riguarda l'introduzione di punti di sequenza.

