为什么i^=j^=i^=j不是't等于*i^=*j^=*i^=*j
why i^=j^=i^=j isn't equal to *i^=*j^=*i^=*j
在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
。
在C中
1
int i=3, j=5;
i^=j^=i^=j; // after this i = 5, j=3
2
int i = 3, j= 5;
int *pi = &i, *pj = &j;
*pi^=*pj^=*pi^=*pj; // after this, $pi = 0, *pj = 5
在JavaScript中
var i=3, j=5;
i^=j^=i^=j; // after this, i = 0, j= 3
JavaScript中的结果让我更感兴趣
我的示例代码,在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
使用visualstudio2005在windows7上编译的代码产生预期结果(输出i=7,j=9两次)
2
在ubuntu上使用gcc编译的代码(gcc-test.c)会产生意外结果(输出i=7,j=9,然后i=0,j%9)
3
在ubuntu上使用gcc编译的代码(gcc-O test.c)产生预期结果(输出i=7,j=9两次。)
i^=j^=i^=j
是C.中未定义的行为
您在两个序列点之间修改i
两次,违反了序列点规则。
这意味着实现可以自由地分配任何值,甚至使程序崩溃。
出于同样的原因,*i^=*j^=*i^=*j
也是未定义的行为。
(C99,6.5p2)"在上一个和下一个序列点之间,对象的存储值应通过表达式的评估最多修改一次。"
考虑以下代码:
#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编译器中没有任何警告。
至于Javascript结果添加的"更有趣"方面:
正如ouah的回答中所解释的,这个表达式在C中是未定义的,但在Javascript中是定义良好的。然而,在Javascript中如何计算表达式的规则可能不是您所期望的。
ECMAscript规范规定,复合赋值运算符的求值方式如下(ECMA-262 11.13.2):
生产赋值表达式:LeftHandSideExpression
@=
AssignmentExpression,其中@
表示运算符之一如上所述,评估如下:
- 评估LeftHandSideExpression
- 调用GetValue(Result(1))
- 评估AssignmentExpression
- 调用GetValue(Result(3))
- 将运算符@应用于Result(2)和Result(4)
- 调用PutValue(Result(1),Result(5))
- 返回结果(5)
因此,表达式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操作交换值的另一个原因。
- JavaScript数组排序(函数)用于对表行进行排序,而不是排序
- Node.js v6.2.0类扩展不是函数错误
- 直接下载文件,而不是从window.open(url)
- 使用压缩的JavaScript文件(不是运行时压缩)
- 控制台返回var不是't定义,但它是
- 谷歌地图不是以HTML显示,而是在JS Fiddle上工作
- 为什么会出现错误;未捕获的类型错误:undefined不是函数;
- 为什么不是't窗口.恢复正常工作吗?(javascript/jquery)
- 后焦点更改为IE 11中的地址栏,而不是转移到表单中的下一个控件
- 文本框不是从Javascript/Asp.net中的对话框中打印出来的
- 为什么不是't运行此Javascript的Chrome
- MongoDB (php) - 以数组而不是多个属性的形式返回文档属性
- 禁用(而不是隐藏)浏览器滚动条
- 为什么i^=j^=i^=j不是't等于*i^=*j^=*i^=*j
- 如果在 JavaScript 中的 if 条件之后在执行块中使用两个等于而不是只有一个,会发生什么情况
- 这是检查变量是否被定义的最佳方法,而不是 null 并等于 “” 或其他任何内容
- 选择框文本等于而不是选择框文本包含-jQuery
- 为什么按位“不是 1”等于 -2
- Opera HTML5 视频就绪状态 == 2 而不是等于 4
- 这个内部原型函数等于窗口,而不是对象实例