Punti di sequenza e ordine parziale

Qualche giorno prima c’era una discussione sul fatto se l’espressione

i = ++ i + 1

invoca UB (comportamento non definito) o no.

Alla fine si è concluso che invoca UB poiché il valore di ‘i’ sta cambiando più di una volta tra due punti di sequenza.

Sono stato coinvolto in una discussione con Johannes Schaub nella stessa discussione. Secondo lui

i = (i, i ++, i) +1 —— (1) / * invoca anche UB * /

Ho detto (1) non invocare UB perché gli effetti collaterali delle sottoespressioni precedenti vengono cancellati dall’operatore virgola ‘,’ tra i e i ++ e tra i ++ e i.

Quindi ha dato la seguente spiegazione:

“Sì, il punto della sequenza dopo che i ++ ha completato tutti gli effetti collaterali prima di esso, ma non c’è nulla che impedisca l’effetto collaterale del compito che si sovrappone all’effetto collaterale di i ++. Il problema di fondo è che l’effetto collaterale di un compito non viene specificato dopo o prima della valutazione di entrambi gli operandi dell’assegnazione, e quindi i punti di sequenza non possono fare nulla per quanto riguarda la protezione di questo: i punti di sequenza inducono un ordine parziale: solo perché c’è un punto di sequenza dopo e prima che i ++ non significhi che tutti gli effetti collaterali sono sequenziati riguardo a i .

Inoltre, si noti che solo un punto di sequenza non significa nulla: l’ordine delle valutazioni non è dettato dalla forma del codice. È dettato da regole semantiche. In questo caso, non vi è alcuna regola semantica che dice quando l’effetto collaterale di assegnazione avviene con riguardo alla valutazione di entrambi i suoi operandi o sottoespressioni di quegli operandi “.

La dichiarazione scritta in “grassetto” mi ha confuso. Per quanto ne so:

“In determinati punti specificati nella sequenza di esecuzione denominati punti di sequenza, tutti gli effetti collaterali delle valutazioni precedenti devono essere completi e non devono aver luogo effetti collaterali delle valutazioni successive.”

Poiché, gli operatori virgola specificano anche l’ordine di esecuzione, l’effetto collaterale di i ++ è stato cancellato quando raggiungiamo l’ultimo i.He (Johannes) avrebbe avuto ragione se l’ordine di valutazione non fosse stato specificato (ma in caso di operatore virgola è ben specificato ).

Quindi voglio solo sapere se (1) invoca UB o no ?. Qualcuno può dare un’altra spiegazione valida?

Grazie!

Lo standard C dice questo sugli operatori di assegnazione (C90 6.3.16 o C99 6.5.16 Operatori di assegnazione):

L’effetto collaterale dell’aggiornamento del valore memorizzato dell’operando di sinistra deve avvenire tra il precedente e il successivo punto di sequenza.

Mi sembra che nella dichiarazione:

i=(i,i++,i)+1; 

il punto di sequenza ‘precedente’ all’operatore di assegnazione sarebbe il secondo operatore di virgola e il punto di sequenza ‘successivo’ sarebbe la fine dell’espressione. Quindi direi che l’espressione non richiama un comportamento indefinito.

Tuttavia, questa espressione:

 *(some_ptr + i) = (i,i++,i)+1; 

avrebbe un comportamento indefinito perché l’ordine di valutazione dei 2 operandi dell’operatore di assegnazione non è definito, e in questo caso invece del problema riscontrato quando si verifica l’effetto collaterale dell’operatore di assegnazione, il problema è che non si sa se il valore di Io ho usato l’handle dell’handle di sinistra che sarà valutato prima o dopo il lato destro. Questo ordine di problema di valutazione non si verifica nel primo esempio perché in quell’espressione il valore di i non è effettivamente utilizzato nel lato sinistro – tutto ciò a cui l’operatore di assegnazione è interessato è il “valore di lvalue” di i .

Ma penso anche che tutto ciò sia abbastanza schematico (e che la mia comprensione delle sfumature coinvolte sia abbastanza schematica) che non sarei sorpreso se qualcuno potesse convincermi del contrario (su entrambi i punti).

Credo che la seguente espressione abbia decisamente un comportamento indefinito.

 i + ((i, i++, i) + 1) 

Il motivo è che l’operatore virgola specifica punti di sequenza tra le sottoespressioni tra parentesi ma non specifica dove in quella sequenza si verifica la valutazione dell’operando di sinistra di + . Una possibilità è tra i punti di sequenza che circondano l’ i++ e questo viola il 5/4 mentre viene scritto tra due punti di sequenza ma viene anche letto due volte tra gli stessi punti di sequenza e non solo per determinare il valore da memorizzare ma anche per determinare il valore del primo operando all’operatore + .

Anche questo ha un comportamento indefinito.

 i += (i, i++, i) + 1; 

Ora, non sono così sicuro di questa affermazione.

 i = (i, i++, i) + 1; 

Anche se si applicano gli stessi principi, devo essere “valutato” come un lvalue modificabile e può essere fatto in qualsiasi momento, ma non sono convinto che il suo valore sia mai letto come parte di questo. (O c’è un’altra restrizione che l’espressione viola per causare UB?)

La sottoespressione (i, i++, i) avviene come parte della determinazione del valore da memorizzare e quella sottoespressione contiene un punto di sequenza dopo la memorizzazione di un valore in i . Non vedo in alcun modo che ciò non richieda che l’effetto collaterale di i++ sia completo prima della determinazione del valore da memorizzare e, quindi, il primo punto ansible che l’effetto collaterale potrebbe verificarsi.

Dopo questo punto di sequnce, il valore di i viene letto al massimo una sola volta e solo per determinare il valore che verrà memorizzato su i , quindi questa ultima parte va bene.

i=(i,i++,i)+1 ------ (1) /* invokes UB as well */

Non invoca un comportamento indefinito. L’effetto collaterale di i++ avrà luogo prima della valutazione del punto successivo della sequenza, che è indicato dalla virgola che lo segue e anche prima dell’assegnazione.

Un buon linguaggio per il sudoku, comunque. 🙂

modifica: qui c’è una spiegazione più elaborata.

All’inizio ero confuso riguardo alla dichiarazione di Johannes, ma lo ha menzionato in:

 i = (i, ++i, i) +1 


Se è assegnamento ed è un incremento. :s: è un punto di sequenza, quindi gli effetti collaterali possono essere sequenziati come segue tra i punti di sequenza: (i :s: i++< a > :s: i) + 1 . Il valore dello scalare i stato cambiato due volte tra il primo e il secondo punto di sequenza qui. L’ordine in cui avviene l’assegnazione e l’incremento non è specificato e poiché tra di essi non vi è alcun punto di sequenza, non è nemmeno atomico l’uno rispetto all’altro. Si tratta di un ordine consentito consentito dall’ordinamento non specificato di questi effetti collaterali.

Questo è diverso da (i++, i++) , perché l’ordine di valutazione delle due sottoespressioni va da sinistra a destra, e nel punto di sequenza tra loro, l’incremento della valutazione precedente deve essere completo e l’incremento successivo non deve ancora essere completato avuto luogo Ciò impone che non vi sia alcun cambiamento del valore di i tra due punti di sequenza, il che rende (i++, i++) valido

Questo mi ha fatto pensare che la sequenza menzionata da litb non sia valida perché, come per C99:

6.5.16.1 (2) Nell’assegnazione semplice (=), il valore dell’operando di destra viene convertito nel tipo dell’espressione di assegnazione e sostituisce il valore memorizzato nell’object designato dall’operando di sinistra.

cioè il valore dell’operando di destra deve essere noto prima dell’effetto collaterale di assegnazione (modifica del valore memorizzato nell’object corrispondente all’operando di sinistra)

6.5.17 (2) L’operando di sinistra di un operatore virgola viene valutato come espressione di vuoto; c’è un punto di sequenza dopo la sua valutazione. Quindi viene valutato l’operando destro; il risultato ha il suo tipo e valore.

vale a dire che l’operando più a destra dell’operazione virgola deve essere valutato per conoscere il valore e il tipo di espressione della virgola (e il valore dell’operando di destra per il mio esempio).

Quindi, in questo caso, il “punto di sequenza precedente” per l’effetto collaterale di assegnazione sarebbe, in effetti, l’operazione con la virgola più a destra. La ansible sequenza menzionata da Johannes non è valida.

Perfavore, correggimi se sbaglio.