表达式中有序列点a ^ = b ^ = a ^ = b,还是未定义?

据称“巧妙”(但实际上效率低下)交换两个整数变量而不是使用临时存储的方式通常涉及这一行:

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

但我想知道,复合赋值运算符如^=不是序列点,是吗? 这是否意味着它实际上是未定义的行为?

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

这是未定义的行为。

您正在两个序列点之间多次修改对象( a )。

(C99,6.5p2)“在上一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。

简单赋值和复合赋值不会引入序列点。 这里在表达式语句表达式之前和表达式语句之后有一个序列点。

序列点列于c99和c11标准的附录C(资料性附录)中。

^ =不是序列点,是它们

他们不是。

这是否意味着它实际上是未定义的行为?

是的。 不要使用这种“聪明”的技巧。

该表达式中没有序列点,因此会产生未定义的行为。

您可以通过使用逗号运算符来简单地修复它并保留大部分简洁性,这引入序列点:

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

^=运算符的评估顺序已明确定义。 没有明确定义的是ab被修改的顺序。

 a ^= b ^= a ^= b; 

相当于

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

在评估参数之前无法对运算符求值,因此它肯定会首先执行a ^= b

使其成为未定义行为的原因是,为了使编译器在进行优化时具有更大的灵活性,允许以其选择的任何顺序修改变量值。 它可以选择这样做:

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

或这个:

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

甚至这个:

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

如果编译器只能选择这三种方法中的一种来做事,那么这只是“未指定”的行为。 但是,标准更进一步,并使其成为“未定义”行为,这基本上允许编译器假设它甚至不会发生。