Qual è il risultato di + = in C e C ++?


93

Ho il seguente codice:

#include <stdio.h>
int main(int argc, char **argv) {
    int i = 0;
    (i+=10)+=10;
    printf("i = %d\n", i);
    return 0;
}

Se provo a compilarlo come sorgente C usando gcc ottengo un errore:

error: lvalue required as left operand of assignment

Ma se lo compilo come sorgente C ++ usando g ++ non ottengo alcun errore e quando eseguo l'eseguibile:

i = 20

Perché il comportamento diverso?


85
Lingua diversa, regole di sintassi diverse ?. Personalmente, rifiuterei quel codice nella revisione del codice.
Max

7
Evita codici come questo imo ... Non chiaro per tutti.
allaire

1
Indubbiamente, il codice non è pulito e dovrebbe essere evitato nello sviluppo "reale". Tuttavia, osservo lo stesso comportamento e vorrei conoscerne le ragioni.
ulidtko

9
Questo NON è un estratto di codice da un vero software. Questo è solo un nodo in cui sono incappato accidentalmente.
Svetlin Mladenov

3
@JohnDibling Penso che il voto positivo sia specificamente (i + = 10) + = 10 Non so in quale linguaggio sia codice legittimo e il fatto che lui dica che C ++ compila effettivamente mi intriga.
Tony318

Risposte:


133

La semantica degli operatori di assegnazione composta è diversa in C e C ++:

Standard C99, 6.5.16, parte 3:

Un operatore di assegnazione memorizza un valore nell'oggetto designato dall'operando sinistro. Un'espressione di assegnazione ha il valore dell'operando sinistro dopo l'assegnazione, ma non è un lvalue.

In C ++ 5.17.1:

L'operatore di assegnazione (=) e gli operatori di assegnazione composti si raggruppano tutti da destra a sinistra. Tutti richiedono un valore modificabile come operando sinistro e restituiscono un valore l con il tipo e il valore dell'operando sinistro dopo che l'assegnazione è avvenuta.

EDIT: il comportamento di (i+=10)+=10in C ++ non è definito in C ++ 98, ma ben definito in C ++ 11. Vedere questa risposta alla domanda di NPE per le parti pertinenti degli standard.


Corretta. Uno restituisce il valore del risultato e uno restituisce la variabile (indirizzo)
texasbruce

7
Importante : nota che (i+=10)+=10è un comportamento indefinito in C ++, vedi risposta @aix.
David Rodríguez - scrive

@ DavidRodríguez-dribeas Volevi dire non specificato , non indefinito , giusto?
dasblinkenlight

4
@dasblinkenlight: No, intendeva indefinito . In C ++ 03 e versioni precedenti, la modifica del risultato lvalue di un'espressione si comporta in modo imprevedibile in tutti i compilatori a causa della mancanza di un punto di sequenza intermedio . Se non fosse specificato , si comporterebbe in modo prevedibile ma diverso su compilatori diversi .
Justin ᚅᚔᚈᚄᚒᚔ

2
Sarebbe stato utile in un'impostazione come int f(int &y); f(x += 10);- passare un riferimento alla variabile modificata in una funzione.
Phil Miller

51

Oltre ad essere un codice C non valido, la riga

(i+=10)+=10;

comporterebbe un comportamento indefinito sia in C che in C ++ 03 poiché modificherebbe idue volte tra i punti della sequenza.

Quanto al motivo per cui è consentito compilare in C ++:

[C ++ N3242 5.17.1] L'operatore di assegnazione (=) e gli operatori di assegnazione composti si raggruppano tutti da destra a sinistra. Tutti richiedono un lvalue modificabile come operando sinistro e restituiscono un lvalue che fa riferimento all'operando sinistro.

Lo stesso paragrafo continua a dirlo

In tutti i casi, l'assegnazione viene sequenziata dopo il calcolo del valore degli operandi destro e sinistro e prima del calcolo del valore dell'espressione di assegnazione.

Ciò suggerisce che in C ++ 11 l'espressione non ha più un comportamento indefinito.


3
Beh, è ​​certamente UB a causa dei punti di sequenza. È anche codice non valido in C (ma non C ++) ma non è correlato ai punti di sequenza e dovrebbe essere rilevato dal compilatore.
Konrad Rudolph

2
@KonradRudolph: no, il compilatore non è obbligato a cogliere un comportamento indefinito, a differenza del codice mal formato. La parte "dovrebbe essere catturata dal compilatore" è dove non siamo d'accordo.

2
I punti di sequenza non esistono in C ++ 11, quindi la vera ragione per l'UB è che ci sono due modifiche iche non sono sequenziali.
Mankarse

4
È falso che questo sia un comportamento indefinito. Se l'assegnazione non fosse sequenza prima del calcolo del valore dell'espressione di assegnazione i = j+=1, il risultato sarebbe un valore indeterminato. Dallo stesso paragrafo si cita "In tutti i casi, l'assegnazione è sequenziata dopo il calcolo del valore degli operandi destro e sinistro e prima del calcolo del valore dell'espressione di assegnazione". Quindi (i+=10)+=10è ben definito da fare i += 10; i += 10;. D'altra parte (i+=10)+=(i+=10)è UB.
bames53

2
Dato che ho visto che c'era qualche disaccordo su questo tra i commentatori, l'ho pubblicata come domanda separata: stackoverflow.com/questions/10655290/…
NPE
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.