VC++中使用Disassembly查看代码
最近在恶补C/C++的基础知识。一直以来,遇到一些与编译器相关的小程序时,我往往都迷惑不解,每每思索都不得要领。最近一个同学告诉我用反汇编来查看编译的代码,什么都会变得清楚明朗。
从一个例子引起的问题:
int k = 1; printf("%d/n",(++k)+(++k)); |
这个例子是我一个朋友发给我的,他告诉我他一直都想不通为什么在VC6.0下面会输出6。我接过例子的时候也以为结果是5,至少我在GNU Gcc下面编译运行是5。可是VC6.0下面输出却是6。
这回,我编译后用debug调试,打开disassembly查看汇编代码。的确,一看汇编后,什么事情都变得明朗。
25: int k = 1; 0040D799 mov dword ptr [ebp-10h],1 26: 27: printf("%d/n",(++k)+(++k)); 0040D7A0 mov edx,dword ptr [ebp-10h] 0040D7A3 add edx,1 0040D7A6 mov dword ptr [ebp-10h],edx 0040D7A9 mov eax,dword ptr [ebp-10h] 0040D7AC add eax,1 0040D7AF mov dword ptr [ebp-10h],eax 0040D7B2 mov ecx,dword ptr [ebp-10h] 0040D7B5 add ecx,dword ptr [ebp-10h] 0040D7B8 push ecx 0040D7B9 push offset string "%d/n" (00422f9c) 0040D7BE call printf (0040d6e0) |
ebp是运行时堆栈的栈低指针。ebp-10h地址上存放了k的值([ebp-10h]表示ebp-10h地址上的内容),运行int k = 1;后,[ebp-10h]的值是1。
接着运行printf("%d/n",(++k)+(++k));语句。printf()实际上是库函数,在调用printf()函数前,需要先计算(++k)+(++k)的值,然后把该值压到栈顶,以传送给printf()的形参。
关键还是(++k)+(++k)的计算问题。
C语言中只有四个运算存在规定的求值顺序:&&、||、?:和,(注意:此处的,是逗号操作符,并非函数参数列表的分隔符,)。
由于()的优先级作用,会先计算++k,但是并没有规定两边(++k)的求值顺序。所以,有可能是左边的++k先求值,也有可能是+右边的++k先求值。但无论如何,两次的++k之后,k的值是3 。然而,为何计算结果为什么不是2 + 3 或者是 3 + 2 呢?这似乎与求值顺序无关。
但是,编译器似乎没有预计得到,计算其中一边的表达式算影响到另一边的表达式的结果。所以,编译器认为两边的++k是相等的。
mov edx,dword ptr [ebp-10h] //把k的值放到edx寄存器中
add edx,1 //计算++k
mov dword ptr [ebp-10h],edx //再把++k的结果放回ebp-10h地址中,此时k=2
mov eax,dword ptr [ebp-10h] //把k值复制到eax寄存器中
add eax,1 //计算++k
mov dword ptr [ebp-10h],eax //再把++k的结果放回ebp-10h地址中,此时k=3
mov ecx,dword ptr [ebp-10h] //把k值复制到ecx中
add ecx,dword ptr [ebp-10h] //计算k+k的值
push ecx //将k+k的值压栈,以传送给printf的形参
听说,这个现象叫做存储器别名使用(memory aliasing)。反正,不要这么用就可以避免了。