44 votes

Pourquoi le comportement `i = ++ i + 1` n'est-il pas spécifié?

Envisager le C++ suivant la Norme ISO/IEC 14882:2003(E) citation (article 5, paragraphe 4):

Sauf indication contraire, l'ordre de l'évaluation des opérandes de l'individu les opérateurs et les sous-expressions de la personne expressions, et l'ordre dans de quel côté effets, est non spécifié. 53) Entre la précédente et la prochaine séquence, point un scalaire l'objet doit avoir sa valeur stockée modifié plus d'une fois par le l'évaluation d'une expression. En outre, l'état de la valeur doit être accessible uniquement à déterminer la valeur pour être stocké. Les exigences de la présente paragraphe doivent être satisfaites pour chaque admissible de la commande de la les sous-expressions d'une pleine expression; sinon le comportement est indé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

fin de l'exemple]

J'ai été surpris qu' i = ++i + 1 donne une valeur non définie d' i. Quelqu'un sait d'un compilateur de mise en œuvre qui ne donne pas d' 2 pour le cas suivant?

int i = 0;
i = ++i + 1;
std::cout << i << std::endl;

Le truc, c'est qu' operator= a deux arguments. D'abord on est toujours i de référence. L'ordre d'évaluation n'a pas d'importance dans ce cas. Je ne vois pas de problème à l'exception de C++ Standard tabou.

Svp, ne pas considérer de tels cas où l'ordre des arguments est important de l'évaluation. Par exemple, ++i + i est évidemment pas défini. S'il vous plaît, tenez compte uniquement de mon cas i = ++i + 1.

Pourquoi le C++ Standard interdire de telles expressions?

62voto

Rob Kennedy Points 107381

Vous faites l'erreur de la pensée de l' operator= comme un argument de la fonction, d'où les effets secondaires des arguments doit être complètement évalués avant la fonction commence. Si c'était le cas, alors l'expression i = ++i + 1 aurait plusieurs séquence de points, et ++i serait totalement évalués avant l'affectation a commencé. Ce n'est pas le cas, cependant. Ce qui est évalué dans les intrinsèque de l' opérateur d'affectation, pas un opérateur défini par l'utilisateur. Il n'y a qu'un point de séquence dans l'expression.

Le résultat de l' ++i est évaluée avant la cession (et avant l'ajout de l'opérateur), mais l' effet secondaire n'est pas nécessairement appliqués immédiatement. Le résultat de l' ++i + 1 est toujours la même que i + 2, de sorte que la valeur qui sera affectée à l' i dans le cadre de la cession de l'opérateur. Le résultat de l' ++i toujours i + 1, de sorte que ce qui est attribué à l' i dans le cadre de l'opérateur d'incrémentation. Il n'y a pas de séquence de point à contrôler valeur doit obtenir préalablement affecté.

Depuis que le code est en train de violer la règle que "entre le précédent et suivant de la séquence de point un scalaire objet doit avoir sa valeur stockée modifié plus d'une fois par l'évaluation d'une expression," le comportement est indéfini. Pratiquement, cependant, il est probable que ce soit i + 1 ou i + 2 sera affecté en premier, puis l'autre valeur sera affectée, et enfin, le programme continuera à fonctionner comme d'habitude - pas de nasale démons ou exploser des toilettes, et pas i + 3, soit.

37voto

Charles Bailey Points 244082

C'est un comportement indéterminé, et non pas (seulement) non spécifié comportement parce qu'il y a deux écritures en i sans l'intermédiaire d'un point de séquence. C'est de cette manière que, par définition, aussi loin que la norme précise.

La norme permet de compilateurs pour générer du code qui les retards écrit à l'entreposage ou à partir d'un autre point de vue, à rétablissez la séquence des instructions de mise en œuvre de effets secondaires - de toute façon, il choisit aussi longtemps qu'il respecte les exigences de la séquence de points.

Le problème avec cette déclaration, l'expression est qu'elle implique deux écritures en i sans l'intermédiaire d'un point de séquence:

i = i++ + 1;

Une écriture est la valeur de la valeur d'origine de l' i "de plus, l'un et l'autre, pour que la valeur de "plus d'un" nouveau. Ces écritures qui pourrait arriver dans n'importe quel ordre ou souffler complètement aussi loin que la norme permet. Théoriquement, cela donne même des implémentations de la liberté d'effectuer des écritures différées en parallèle, sans se soucier de vérifier les accès simultanés des erreurs.

15voto

Charles Salvia Points 28661

C / C ++ définit un concept appelé points de séquence , qui fait référence à un point d'exécution où il est garanti que tous les effets des évaluations précédentes auront déjà été exécutés. Dire i = ++i + 1 n'est pas défini car il incrémente i et attribue également i à lui-même, aucun des deux n'étant un point de séquence défini. Par conséquent, il n'est pas précisé ce qui se passera en premier.

9voto

DigitalRoss Points 80400

Étant donné deux choix: défini ou indéfini, quel choix avez-vous fait?

Les auteurs de la norme avais deux choix: définir le comportement ou le spécifier comme indéfini.

Compte tenu de l'clairement pas sage de la nature de la rédaction d'un tel code, en premier lieu, il ne ferait aucun sens de spécifier un résulter pour elle. On voudrait décourager code comme ça et de ne pas l'encourager. Il n'est pas nécessaire ou utile pour quoi que ce soit.

En outre, des comités de normes n'ont aucun moyen de forcer les rédacteurs du compilateur de faire quoi que ce soit. S'ils avaient tenu un comportement spécifique, il est probable que l'exigence d'avoir été ignoré.

Il y a des raisons pratiques, mais je soupçonne qu'ils étaient subordonnés au-dessus de la considération générale. Mais pour l'enregistrement, toute sorte de comportement requis pour ce genre d'expression et liées sortes limitera la capacité du compilateur de générer le code pour le facteur de communes des sous-expressions, de déplacer des objets entre les registres et la mémoire, etc. C était déjà handicapé par la faiblesse des restrictions de visibilité. Des langages comme Fortran il y a longtemps réalisé que alias paramètres et variables globales ont été une optimisation de tueur et je crois qu'ils ont tout simplement interdit.

Je sais que vous étiez intéressé par une expression spécifique, mais la nature exacte de construire n'a pas beaucoup d'importance. Il ne va pas être facile de prédire ce qu'un complexe générateur de code va faire, et le langage tente de ne pas exiger de ces prédictions en ridicule les cas.

8voto

Trent Points 5924

La partie importante de la norme est:

sa valeur stockée modifiée au plus une fois par l'évaluation d'une expression

Vous modifiez la valeur deux fois, une fois avec l'opérateur ++, une fois avec l'affectation

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X