交换两个整数变量而不是使用临时存储的所谓“聪明”(但实际上效率低下)的方式通常涉及这一行:
int a = 10;
int b = 42;
a ^= b ^= a ^= b; /*Here*/
printf("a=%d, b=%d\n", 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;
运算符的评估顺序^=
是明确定义的。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;
如果编译器只能选择这三种方式中的一种来做事,那将只是“未指定”的行为。但是,该标准更进一步,使其成为“未定义”行为,这基本上允许编译器假设它甚至不会发生。