Question

Quelqu'un peut-il m'expliquer pourquoi ce code affiche 14? Un autre étudiant vient de me demander et je ne pouvais pas le comprendre.

int i = 5;
i = ++i + ++i;
cout<<i;
Était-ce utile?

La solution

L'ordre des effets secondaires n'est pas défini en C ++. De plus, modifier une variable deux fois dans une seule expression n'a pas de comportement défini (voir le C ++ standard , & # 167; 5.0.4, page physique 87 / page logique 73).

Solution: N'utilisez pas d'effets secondaires dans des expressions complexes, n'utilisez pas plus d'un dans des expressions simples. Et il n’est pas inutile d’activer tous les avertissements que le compilateur peut vous donner: Ajouter -Wall (gcc) ou /Wall /W4 (Visual C ++) à la ligne de commande produit un avertissement d’ajustement:

test-so-side-effects.c: In function 'main':
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
test-so-side-effects.c:5: warning: operation on 'i' may be undefined

Évidemment, le code est compilé pour:

i = i + 1;
i = i + 1;
i = i + i;

Autres conseils

C'est un comportement indéfini, le résultat variera en fonction du compilateur que vous utilisez. Voir, par exemple, FAQ C ++ Lite .

Dans certaines des réponses / commentaires, il a été question de la signification de "comportement indéfini" et de la question de savoir si cela rend le programme invalide. Je publie donc cette réponse plutôt longue détaillant exactement ce que dit la norme avec quelques notes. J'espère que ce n'est pas trop ennuyeux ...

Les bits cités de la norme proviennent de la norme C ++ actuelle (ISO / IEC 14882: 2003). Il existe une formulation similaire dans la norme C.

Selon la norme C ++, modifier une valeur plusieurs fois dans un ensemble de points de séquence entraîne un comportement indéfini (section 5, paragraphe 4):

  

Sauf indication contraire, l'ordre de   évaluation d'opérandes d'individu   opérateurs et sous-expressions de   expressions individuelles, et l'ordre   dans lequel les effets secondaires ont lieu, est   non spécifié.53) Entre le précédent   et la prochaine séquence pointe un scalaire   l'objet doit avoir sa valeur stockée   modifié au plus une fois par le   évaluation d'une expression.   En outre, la valeur antérieure doit être   accédé uniquement pour déterminer la valeur   à stocker. Les exigences de cette   paragraphe doit être rempli pour chaque   commande autorisée du   sous-expressions d'une expression complète;   sinon le comportement n'est pas défini.   [Exemple:

i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
     

& # 8212; exemple de fin]

Notez que le deuxième exemple, & "; i = 7, i++, i++; &"; est défini car l'opérateur de virgule est un point de séquence.

Voici ce que la norme C ++ dit "comportement indéfini" signifie:

  

1.3.12 comportement indéfini [defns.undefined]

     

comportement, tel qu’il pourrait se produire lors de l’utilisation d’un programme de construction ou de données erronés, pour lequel   Norme internationale n'impose aucune exigence. Un comportement indéfini peut également être attendu lorsque cela   Norme internationale omet la description de toute définition explicite du comportement. [Remarque: admissible non défini   comportement va de l’ignorance complète de la situation avec des résultats imprévisibles au comportement   traduction ou exécution du programme de manière documentée, caractéristique de l’environnement (avec ou sans   l’émission d’un message de diagnostic), à mettre fin à la traduction ou à l’exécution (avec l’émission d’une   message de diagnostic). Beaucoup de constructions de programme erronées n'engendrent pas un comportement indéfini; ils doivent être diagnostiqués. ]

En d'autres termes, le compilateur est libre de faire ce qu'il veut, y compris

  1. crachant un message d'erreur,
  2. faisant quelque chose d'implémentation définie & amp; documenté,
  3. ayant des résultats complètement imprévisibles

Le deuxième élément concerne les extensions de langage dont disposent la plupart des compilateurs, mais qui ne sont bien sûr pas définies dans la norme.

Donc, je suppose que, à proprement parler, quelque chose qui présente un comportement indéfini n'est pas "illégal", mais d'après mon expérience, chaque fois qu'un programme C / C ++ présente quelque chose qui présente un "comportement indéfini" (sauf si c'est une extension) - c'est un bug . Je pense que qualifier une telle construction d’illégale n’est pas déroutant, trompeur ou erroné.

De plus, je pense qu'essayer d'expliquer ce que fait le compilateur pour atteindre la valeur 14 n'est pas particulièrement utile, car cela passe à côté de la question. Le compilateur pourrait faire presque n'importe quoi; en fait, il est probable que le compilateur obtienne un résultat différent lorsqu'il est exécuté à l'aide d'options d'optimisation différentes (ou peut générer un code qui se bloque - qui sait?).

Pour ceux qui veulent des références supplémentaires ou un appel à l'autorité, voici quelques indications:

La réponse longue et longue de Steve Summit (responsable de la comp.lang.c à la foire aux questions) de 1995 à ce sujet:

Voici ce que Bjarne Stroustrup a à dire à ce sujet:

Footnote : la norme C ++ utilise le mot "illégal" une seule fois - pour décrire une différence entre C ++ et le standard C en ce qui concerne l'utilisation de static ou extern avec des déclarations de type.

Simple ... votre compilateur évalue les incréments BOTH avant d'effectuer la somme, sans mettre en cache les résultats intermédiaires. Cela signifie que lorsque vous ajoutez deux fois i, il a maintenant la valeur 7.

Si vous le faites

int j=++i; 
int k=++i;

i = j+k;

vous verrez 13 comme prévu.

Sur votre compilateur particulier, il choisit de commencer par les deux opérations ++, puis par l’ajout. Il interprète le code comme suit:

int i = 5;
++i;
++i;
i = i + i;
cout << i;

C'est parfaitement valide.

Une meilleure question serait, est-ce que ça va toujours être 14?

int i = 5;
i = ++i + ++i;
cout<<i;

i = ++i   + ++i   ;
i = ++(5) + ++(5) ;
i =    6  +    6  ;
i = 12;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++i   +   (6) ;
i = ++(6) +    6  ;
i =   (7) +    6  ;
i = 13;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++(6) +   (6) ;
i =   (7) +   (7) ;
i = 14;

Selon toute vraisemblance, ce sera probablement <=>, car cela donne un peu plus de sens.

Je pense qu'en regardant le problème du point de vue de l'arbre de syntaxe, la réponse au problème devient plus claire:

i
|
=
|
+
|
expression unaire - expression unaire

expression unaire: expression opérateur unaire

Dans notre cas, l'expression se résume à la variable i.

Maintenant, il se produit que les deux expressions unaires modifient le même opérande. Le code effectue donc deux fois ++ i lors de l'évaluation des expressions unaires avant d'ajouter les résultats des deux expressions unaires.

Donc, ce que fait le code est en effet

++ i;
++ i;
i = i + i;

Pour i = 5 cela signifie que

i = i + 1; // i < - 6
i = i + 1; // i < - 7
i = i + i; // i < - 14

Parce que l'incrément de préfixe a la priorité:

int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14
 i = i++ + i; //11  

 i = i++ + i++; //12

 i = i++ + ++i; //13

 i = ++i + i++; //13

 i = ++i + ++i; //14    
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top