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

时间:2013-07-04 16:50:55

标签: c undefined-behavior sequence-points compound-assignment

据称"聪明" (但实际上效率很低)交换两个整数变量的方式,而不是使用临时存储,通常涉及这一行:

int a = 10;
int b = 42;

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

printf("a=%d, b=%d\n", a, b); 

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

4 个答案:

答案 0 :(得分:18)

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

这是未定义的行为。

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

  

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

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

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

答案 1 :(得分:13)

  

^ =不是序列点,是吗

他们不是。

  

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

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

答案 2 :(得分:7)

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

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

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

答案 3 :(得分:5)

^=运算符的评估顺序已明确定义。未明确定义的是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;

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