我对C#表达式的理解

今早看到了《你真的了解a ^= (b ^= (a ^= b))吗?》一文,文章中提到了一个有趣的表达式:a ^= (b ^= (a ^= b)); 很简单的一个程序: 

1 int a = 2;
2 int b = 9;
3 a ^= (b ^= (a ^= b));
4 Console.WriteLine("{0}, {1}", a.ToString(), b.ToString());

最后的运算结果不是预期的9,2,而是0,2。同时,还有1个表达式: 

1 int i=0;
2 int j=(i++)+(i++);
3 j=?

j的值是多少?2么?不是,正确的结果是1。

为啥会有这样的结果,难道是编译器的bug么?其实不然,编写编译器的人可不是数学白痴,是我们对计算机的理解有误。

C#中,表达式的计算遵循一个规律:从左到右依次计算。

例如,Z= X operation Y,计算机依次计算X,Y的值,然后OperationX和Y,最后赋值给Z。

同理,Z = X operation1 ( X operation2 Y),计算机则把(X operation2 Y)当做一个变量Temp。此时Z = X operation1 Temp。然后计算机再计算Temp表达式,并压栈,计算完Temp表达式后,将结果出栈。这是个递归过程。注意:这个过程中,X与Temp是两个并列的表达式,如果Temp中有对X进行再操作,并不会影响X的结果。因此表达式规律是从左到右依次计算,X在左,已经计算完毕。如果是Z = Temp operation1 X,那么Temp中对X的操作将会影响到X本身。

OK,理论分析完毕,让我们来实践一下为什么上面2个表达式的结果令人诧异。

对于第一个表达式:a ^= (b ^= (a ^= b))。先将运算符分解:a = a ^ (b = b ^ (a = a ^ b)); 这样看起来更清晰。

1. 计算机分解表达式为 a = a ^ Temp1。计算左边的a,a本身是一个变量,因此a用2来代替。接下来计算右边的Temp1表达式,发现其中比较复杂,无法直接用值来代替,因此开始对Temp1堆栈计算。

2. Temp1 是 b = b ^ (a = a ^ b)。计算左边,b是变量,可以直接赋值为9。右边是表达式,用Temp2代替。

3. Temp2 是 a = a ^ b。计算左边,a是变量,替换为2; 右边,b也是变量,替换为9。并计算出Temp2=2 ^ 9 = 11。

4. 回归计算Temp 1 = 9 ^ Temp2 = 9 ^ 11 = 2,也就是b = 2。

5. 回归计算a = 2 ^ Temp1 = 2 ^ 2 = 0。也就是a = 0。

OK,这样0, 2结果就出现了。

对于第二个表达式: j = (i++) + (i++);。

1. 先计算左边的表达式,i++,因为是后缀,所以(i++)表达式返回i,此时i = 0, 意味着返回0。

2. 执行++运算符,0++就是1,因此i = 1;

3. 计算右边的表达式(i++),也是后缀,因此返回i ,此时 i = 1,意味着返回1;

4. 执行++运算符,1++就是2,因此i = 2;

5 赋值给j. j = 0 + 1 = 1。

综上,其实不是什么编译器的bug,而是计算机的做法永远是死脑筋的。

还有,实际项目中千万不要出现这样的表达式,宁可多写几行,也不要堆在一行,我们一定要遵循《代码规范》。这也是为了提高代码的可阅读性和可维护性。

最后,给大家出个题目:

int i = 0; int j = (i--) + ((i++) + (++i)); 计算结果i 和 j 分别是多少?

 

posted @ 2009-06-14 20:09  primeli  阅读(1434)  评论(1编辑  收藏  举报