1030 votes

Comportement indéfini et points de séquence

Que sont les "points de séquence" ?

Quelle est la relation entre le comportement indéfini et les points de séquence ?

J'utilise souvent des expressions amusantes et alambiquées comme a[++i] = i; pour me sentir mieux. Pourquoi devrais-je arrêter de les utiliser ?

Si vous avez lu cet article, n'oubliez pas de consulter la question de suivi. Comportement indéfini et points de séquence rechargés .

<i>(Note : Ceci est censé être une entrée à <a href="https://stackoverflow.com/questions/tagged/c++-faq">FAQ C++ de Stack Overflow </a>. Si vous voulez critiquer l'idée de fournir une FAQ dans ce formulaire, alors <a href="https://meta.stackexchange.com/questions/68647/setting-up-a-faq-for-the-c-tag">le post sur meta qui a commencé tout ça </a>serait l'endroit idéal pour le faire. Les réponses à cette question sont suivies dans le <a href="https://chat.stackoverflow.com/rooms/10/c-lounge">Chatroom C++ </a>où l'idée de la FAQ est née en premier lieu, donc votre réponse a de grandes chances d'être lue par ceux qui ont eu l'idée).</i>

709voto

Prasoon Saurav Points 47488

C++98 et C++03

Cette réponse concerne les anciennes versions de la norme C++. Les versions C++11 et C++14 de la norme ne contiennent pas formellement de "points de séquence" ; les opérations sont "séquencées avant", "non séquencées" ou "séquencées de manière indéterminée". L'effet net est essentiellement le même, mais la terminologie est différente.


Avis de non-responsabilité : Ok. Cette réponse est un peu longue. Soyez donc patient en la lisant. Si vous connaissez déjà ces choses, les relire ne vous rendra pas fou.

Pré-requis : Une connaissance élémentaire de Norme C++


Que sont les points de séquence ?

La norme dit

À certains points précis de la séquence d'exécution appelés points de séquence tous effets secondaires des évaluations précédentes doivent être complètes et aucun effets secondaires des évaluations ultérieures doivent avoir eu lieu. (§1.9/7)

Effets secondaires ? Que sont les effets secondaires ?

L'évaluation d'une expression produit quelque chose et si, en plus, il y a un changement dans l'état de l'environnement d'exécution, on dit que l'expression (son évaluation) a un ou plusieurs effets secondaires.

Par exemple :

int x = y++; //where y is also an int

En plus de l'opération d'initialisation la valeur de y est modifié en raison de l'effet secondaire de ++ opérateur.

Jusqu'à présent, tout va bien. Passons aux points de séquence. Une définition d'alternance de seq-points donnée par l'auteur comp.lang.c Steve Summit :

Le point de séquence est un moment où la poussière est retombée et où tous les effets secondaires observés jusqu'à présent sont garantis.


Quels sont les points de séquence communs énumérés dans la norme C++ ?

Ce sont :

  • à la fin de l'évaluation de l'expression complète ( §1.9/16 ) (Une expression complète est une expression qui n'est pas une sous-expression d'une autre expression). 1

    Exemple :

    int a = 5; // ; is a sequence point here
  • dans l'évaluation de chacune des expressions suivantes après l'évaluation de la première expression ( §1.9/18 ) 2

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) (ici, a , b est un opérateur de virgule ; en func(a,a++) , n'est pas un opérateur de virgule, c'est simplement un séparateur entre les arguments a y a++ . Ainsi, le comportement est indéfini dans ce cas (si a est considéré comme un type primitif))
  • lors d'un appel de fonction (que la fonction soit en ligne ou non), après l'évaluation de tous les arguments de la fonction (le cas échéant) qui a lieu avant l'exécution de toute expression ou instruction dans le corps de la fonction ( §1.9/17 ).

1 : Note : l'évaluation d'une expression complète peut inclure l'évaluation de sous-expressions qui ne font pas partie lexicalement de l'expression complète. pas lexicalement partie de l'expression complète. Par exemple, les sous-expressions impliquées dans l'évaluation des expressions d'arguments par défaut (8.3.6) sont considérées comme étant créées dans l'expression qui appelle la fonction, et non dans l'expression qui définit l'argument par défaut.

2 : Les opérateurs indiqués sont les opérateurs intégrés, tels que décrits dans la clause 5. Lorsque l'un de ces opérateurs est surchargé (clause 13) dans un contexte valide, désignant ainsi une fonction opérateur définie par l'utilisateur, l'expression désigne une invocation de fonction et les opérandes forment une liste d'arguments, sans point de séquence implicite entre eux.


Qu'est-ce qu'un comportement indéfini ?

La norme définit le comportement indéfini dans la section §1.3.12 comme

le comportement, tel qu'il pourrait résulter de l'utilisation d'une construction de programme erronée ou de données erronées, pour lequel la présente Norme Internationale impose aucune exigence 3 .

On peut également s'attendre à un comportement non défini lorsque cette Norme internationale omet la description de toute définition explicite du comportement.

3 : le comportement indéfini autorisé va de l'ignorance totale de la situation avec des résultats imprévisibles, au comportement pendant la traduction ou l'exécution du programme d'une manière documentée et caractéristique de l'environnement (avec ou sans émission de code). avec ou sans l'émission d'un message de diagnostic), à l'interruption d'une traduction ou d'une exécution (avec l'émission d'un message de diagnostic).

En bref, un comportement indéfini signifie tout ce qui est peuvent arriver, des démons qui sortent de ton nez à ta copine qui tombe enceinte.


Quelle est la relation entre le comportement indéfini et les points de séquence ?

Avant d'entrer dans le vif du sujet, vous devez connaître la ou les différences entre Comportement indéfini, comportement non spécifié et comportement défini par l'implémentation .

Vous devez également savoir que the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified .

Par exemple :

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

Un autre exemple aquí .


Maintenant, la norme en §5/4 dit

  • 1) Entre le point de séquence précédent et le suivant, la valeur stockée d'un objet scalaire doit être modifiée au maximum une fois par l'évaluation d'une expression.

Qu'est-ce que cela signifie ?

De manière informelle, cela signifie qu'entre deux points de séquence, une variable ne doit pas être modifiée plus d'une fois. Dans une instruction d'expression, le next sequence point se trouve généralement à la fin du point-virgule, et l'élément previous sequence point est à la fin de la déclaration précédente. Une expression peut également contenir des éléments intermédiaires sequence points .

D'après la phrase ci-dessus, les expressions suivantes invoquent un comportement indéfini :

i++ * ++i;   // UB, i is modified more than once btw two SPs
i = ++i;     // UB, same as above
++i = 2;     // UB, same as above
i = ++i + 1; // UB, same as above
++++++i;     // UB, parsed as (++(++(++i)))

i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)

Mais les expressions suivantes sont correctes :

i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i);   // well defined 
int j = i;
j = (++i, i++, j*i); // well defined

  • 2) En outre, on ne peut accéder à la valeur antérieure que pour déterminer la valeur à stocker.

Qu'est-ce que cela signifie ? Cela signifie que si un objet est écrit à l'intérieur d'une expression complète, tous les accès à cet objet à l'intérieur de la même expression doit être directement impliqué dans le calcul de la valeur à écrire. .

Par exemple dans i = i + 1 tout l'accès de i (en L.H.S. et en R.H.S.) sont directement impliqué dans le calcul de la valeur à écrire. C'est donc bien.

Cette règle limite effectivement les expressions légales à celles dans lesquelles les accès précèdent manifestement la modification.

Exemple 1 :

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

Exemple 2 :

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

n'est pas autorisé parce que l'un des accès de i (celui de a[i] ) n'a rien à voir avec la valeur qui finit par être stockée dans i (ce qui se produit plus dans i++ ), et il n'y a donc pas de bon moyen de définir - que ce soit pour notre compréhension ou celle du compilateur - si l'accès doit avoir lieu avant ou après le stockage de la valeur incrémentée. Le comportement est donc indéfini.

Exemple 3 :

int x = i + i++ ;// Similar to above

Réponse complémentaire pour C++11 aquí .

1 votes

Je ne comprends pas la partie "En outre". Cela ne rend-il pas le postfixe ++ inutile ? Par exemple, dans l'expression *p++ = 4 La valeur précédente de p est accédée à la fois pour déterminer la valeur à stocker dans p (OK), et pour déterminer l'adresse où stocker le 4 (pas OK ?). Mais cette idiome commune n'est sûrement pas un comportement indéfini ?

46 votes

*p++ = 4 n'est pas un comportement indéfini . *p++ est interprété comme *(p++) . p++ renvoie à p (une copie) et la valeur est stockée à l'adresse précédente. Pourquoi cela invoquerait-il UB ? C'est parfaitement normal.

2 votes

@Prasoon : Donc renvoyer une copie de p ne compte pas comme " accès " à la " valeur antérieure " de p ? Pourquoi pas ? Le terme "accès" a-t-il ici un sens technique ?

285voto

Prasoon Saurav Points 47488

Il s'agit d'une suite à mon réponse précédente et contient des éléments relatifs à C++11. .


Pré-requis : Une connaissance élémentaire des relations (mathématiques).


Est-il vrai qu'il n'y a pas de points de séquence dans C++11 ?

Oui ! C'est très vrai.

Points de séquence ont été remplacés par Séquencé avant y Séquence après (et Non séquencé y Séquence indéterminée ) relations en C++11.


C'est quoi exactement cette histoire de "séquencé avant" ?

Séquencé avant (§1.9/13) est une relation qui est :

entre les évaluations exécutées par un seul filetage et induit une Ordre partiel strict 1

Formellement, cela signifie qu'étant donné deux évaluations quelconques (Voir ci-dessous) A y B si A es séquencé avant B alors l'exécution de A précède l'exécution de B . Si A n'est pas séquencé avant B y B n'est pas séquencé avant A alors A y B sont non séquencé 2 .

Évaluations A y B sont séquencé de manière indéterminée lorsque soit A est séquencé avant B o B est séquencé avant A mais il n'est pas précisé lequel 3 .

[NOTES]<br>1 : Un ordre partiel strict est un <a href="http://en.wikipedia.org/wiki/Binary_relation" rel="noreferrer"><strong>relation binaire </strong></a><code>"<"</code> sur un ensemble <code>P</code> qui est <a href="http://en.wikipedia.org/wiki/Asymmetric_relation" rel="noreferrer"><code>asymmetric</code></a> y <a href="http://en.wikipedia.org/wiki/Transitive_relation" rel="noreferrer"><code>transitive</code></a> c'est-à-dire que pour tous les <code>a</code> , <code>b</code> y <code>c</code> en <code>P</code> nous l'avons :<br>........(i). si a < b alors ¬ (b < a) ( <code>asymmetry</code> ) ;<br>........(ii). Si a < b et b < c alors a < c ( <code>transitivity</code> ).<br>2 : L'exécution de <strong>évaluations non séquencées </strong>peut <em>chevauchement </em>.<br>3 : <strong>Evaluations à séquence indéterminée </strong>ne peut pas <em>chevauchement </em>mais l'un ou l'autre pourrait être exécuté en premier.


Quelle est la signification du mot "évaluation" dans le contexte de C++11 ?

En C++11, l'évaluation d'une expression (ou d'une sous-expression) comprend en général :

Maintenant (§1.9/14) dit :

Chaque calcul de valeur et effet secondaire associé à une expression complète est séquencé avant chaque calcul de valeur et chaque effet secondaire associé à la prochaine expression complète à évaluer .

  • Exemple trivial :

    int x; x = 10; ++x;

    Calcul de la valeur et effet secondaire associé à ++x est séquencé après le calcul de la valeur et l'effet secondaire de x = 10;


Il doit donc y avoir une relation entre le comportement indéfini et les éléments susmentionnés, non ?

Oui ! Bien.

Dans (§1.9/15), il a été mentionné que

Sauf indication contraire, les évaluations des opérandes d'opérateurs individuels et des sous-expressions d'expressions individuelles sont non séquencé 4 .

Par exemple :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. Évaluation des opérandes de + sont non séquencés les uns par rapport aux autres.
  2. Évaluation des opérandes de << y >> Les opérateurs ne sont pas séquencés les uns par rapport aux autres.

4 : Dans une expression qui est évaluée plus d'une fois au cours de l'exécution d'un programme, <strong>non séquencé </strong>y <strong>séquencé de manière indéterminée </strong>les évaluations de ses sous-expressions ne doivent pas nécessairement être effectuées de manière cohérente dans différentes évaluations.

(§1.9/15) Les calculs de la valeur des opérandes d'une fonction sont séquencés avant le calcul de la valeur du résultat de l'opérateur.

Cela signifie qu'en x + y le calcul de la valeur de x y y sont séquencés avant le calcul de la valeur de (x + y) .

Plus important encore

(§1.9/15) Si un effet secondaire sur un objet scalaire n'est pas séquencé par rapport à l'un ou l'autre des éléments suivants

(a) un autre effet secondaire sur le même objet scalaire

ou

(b) un calcul de valeur utilisant la valeur du même objet scalaire.

le comportement est indéfini .

Exemples :

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

Lors de l'appel d'une fonction (que la fonction soit en ligne ou non), chaque calcul de valeur et effet secondaire associé à une expression d'argument, ou à l'expression postfixe désignant la fonction appelée, est séquencé avant l'exécution de chaque expression ou instruction dans le corps de la fonction appelée. [ Note : Les calculs de valeurs et les effets secondaires associés à différentes expressions d'arguments ne sont pas séquencés. . - note de fin ]

Expressions (5) , (7) y (8) n'invoquent pas de comportement non défini. Consultez les réponses suivantes pour une explication plus détaillée.


Note finale :

Si vous trouvez une faille dans cet article, veuillez laisser un commentaire. Les Power-users (avec un rep >20000) n'hésitent pas à éditer le post pour corriger les fautes de frappe et autres erreurs.

4 votes

Au lieu de "asymétrique", les relations séquentielles avant/après sont "antisymétriques". Ceci devrait être modifié dans le texte pour se conformer à la définition d'un ordre partiel donnée plus loin (qui est également en accord avec Wikipedia).

1 votes

Pourquoi le point 7) du dernier exemple est-il un UB ? Peut-être devrait-il être f(i = -1, i = 1) ?

1 votes

J'ai corrigé la description de la relation "séquencé avant". Il s'agit d'une Ordre partiel strict . Évidemment, une expression ne peut pas être séquencée avant elle-même, donc la relation ne peut pas être réflexive. Elle est donc asymétrique et non anti-symétrique.

74voto

Points de séquence

Les points de séquence sont des points dans l'exécution d'un programme où tous les effets secondaires produits par les évaluations antérieures aux points de séquence ont été achevés. Les effets secondaires produits par les évaluations qui se produisent après le point de séquence seront donc séparés des effets secondaires produits par les évaluations qui se produisent avant le point de séquence et qui se produisent après.

Évaluations

Évaluer quelque chose signifie appliquer une sémantique d'exécution à une expression. Il existe des expressions non évaluées (opérandes de sizeof certains opérandes de typeid et autres) qui ne font qu'inspecter le type de l'expression et n'ont pas de signification au moment de l'exécution. Si une expression est évaluée, elle peut donner lieu à une valeur, ce qui peut impliquer la lecture des valeurs des objets, ou elle peut simplement être évaluée sur un objet sans en lire la valeur (elle reste alors une expression de type lvalue comme avec la sous-expression gauche d'une affectation). En outre, il peut produire des effets secondaires si nécessaire. Une évaluation est complète si sa valeur est connue, mais tant qu'un point de séquence n'a pas été atteint, les effets secondaires produits par l'évaluation sont supposés être encore traités.

Vous avez des points de séquence après toutes les évaluations qui doivent généralement être traitées complètement avant que certaines autres expressions ne soient traitées. Ces points sont

  • Après l'évaluation de a en a && b y a || b y a ? b : c . Aussi après l'évaluation de a en a, b - cet opérateur est appelé "opérateur virgule".
  • Pour un appel de fonction, après avoir évalué les arguments de l'appel de fonction et avant de commencer les évaluations dans le corps de la fonction.
  • Après l'évaluation d'une expression complète (qui n'a pas été évaluée comme partie d'une autre expression). Les exemples sont les conditions de boucle, les conditions if, les valeurs de commutation et les instructions d'expression.
  • Immédiatement avant qu'une fonction ne se termine (en déroulant la fonction par une exception ou en la retournant ordinairement après avoir (éventuellement) créé la valeur de retour). Cela permet de s'assurer que chaque effet secondaire d'une fonction a réellement été réglé et est entièrement traité.

Effets secondaires

Un effet secondaire est un changement dans l'environnement d'exécution du programme qui se produit en plus du simple calcul d'une valeur. Il peut s'agir (entre autres) d'écrire dans un objet, d'appeler une fonction d'entrée/sortie ou d'appeler une fonction qui le fait.

Flux d'exécution du programme

Avec ces trois termes, le flux d'un programme peut être visualisé comme suit. Dans les diagrammes suivants, un E(X) spécifie l'évaluation d'une (sous-)expression x , un % spécifie un point de séquence et un S(k, e) spécifie un effet secondaire k sur un objet e . Si une évaluation doit lire une valeur à partir d'un objet nommé (si x est un nom), l'évaluation s'écrit comme suit V(x) sinon, il s'écrit E(x) . Les effets secondaires sont écrits à droite et à gauche des expressions. Un bord entre deux expressions signifie que l'expression supérieure est évaluée avant l'expression inférieure (généralement parce que l'expression inférieure dépend de la valeur ou de la lvalue de l'expression supérieure).

Si vous regardez les deux expressions i++; i++; vous pouvez représenter le diagramme suivant

E(i++) -> { S(increment, i) }
   |
   %
   |
E(i++) -> { S(increment, i) }
   |
   %

Comme on peut le voir, il y a deux points de séquence, et l'un d'eux sépare les deux modifications de i . Les arguments d'appel de fonction sont également intéressants, bien que j'omette le diagramme pour cette raison.

int c = 0;
int d = 0;
void f(int a, int b) { assert((a == c - 1) && (b == d - 1)); }
int main() { f(c++, d++); }

L'assert est bien, car il est garanti que lorsque f Lorsque le corps de la commande est exécuté, les effets secondaires produits par les évaluations des arguments sont terminés : Par conséquent, c y d ont été complètement incrémentés.

Considérons l'énoncé de l'expression i++ * j++;

{ S(increment, i) } <- E(i++)      E(j++) -> { S(increment, j) }
                           \       /
                            +--+--+
                               |
                         E(i++ * j++)
                               |
                               %

Wow, d'où viennent les deux branches ? Rappelez-vous de la définition initiale du point de séquence : Les points de séquence affectent les évaluations qui se produisent avant à ce sujet. Toutes les sous-expressions de la multiplication sont évaluées avant celle-ci et il n'y a pas d'autre point de séquence, nous devons donc supposer une "parallélité maximale" pour trouver où nous avons potentiellement des écritures concurrentes sur le même objet. Plus formellement, les deux branches ne sont pas commandé . La relation de point de séquence est une relation qui ordonne certaines évaluations entre elles et n'en ordonne pas d'autres : C'est donc une ordre partiel .

Effets secondaires contradictoires

Pour donner au compilateur une liberté maximale dans la génération et l'optimisation du code machine, les cas comme la multiplication ci-dessus ne séquencent pas les évaluations des sous-expressions et ne séparent pas les effets secondaires produits par celles-ci, sauf dans les quelques cas décrits ci-dessus. Cela peut conduire à des conflits, et la norme C++ marque le comportement des programmes comme indéfini s'ils essaient de modifier le même objet sans point de séquence intermédiaire (en réalité, cela s'applique aux objets scalaires, car les autres objets sont soit non modifiables (tableaux), soit simplement non applicables à cette règle (objets de classe)). Le comportement est également indéfini si un précédent la valeur est lue depuis l'objet mais il y a aussi une modification, comme dans i * i++

// This yields to undefined behavior!
// Left 'i' is not guaranteed to read new value:

    V(i)        E(i++) -> { S(increment, i) })
      \         /
       +---+---+
           |
       E(i * i++)
           |
           %

Par exception, il est permis de lire la valeur de l'objet si elle est nécessaire pour calculer la nouvelle valeur. C'est le cas dans i = i + 1

                V(i)        E(1)
                   \         /
                    +---+---+
                        |
  E(i)              E(i + 1)
     \                 /
      +-------+-------+
              |
        E(i = i + 1) -> { S(assign, i) }
              |
              %

Comme nous le voyons ici, la valeur de i est lu du côté droit et après l'évaluation des deux côtés, l'affectation a lieu. Nous avons donc un effet secondaire et la lecture de i sans point de séquence intermédiaire, mais la lecture ne sert qu'à déterminer la valeur à enregistrer dans i donc tout va bien.

Parfois, une valeur est lue après qu'une modification ait été effectuée. C'est le cas pour a = (b = 0) qui, en C++, sera écrit en b et ensuite lire de b sans point de séquence intermédiaire ! Cela ne pose cependant aucun problème, car il ne lit pas l'élément précédent valeur de b mais le nouveau valeur de celle-ci. Dans ce cas, l'effet secondaire de l'affectation à b a été achevé non seulement avant le point de séquence suivant, mais aussi avant la lecture de b selon les besoins de l'affectation à a pour obtenir la nouvelle valeur de b . Dans la spec, cette relation est établie par des contraintes explicites, dans ce cas elle s'applique en particulier à b = 0 et lit " Le résultat de l'opération d'affectation est la valeur stockée dans l'opérande de gauche après que l'affectation ait eu lieu ; le résultat est une lvalue. " Pourquoi pas un point de séquence pour faire cette relation ? Parce qu'un point de séquence aurait l'effet indésirable d'exiger que chaque effet secondaire qui se produit dans l'évaluation de l'opérande gauche et droit soit complet, au lieu de ne le faire que pour l'affectation dans le cas où la valeur l qui en résulte est lue.

Mot de la fin

Il convient de noter que les temporaires créés lors de l'évaluation d'une expression complète ne sont généralement pas nettoyés avant le point de séquence suivant mais seulement lorsque l'expression complète a été complètement évaluée (dans certaines situations, la durée de vie des temporaires sera au contraire encore plus longue si des références leur sont liées).

32voto

AlexD Points 7089

C++17 ( N4659 ) comprend une proposition Affiner l'ordre d'évaluation des expressions pour le C++ idiomatique qui définit un ordre plus strict d'évaluation des expressions.

En particulier, le phrase suivante

8.18 Opérateurs d'assignation et d'assignation composée :
....

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

ainsi que les précisions suivantes

Une expression X est dit être séquencé avant une expression Y si chaque chaque calcul de valeur et chaque effet secondaire associé à l'expression X est séquencé avant chaque valeur et chaque effet secondaire associé à l'expression. Y .

rendent valides plusieurs cas de comportements précédemment non définis, y compris celui en question :

a[++i] = i;

Cependant, plusieurs autres cas similaires conduisent encore à un comportement non défini.

Sur N4140 :

i = i++ + 1; // the behavior is undefined

Mais en N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

Bien entendu, l'utilisation d'un compilateur compatible avec C++17 ne signifie pas nécessairement que l'on doit commencer à écrire de telles expressions.

11voto

Yttrill Points 2461

Je suppose qu'il y a une raison fondamentale pour le changement, ce n'est pas simplement cosmétique pour rendre l'ancienne interprétation plus claire : cette raison est la concurrence. L'ordre d'élaboration non spécifié est simplement la sélection d'un ordre parmi plusieurs ordres sériels possibles, ce qui est très différent des ordres avant et après, car s'il n'y a pas d'ordre spécifié, l'évaluation concurrente est possible : ce n'est pas le cas avec les anciennes règles. Par exemple dans :

f (a,b)

Auparavant, soit a puis b, soit b puis a. Maintenant, a et b peuvent être évalués avec des instructions entrelacées ou même sur des cœurs différents.

5 votes

Je crois cependant que si "a" ou "b" inclut un appel de fonction, ils sont indéterminément séquencés plutôt que non séquencés, c'est-à-dire que tous les effets secondaires de l'un doivent se produire avant ceux de l'autre, bien que le compilateur n'ait pas besoin d'être cohérent quant à celui qui passe en premier. Si cela n'était plus vrai, cela casserait beaucoup de code qui repose sur le fait que les opérations ne se chevauchent pas (par exemple, si 'a' et 'b' installent, utilisent et retirent chacun un état statique partagé).

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