35 votes

Est-ce que ce comportement C non défini?

Cette question a été posée à notre classe par le prof de programmation C:

On vous donne le code:

 int x=1;
printf("%d",++x,x+1);
 

Quelle sortie produira-t-il toujours?

La plupart des étudiants ont déclaré un comportement indéfini. Quelqu'un peut-il m'aider à comprendre pourquoi il en est ainsi?

Merci pour le montage et les réponses mais je suis toujours confus.

40voto

Jerry Coffin Points 237758

La sortie est susceptible d'être 2 dans tous les cas. En réalité, ce que vous avez est un comportement indéfini si.

Plus précisément, la norme dit:

Entre le précédent et suivant de la séquence de point d'un objet doit avoir sa valeur stockée modifié plus d'une fois par l'évaluation d'une expression. En outre, l'état de la valeur est en lecture seule pour déterminer la valeur à stocker.

Il y a un point de séquence avant d' évaluer les arguments à une fonction, et d'un point de séquence après tous les arguments ont été évalués (mais la fonction n'est pas encore appelé). Entre les deux (c'est à dire, si les arguments sont en cours d'évaluation) il n'y a pas un point de séquence (sauf si un argument est une expression qui inclut un en interne, tels que l'utilisation de l' && || ou , opérateur).

Cela signifie que l'appel à l' printf est la lecture de l'état de la valeur à la fois pour déterminer la valeur à stocker (c'est à dire, l' ++x) et pour déterminer la valeur du deuxième argument (c'est à dire, l' x+1). Ceci est clairement en contradiction avec l'exigence cité ci-dessus, résultant en un comportement indéfini.

Le fait que vous avez fourni un argument supplémentaire pour lesquels aucun indicateur de conversion est donné de ne pas entraîner un comportement indéfini. Si vous donnez moins d' arguments que la conversion des prescripteurs, ou si l' (promu) type de l'argument n'est pas d'accord avec celle de l'indicateur de conversion vous obtenez un comportement indéfini -- mais en passant un paramètre supplémentaire ne fait pas.

14voto

Gilles Points 37537

De tout temps le comportement d'un programme n'est pas défini, tout peut arriver - le classique de la phrase est que "les démons peuvent voler hors de votre nez" - bien que la plupart des implémentations ne pas aller que de loin.

Les arguments d'une fonction sont conceptuellement évalué en parallèle (le terme technique est qu'il n'y a pas de point de séquence entre leur évaluation). Cela signifie que les expressions ++x et x+1 peuvent être évaluées dans cet ordre, dans l'ordre inverse, ou, dans certains entrelacés façon. Lorsque vous modifiez une variable et essayez d'accéder à sa valeur en parallèle, le comportement est indéfini.

Avec de nombreuses implémentations, les arguments sont évalués dans l'ordre (mais pas toujours de la gauche vers la droite). Si vous avez peu de chances de voir quelque chose, mais 2 dans le monde réel.

Cependant, un compilateur peut générer un code comme ceci:

  1. Chargement de x dans le registre r1.
  2. Calculer x+1 par l'ajout de 1 r1.
  3. Calculer ++x par l'ajout de 1 r1. C'est ok, car x a été chargé en r1. Étant donné la façon dont le compilateur a été conçu, à l'étape 2 ne peut pas avoir modifié r1, parce que cela pourrait se faire que si x a été lue à la fois comme écrit entre les deux séquence de points. Ce qui est interdit par la norme.
  4. Stocker r1 en x.

Et sur ce (hypothétique, mais correct) compilateur, le programme d'impression 3.

(EDIT: en passant un argument supplémentaire pour printf est correcte (§7.19.6.1-2 dans N1256; grâce à Prasoon Saurav) pour le rappeler. Aussi: ajout d'un exemple.)

11voto

AndreyT Points 139512

La bonne réponse est: le code produit un comportement indéfini.

La raison pour laquelle le comportement est indéfini, c'est que les deux expressions ++x et x + 1 sont en modifiant x et de la lecture x pour une autre (pour modification) de la raison et de ces deux actions ne sont pas séparés par un point de séquence. Il en résulte un comportement non défini en C (et C++). L'exigence est donné à 6.5/2 de la norme du langage C.

Notez que le comportement non défini dans la présente affaire n'a absolument rien à voir avec le fait qu' printf de la fonction est donné qu'un spécificateur de format et de deux arguments. Pour donner plus d'arguments pour printf qu'il y a des spécificateurs de format dans la chaîne de format est parfaitement légal dans C. de Nouveau, le problème est enraciné dans la violation de l'évaluation de l'expression des exigences de langage C.

Aussi, notez que certains participants de cette discussion ne parviennent pas à saisir le concept de comportement indéfini, et d'insister sur la mélanger avec la notion de comportement quelconque. Afin de mieux illustrer la différence considérons l'exemple simple suivant

int inc_x(int *x) { return ++*x; }
int x_plus_1(int x) { return x + 1; }

int x = 1;
printf("%d", inc_x(&x), x_plus_1(x));

Le code ci-dessus est "équivalent" à celui d'origine, sauf que les opérations qui impliquent x sont enveloppés dans des fonctions. Ce qui va se passer dans ce dernier exemple?

Il n'y a pas un comportement non défini dans le présent code. Mais depuis que l'ordre d'évaluation des printf arguments est indéterminée, ce code génère un comportement non spécifié, c'est à dire qu'il est possible qu' printf sera appelé comme printf("%d", 2, 2) ou printf("%d", 2, 3). Dans les deux cas, la sortie sera en effet 2. Cependant, la différence importante de cette variante est que tous les accès à l' x sont enveloppés dans la séquence des points présents au début et à la fin de chaque fonction, de sorte que cette variante ne permet pas de produire un comportement indéfini.

C'est exactement le raisonnement de certains autres affiches sont d'essayer de forcer sur l'exemple d'origine. Mais il ne peut pas être fait. L'exemple original produit non défini comportement, ce qui est totalement différent de la bête. Ils sont apparemment en train d'insister pour que, dans la pratique, un comportement indéfini est toujours équivalent à un comportement non spécifié. Ceci est totalement faux de prétendre que seuls indiquent le manque d'expertise dans ceux qui la font. Le code original produit un comportement indéfini, période.

Pour poursuivre l'exemple, nous allons modifier l'exemple de code précédent pour

printf("%d %d", inc_x(&x), x_plus_1(x));

la sortie de ce code deviendra généralement imprévisibles. Elle peut imprimer 2 2 ou elle peut imprimer 2 3. A noter toutefois que même si le comportement est imprévisible, il ne produit toujours pas le comportement indéfini. Le comportement est indéterminé, peu pas indéfini. Un comportement non spécifié est limité à deux possibilités: soit 2 2 ou 2 3. Un comportement indéfini n'est pas limité à quoi que ce soit. Il peut format de votre disque dur à la place de quelque chose d'impression. Sentez la différence.

2voto

Vladimir Points 110327

La plupart des étudiants ont déclaré un comportement indéfini. Quelqu'un peut-il m'aider à comprendre pourquoi il en est ainsi?

Parce que l'ordre dans lequel les paramètres de fonction sont calculés n'est pas spécifié.

2voto

Peter G. Points 8566

Ce résultat sera toujours produire ?

Il produira 2 dans tous les environnements, que je pense. L'interprétation stricte de la norme C99 cependant rend le comportement indéfini parce que l'accès à x ne répondent pas aux exigences qui existent entre la séquence de points.

La plupart des étudiants, a déclaré un comportement indéfini. Quelqu'un peut-il m'aider à comprendre pourquoi il est ainsi?

Je vais maintenant répondre à la seconde question que je comprends comme "Pourquoi la plupart des élèves de ma classe disent que le code constitue un comportement indéterminé?" et je pense que aucun autre affiche a répondu jusqu'à présent. Une partie des élèves se souviennent des exemples de valeur indéfinie des expressions comme

f(++i,i)

Le code que vous donnez correspond à ce modèle, mais les étudiants tort de penser que le comportement est défini de toute façon parce que printf ignore le dernier paramètre. Cette nuance confond beaucoup d'étudiants. Une autre partie de l'élève sera ainsi versé dans la norme comme David Thornley et de dire "comportement indéfini" pour les bonnes raisons expliquées ci-dessus.

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