C和C#自加操作的汇编比较
作者:山东人在成都
看到一篇文章说在C语言中,i=7;使用i++ * i++后得出的值为49,而不是56。按照定义,++运算符在后面,是先引用后自加。在第一次引用后为什么在第二次引用时却没有自加呢。因为我平时使用的是C#,因此在C#中进行了测试,结果表示计算的值为56。看来二者的编译原理不同。
为了深入的比较二者的不同,特用C和C#各自编写了相同的代码,用来计算自加。代码如下:
int i=7;
int x = i++ * i++;
将两者的源代码编译成可执行文件,运行后得出的结果,C编译下为49,.Net编译下为56。
首先看看.Net编译后的IL代码。
.maxstack 4
.locals init (int32 V_0,
int32 V_1)
IL_0000: nop
IL_0001: ldc.i4.7
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: dup
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: dup
IL_000a: ldc.i4.1
IL_000b: add
IL_000c: stloc.0
IL_000d: mul
IL_000e: stloc.1
.Net编译后,首先分配了4个字节的堆栈,并初始化了两个变量。接着装载整数7到堆栈,从堆栈弹出数据保存到变量0,也就是将整数7保存到变量0,再将变量0装载到堆栈,然后将堆栈顶部的数据(也就是整数7)复制一份。这样堆栈中就保存了两份整数7。再加载整数1到堆栈顶部,add指令将堆栈顶部的第一个和第二个数值进行相加,也就是7+1,再将结果存入堆栈,此时堆栈上只有两个数7和8。将8从堆栈顶部弹出保存至变量0,再加载到堆栈并复制一份。这样堆栈中就保存了三个数值,7,8,8。再加载整数1到堆栈,执行add指令后,堆栈中的数值为7,8,9。从堆栈顶部弹出9保存到变量0(也就是变量i),最后,将堆栈中的7,8进行mul操作,得到数值56,弹出保存在变量1也就是变量x。
可以看出C#编译器在自加操作时,复制了一份作为临时变量,紧接着就会执行加法操作。在第二次引用时,其值已经自加了1。所以得出的结果为56。
再来看看C编译器编译后的结果。使用反汇编工具反汇编后的代码。
push ebp
mov ebp, esp
sub esp, 00000008
mov [ebp-08], 00000007
mov eax, dword ptr [ebp-08]
imul eax, dword ptr [ebp-08]
mov dword ptr [ebp-04], eax
mov ecx, dword ptr [ebp-08]
add ecx, 00000001
mov dword ptr [ebp-08], ecx
mov edx, dword ptr [ebp-08]
add edx, 00000001
mov dword ptr [ebp-08], edx
mov eax, dword ptr [ebp-08]
push eax
mov ecx dword ptr [ebp-04]
push ecx
首先分配8个字节堆栈空间,再将整数7加到堆栈,再将堆栈顶部的整数7给寄存器eax,然后将这两个数进行imul操作,结果存入eax,其后的指令则是对整数7进行了两次加1的操作,并将结果存入寄器ecx。再将这两个值移入堆栈。
可以看出C编译器在执行这个操作时,直接先进行了乘法的操作,然后再处理自加的问题。
这是两个编译器对何时先引用后自加的理解执行不同,C#编译器对++运算符,是在引用后马上进行自加,那么在同一表达式中第二次引用时就已经是自加后的值了。而C编译器则是先引用,并执行完整个表达式后,再将变量自加,在执行表达式时,不管表达式中有多少个引用,都不会执行自加操作,只有在表达式执行完后,才会进行自加,有几次自加就加几次。
阅读(1744) | 评论(0) | 转发(0) |