Existe-t-il des points de séquence dans l’expression a ^ = b ^ = a ^ = b, ou est-ce indéfini?

La méthode supposément “intelligente” (mais en réalité inefficace) d’échange de deux variables entières, au lieu d’utiliser le stockage temporaire, implique souvent cette ligne:

int a = 10; int b = 42; a ^= b ^= a ^= b; /*Here*/ printf("a=%d, b=%d\n", a, b); 

Mais je me demande, les opérateurs d’assignation composés comme ^= ne sont pas des points de séquence, n’est-ce pas? Est-ce que cela signifie que c’est en fait un comportement indéfini?

 a ^= b ^= a ^= b; /*Here*/ 

C’est un comportement indéfini.

Vous modifiez un object ( a ) plusieurs fois entre deux points de séquence.

(C99, 6.5p2) “Entre la séquence précédente et la suivante, la valeur stockée d’un object doit être modifiée au plus une fois par l’évaluation d’une expression.

Les assignations simples ainsi que les assignations composées n’introduisent pas de sharepoint séquence. Ici, il y a un sharepoint séquence avant l’expression de l’expression et après celle-ci.

Les points de séquence sont listés dans l’Annexe C (informative) des normes c99 et c11.

^ = ne sont pas des points de séquence, sont-ils

Ils ne sont pas.

Est-ce que cela signifie que c’est en fait un comportement indéfini?

Oui, ça l’est. N’utilisez pas cette technique “intelligente”.

Il n’y a pas de points de séquence dans cette expression, elle génère donc un comportement indéfini.

Vous pouvez résoudre le problème de manière sortingviale et conserver l’essentiel de la concision en utilisant l’opérateur virgule, qui introduit des points de séquence:

 a ^= b, b ^= a, a ^= b; 

L’ordre d’évaluation des opérateurs ^= est bien défini. Ce qui n’est pas bien défini est l’ordre dans lequel a et b sont modifiés.

 a ^= b ^= a ^= b; 

est équivalent à

 a ^= (b ^= (a ^= b)); 

Un opérateur ne peut pas être évalué avant que ses arguments ne soient évalués, il va donc certainement exécuter a ^= b premier.

La raison pour laquelle ce comportement est indéfini est que, pour donner plus de flexibilité au compilateur dans ses optimisations, il est permis de modifier les valeurs des variables dans l’ordre de son choix. Il pourrait choisir de faire ceci:

 int a1 = a ^ b; int b1 = b ^ a1; int a2 = a ^ b1; a = a1; a = a2; b = b1; 

ou ca:

 int a1 = a ^ b; int b1 = b ^ a1; a = a1; int a2 = a ^ b1; a = a2; b = b1; 

ou même ceci:

 int a1 = a ^ b; int b1 = b ^ a1; int a2 = a ^ b1; a = a2; a = a1; b = b1; 

Si le compilateur ne pouvait choisir qu’une de ces trois façons de faire, il s’agirait simplement d’un comportement “non spécifié”. Cependant, la norme va plus loin et fait de ce comportement un comportement “non défini”, ce qui permet au compilateur de supposer que cela ne peut même pas se produire.