看到一个2010 年的帖子[1],里面讨论了一段 JavaScript 代码:
var a = {n:1};
a.x = a = {n:2};
alert(a.x); // --> undefined
帖子里有很多的讨论来分析为何 a.x 是 undefined,下面是我的一些思考。
首先,在JavaScript中,有以下几点需要明确的:
a.x = a = {n: 2};
// =>
a.x = (a = {n: 2});
/*
1. 首先,按 = 号的结合性做个分组;
2. 然后,由于从左到右的求值顺序和运算符优先级,先计算 a.x,获得指向内存中的某个地址,假定为 A;
3. 接着计算第一个等号的右边,其为第二个等号的求值结果: 把变量 a 指向对象 {n: 2}, 然后返回该引用;
4. 然后把该引用赋给 A,即 A 位置存储的值是 {n: 2};
在第三个步骤计算第二个等号的过程中,把 a 重新指向了另一个对象 {n: 2},而不在是 {n: 1} 了,
这个时候 a.x 指向的位置已不在是 A 了,故最后得到的 a.x 是 undefined。
*/
不同于一些用于精简代码显示技术的 Trick,连续赋值应该说是较为常用的一种语句,但需要注意的是,尽量少写出这种修改、引用并存的语句。
在 JavaScript 中还好,规定了从左往右求值,但在 C/C++ 中,并没有明确规定求值顺序,在某些情况下尤其是在函数参数中,可能会带来很大的问题。
翻了下邮件,在 2010 年 4 月的时候,向师兄请教了一个简单的问题:
#include <iostream>
using namespace std;
int add1(int x)
{
x++;
return x;
}
int add2(int& a)
{
a++;
return a;
}
int main()
{
int x=0;
int y=0;
cout << add1(x) << " " << x <<endl;
cout << add2(y) << " " << y <<endl;
/*
cout << "调用前: x y"<<endl;
cout <<"\t"<< x << " " << y <<endl;
cout << "函数结果" << add1(x) <<" "<< add2(y) << endl;
cout << "调用后: x y"<<endl;
cout << "\t"<<x << " " << y <<endl;
*/
system("pause");
return 0;
}
在 VC 6.0,2008,2010,g++ for windows,还有在 Ubuntu下用 g++ 编译运行都是得到 1 0 1 0 (此处不显示换行)的结果;只有在采用了 g++ 编译器的 DEV C++ 5.0 中得到的是 1 0 1 1。
咨询和查资料[5][6][7][8]的结论是:C/C++ 函数参数的求值顺序没有明确的规范,具体依赖于编译器的实现,因此不要写出那种带副作用的连续操作语句。
References
比原帖分析的简单,明了!
握手1楼,原帖看得蛋疼,还是这个好