Quel est le résultat de + = en C et C ++?

93

J'ai le code suivant:

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

Si j'essaie de le compiler en tant que source C en utilisant gcc, j'obtiens une erreur:

error: lvalue required as left operand of assignment

Mais si je le compile en tant que source C ++ en utilisant g ++, je n'obtiens aucune erreur et lorsque j'exécute l'exécutable:

i = 20

Pourquoi ce comportement différent?

Svetlin Mladenov
la source
85
Langage différent, règles de syntaxe différentes ?. Personnellement, je rejetterais ce code lors de la révision du code.
Max
7
Évitez le code comme celui-ci imo ... Pas clair pour tout le monde.
allaire
1
Sans aucun doute, le code n'est pas propre et devrait être évité dans le développement "réel". Mais néanmoins, j'observe le même comportement et j'aimerais en connaître les raisons.
ulidtko
9
Ce n'est PAS un extrait de code d'un vrai logiciel. C'est juste une anomalie sur laquelle je suis tombé accidentellement.
Svetlin Mladenov
3
@JohnDibling Je pense que le vote positif est spécifiquement le (i + = 10) + = 10 Je ne sais pas dans quel langage c'est du code légitime et le fait qu'il dit que C ++ le compile en fait m'intrigue.
Tony318

Réponses:

133

La sémantique des opérateurs d'affectation composée est différente en C et C ++:

Norme C99, 6.5.16, partie 3:

Un opérateur d'affectation stocke une valeur dans l'objet désigné par l'opérande de gauche. Une expression d'affectation a la valeur de l'opérande de gauche après l'affectation, mais n'est pas une lvalue.

Dans C ++ 5.17.1:

L'opérateur d'affectation (=) et les opérateurs d'affectation composés sont tous regroupés de droite à gauche. Tous nécessitent une lvalue modifiable comme opérande de gauche et renvoient une lvalue avec le type et la valeur de l'opérande de gauche après que l'affectation a eu lieu.

EDIT: Le comportement de (i+=10)+=10en C ++ n'est pas défini en C ++ 98, mais bien défini en C ++ 11. Voir cette réponse à la question de NPE pour les parties pertinentes des normes.

dasblinkenlight
la source
Correct. On renvoie la valeur du résultat, et on renvoie la variable (adresse)
texasbruce
7
Important : notez que le (i+=10)+=10comportement n'est pas défini en C ++, voir la réponse @aix.
David Rodríguez - dribeas
@ DavidRodríguez-dribeas Vous vouliez dire non spécifié , pas indéfini , non?
dasblinkenlight
4
@dasblinkenlight: Non, il voulait dire indéfini . Dans C ++ 03 et versions antérieures, la modification du résultat lvalue d'une expression se comporte de manière imprévisible dans tous les compilateurs en raison de l'absence de point de séquence intermédiaire. S'il n'était pas spécifié , il se comporterait de manière prévisible mais différemment sur différents compilateurs .
Justin ᚅᚔᚈᚄᚒᚔ
2
Cela aurait été utile dans un cadre comme int f(int &y); f(x += 10);- passer une référence à la variable modifiée dans une fonction.
Phil Miller
51

En plus d'être un code C invalide, la ligne

(i+=10)+=10;

entraînerait un comportement non défini en C et C ++ 03 car il modifierait ideux fois entre les points de séquence.

Quant à savoir pourquoi il est permis de compiler en C ++:

[C ++ N3242 5.17.1] L'opérateur d'affectation (=) et les opérateurs d'affectation composés sont tous regroupés de droite à gauche. Tous nécessitent une lvalue modifiable comme opérande gauche et renvoient une lvalue faisant référence à l'opérande gauche.

Le même paragraphe continue en disant que

Dans tous les cas, l'affectation est séquencée après le calcul de la valeur des opérandes droit et gauche et avant le calcul de la valeur de l'expression d'affectation.

Cela suggère qu'en C ++ 11, l'expression n'a plus de comportement indéfini.

NPE
la source
3
Eh bien, c'est certainement UB à cause des points de séquence. C'est également du code invalide en C (mais pas en C ++) mais qui n'est pas lié aux points de séquence et devrait être intercepté par le compilateur.
Konrad Rudolph
2
@KonradRudolph: non, le compilateur n'est pas obligé de détecter un comportement indéfini, par opposition à un code mal formé. La partie "devrait être interceptée par le compilateur" est là où nous ne sommes pas d'accord.
2
Les points de séquence n'existent pas dans C ++ 11, donc la véritable raison de l'UB est qu'il y a deux modifications iqui ne sont pas séquencées.
Mankarse
4
Il est faux que ce comportement ne soit pas défini. Si l'affectation n'était pas une séquence avant le calcul de la valeur de l'expression d'affectation, il en i = j+=1résulterait une valeur indéterminée. Dans le même paragraphe, vous citez "Dans tous les cas, l'affectation est séquencée après le calcul de la valeur des opérandes droit et gauche et avant le calcul de la valeur de l'expression d'affectation." Par conséquent, il (i+=10)+=10est bien défini de faire i += 10; i += 10;. D'autre part, (i+=10)+=(i+=10)UB.
bames53
2
Depuis que j'ai vu qu'il y avait un désaccord à ce sujet entre les commentateurs, j'ai posté ceci comme une question distincte: stackoverflow.com/questions/10655290/…
NPE