据称"聪明" (但实际上效率很低)交换两个整数变量的方式,而不是使用临时存储,通常涉及这一行:
int a = 10;
int b = 42;
a ^= b ^= a ^= b; /*Here*/
printf("a=%d, b=%d\n", a, b);
但是我想知道,像^=
这样的复合赋值运算符不是序列点,是吗?
这是否意味着它实际上是未定义的行为?
答案 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)
^=
运算符的评估顺序已明确定义。未明确定义的是a
和b
被修改的顺序。
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;行为,基本上允许编译器假设它甚至不会发生。