C++ 反汇编初探

来源:互联网 发布:手机淘宝联盟没有提现 编辑:程序博客网 时间:2024/05/21 12:47

因为想到昨天一道笔试题:


    char *s = "Apple";
    cout <<s+2;

记得算术运算符 + 号比移位运算符 << 的优先级高,可是当时笔试时脑袋很乱,以为会输出'A'+2 ...剩下的就不说了。上机调试了一下,应该是 指针s+2,然后cout 打印 字符串。这么说。


由此在网上看到一个关于运算符++、--优先级的问题(现在看来,依靠未定义的运算顺序来计算是无意义的),先看一题:

   int i = 1;
   cout << i++ << "," << i-- <<endl;

输出结果 0,1?为什么。我最开始认为按后缀运算符计算方法,起码也该是 i++ 返回 1(此后i=2)、i-- 返回2(此后i=1),但是结果显然和我预料的不一样。此处在 CSDN 的bbs帖子往楼下扫视,得知 cout 是对象,<< 是与它相关的运算符重载函数, “函数”两个字很重要,它表明 ① 一般参数从右至左求值(求值顺序编译器相关,不要依赖这样的求值顺序); ② 函数有返回值。cout<< 的返回值是 (*this),也就是调用者 cout, 这使得运算符 << 像 = 一样可以被连续使用。写成标准的函数调用显示更明显 cout.operator<<(i++).operator<<(i--),与赋值运算符一样,都是从左至右的调用(求值)顺序,那么先用 i=1计算 i-- (返回值为1,但 i 减为0)传参, 然后用 i=0 计算 i++(返回值0)。


再看一题:

    int i=3;
    cout << i++ * ++i + i-- * --i;

由于运算符本身的优先级,使得:

         i++ * ++i + i-- * --i 完全等价于 ( (i++)*(++i))+((i--)*(--i))

我个人认为是 3*5 + 5*3 =30(从左至右求值),于是上机调试,vs 2008 给的结果是 18,也就是 3*3 + 3*3 =18(先求前缀表达式的值++i、--i,则i增1减1不变,整个表达式的值求完后,再求后缀表达式的值),而在 codeblocks 下调试(MinGW gdb 调试器)输出的是 30 。当然后来在 VC++ 6下调试是25, Linux 32 位也是25。

VS 2008 反汇编:

     ret = i++ * ++i + i-- * --i;
004113B5  mov         eax,dword ptr [i]
004113B8  add         eax,1
004113BB  mov         dword ptr [i],eax
004113BE  mov         ecx,dword ptr [i]
004113C1  sub         ecx,1
004113C4  mov         dword ptr [i],ecx
004113C7  mov         edx,dword ptr [i]
004113CA  imul        edx,dword ptr [i]
004113CE  mov         eax,dword ptr [i]
004113D1  imul        eax,dword ptr [i]
004113D5  add         eax,edx
004113D7  mov         dword ptr [ret],eax
004113DA  mov         ecx,dword ptr [i]
004113DD  sub         ecx,1
004113E0  mov         dword ptr [i],ecx
004113E3  mov         edx,dword ptr [i]
004113E6  add         edx,1
004113E9  mov         dword ptr [i],edx

 VC++ 6 汇编:

        ret = i++ * ++i + i-- * --i;
0040156F   mov         eax,dword ptr [ebp-8]
00401572   add         eax,1
00401575   mov         dword ptr [ebp-8],eax
00401578   mov         ecx,dword ptr [ebp-8]
0040157B   imul        ecx,dword ptr [ebp-8]
0040157F   mov         edx,dword ptr [ebp-8]
00401582   sub         edx,1
00401585   mov         dword ptr [ebp-8],edx
00401588   mov         eax,dword ptr [ebp-8]
0040158B   imul        eax,dword ptr [ebp-8]
0040158F   add         ecx,eax
00401591   mov         dword ptr [ebp-4],ecx
00401594   mov         ecx,dword ptr [ebp-8]
00401597   sub         ecx,1
0040159A   mov         dword ptr [ebp-8],ecx
0040159D   mov         edx,dword ptr [ebp-8]
004015A0   add         edx,1
004015A3   mov         dword ptr [ebp-8],edx

 

  codeblocks:

 ret = i++ * ++i + i-- * --i;

0x0040135D    mov    -0xc(%ebp),%eax
0x00401360    lea    0x1(%eax),%edx
0x00401363    mov    %edx,-0xc(%ebp)
0x00401366    addl   $0x1,-0xc(%ebp)
0x0040136A    imul   -0xc(%ebp),%eax
0x0040136E    mov    %eax,%ecx
0x00401370    mov    -0xc(%ebp),%eax
0x00401373    lea    -0x1(%eax),%edx
0x00401376    mov    %edx,-0xc(%ebp)
0x00401379    subl   $0x1,-0xc(%ebp)
0x0040137D    imul   -0xc(%ebp),%eax
0x00401381    add    %ecx,%eax
0x00401383    mov    %eax,-0x10(%ebp)


*gcc 编译器优化的汇编代码里没有继续处理后续的 i++、i--,可能是因为编译器检测到此条语句以后 i 没有再被使用,所有没有后续计算。

值得一提的是,codeblocks 用的是 GCC 编译器,GCC编译器是 Unix/Linux 标准,使用的汇编语言语法格式与Intel的手册有很大不同,Unix/Linux采用AT&T汇编格式作为汇编语言的语法格式。
         如果想了解AT&T汇编可以参考文章:Linux AT&T 汇编语言开发指南

0 0