c# 连等算式都在做什么

在研究两个整数互换的方法时(详细看这里),发现了一个有趣的现象。

a ^= b ^= a ^= b;      a ^= b;b ^= a;a ^= b;

有兴趣的童鞋可以看看下面代码的结果是什么:

int a = 10;
int b = 5;
a ^= b ^= a ^= b;
Console.WriteLine("{0}  {1}", a, b);

一直以为a=b=c就是按照先后顺序执行b=c,a=b。照上面的执行结果来看,还不一定。

到底执行的顺序怎么样,要看看反编译的代码才知道。

 

先看看a=b=c=30都在做什么:

IL_0008:  ldc.i4.s   30 //推送30到栈顶端
IL_000a:  dup           //复制一个30到栈顶端
IL_000b:  stloc.2       //提取顶端的30赋值给索引为2的变量,也就是c
IL_000c:  dup           //复制一个栈顶端的30
IL_000d:  stloc.1       //提取顶端的30赋值给索引为1的变量,也就是b
IL_000e:  stloc.0       //提取顶端的30赋值给索引为0的变量,也就是a

这样来看,a=b=c=30可以理解成c=30,b=30,a=30.

 

再看看a ^= b ^= a ^= b在做什么(初始化这里a=10,b=5):

IL_0006:  ldloc.0 //推送索引为0的变量值到栈顶端,也就是10
IL_0007:  ldloc.1 //推送5
IL_0008:  ldloc.0 //推送10 
IL_0009:  ldloc.1 //推送5
IL_000a:  xor     //提取10和5,做异或运算,将结果15推送到栈顶端 
IL_000b:  dup     //复制15
IL_000c:  stloc.0 //提取15赋值给a
IL_000d:  xor     //取顶端的两个值15和5,做异或运算,将结果10推送到栈顶端
IL_000e:  dup     //复制10
IL_000f:  stloc.1 //提取10赋值给b
IL_0010:  xor     //取顶端的两个值10和10做异或运算,结果0放到栈顶端
IL_0011:  stloc.0 //提取0赋值给a

做图解如下(作图水平不高,但应该勉强能看懂):

整个过程,用代码还原就是

    a1=a0^b0=15;

然后  b1=b0^a1=10;

再然后 a2=a0^b1=10^10=0。

前面两步的环节基本上是我们想要的,但是第三步a2=a0^b1却脱离了我们的原意,这里采用了a0做异或而不是a1,所以对应的结果也就出现了偏差。

因此,这一行代码执行下来,a=0,b=10,结果显然并不是我们想要的。

 

接下来,再看看a ^= b;b ^= a;a ^= b;在干嘛:

IL_0006:  ldloc.0  //推送10
IL_0007:  ldloc.1  //推送5
IL_0008:  xor      //提取10和5做异或运算,得15,推送至栈顶
IL_0009:  stloc.0  //将15赋值给a
IL_000a:  ldloc.1  //推送b的值5
IL_000b:  ldloc.0  //推送a的新值15
IL_000c:  xor      //提取15和5做异或运算,得10,推送至栈顶
IL_000d:  stloc.1  //将10赋值给b
IL_000e:  ldloc.0  //推送a的值15
IL_000f:  ldloc.1  //推送b的新值10
IL_0010:  xor      //提取15和10做异或运算,得5,推送至栈顶
IL_0011:  stloc.0  //将5赋值给a

整个过程比较清晰。中规中矩的异或计算然后赋值,再异或,再赋值,再异或赋值。最后a=5,b=10,结果和我们想的一样。

 

C#里面可以写连等句式,但是其中的逻辑一定要小心,尤其是连等过程中有变量赋值的,更要注意。平时使用的时候,建议不要为了省那两行的代码量而用连等语句拼凑,因为运算的结果可能和我们想要的不一样,而导致程序bug,得不偿失。 

posted @ 2014-10-09 08:11  脸谱匠  阅读(4356)  评论(4编辑  收藏  举报