在C中,当变量(假设两者都是int) i小于j时,我们可以使用下面的公式
i^=j^=i^=j以交换这两个变量的值。例如,让int i = 3,j = 5;在计算i^=j^=i^=j之后,我有i = 5,j = 3。
但是,如果我使用两个int指针来重新使用*i^=*j^=*i^=*j (使用上面的示例),那么我所拥有的将是i = 0和j = 3。
In C
1
int i=3, j=5;
i^=j^=i^=j; // after this i = 5, j=32
int i = 3, j= 5;
int *pi = &i, *pj = &j;
*pi^=*pj^=*pi^=*pj; // after this, $pi = 0, *pj = 5In JavaScript
var i=3, j=5;
i^=j^=i^=j; // after this, i = 0, j= 3JavaScript的结果使我更感兴趣
我的示例代码,在ubuntu服务器11.0和gcc上
#include <stdio.h>
int main(){
int i=7, j=9;
int *pi=&i, *pj=&j;
i^=j^=i^=j;
printf("i=%d j=%d\n", i, j);
i=7, j=9;
*pi^=*pj^=*pi^=*pj
printf("i=%d j=%d\n", *pi, *pj);
}
C中未定义的行为
C中未定义的行为是否是导致这一问题的真正原因?
1
在windows 7上使用visual 2005编译的代码会产生预期的结果(输出i= 7,j=9两次)。
2
使用gcc编译的代码在ubuntu ( gcc test.c )上产生意外结果(输出i= 7,j=9,i= 0,j=9)
3.
使用gcc编译的代码在ubuntu ( gcc -O test.c )上产生预期结果(输出i= 7,j=9两次)。)
发布于 2012-09-08 16:25:13
i^=j^=i^=j是C中未定义的行为。
您违反了序列点规则,在两个序列点之间修改了两次i。
这意味着实现可以自由地分配任何值,甚至使程序崩溃。
出于同样的原因,*i^=*j^=*i^=*j也是未定义的行为。
(C99,6.5p2)“在前一个序列点和下一个序列点之间,对象最多应该通过表达式的计算修改其存储值一次。
发布于 2012-09-08 14:18:45
考虑以下代码:
#include <stdio.h>
int main() {
int i=7, j=9;
int *pi=&i, *pj=&j;
i^=j^=i^=j;
printf("i=%d j=%d\n", i, j);
i=7, j=9;
*pi^=*pj^=*pi^=*pj;
printf("i=%d j=%d\n", *pi, *pj);
return 0;
}如果您试图编译它,您将看到第一行的warning: unsequenced modification。就像@ouath说的那样,它的定义不明确。根据C11标准,赋值表达式以读-修改-写的方式工作。这个操作并不是所有CPU体系结构上的原子操作。您可以阅读有关警告这里的更多信息。
有趣的是,对于*pi^=*pj^=*pi^=*pj;,我的LLVM编译器中没有警告。
发布于 2012-09-08 20:23:00
至于Javascript结果中添加的“更有趣的”方面:
虽然表达式在C 正如奥赫在回答中所解释的中没有定义,但在Javascript中定义得很好。然而,在Javascript中如何计算表达式的规则可能不是您所期望的。
ECMAscript规范指出,复合赋值操作符的计算方式如下(ECMA-262 11.13.2):
production : LeftHandSideExpression
@=AssignmentExpression,其中@表示上述操作之一,评估如下:
因此,表达式i ^= j ^= i ^= j将按以下步骤进行计算:
i = (3 ^ (j ^= i ^= j))
i = (3 ^ (j = (5 ^ i ^= j)))
i = (3 ^ (j = (5 ^ (i = 3 ^ j)))))
i = (3 ^ (j = (5 ^ (i = 3 ^ 5)))))
i = (3 ^ (j = (5 ^ (i = 6)))))
i = (3 ^ (j = (5 ^ 6))))
i = (3 ^ (j = 3)) // j is set to 3
i = (3 ^ 3)
i = 0 // i is set to 0避免使用xor操作来交换值的另一个原因。
https://stackoverflow.com/questions/12331518
复制相似问题