【问题标题】:Why does compiling this code as C and as C++ code generate different assembly?为什么将此代码编译为 C 和 C++ 代码会生成不同的程序集?
【发布时间】:2013-11-18 08:43:26
【问题描述】:

我写了以下代码:

int main()
{
    int i;
    int arr[4];
    for (i = 0; i < 4; i++)
        arr[i] = 0;

    return 0;
}

将其保存在两个文件中:test.c 和 test.cpp
我运行以下命令:

gcc -O0 test.c
objdump -Mintel -d a.out > decompilec
g++ -O0 test.cpp
objdump -Mintel -d a.out > decompilecpp

我将 decompilec 和 decompilecpp 都编辑为只包含 main 函数。

现在,我运行 diff decompilec decompilecpp 并得到以下输出:

12,21c12,19
<  80483fe: 7e eb                   jle    80483eb <main+0xf>
<  8048400: b8 00 00 00 00          mov    eax,0x0
<  8048405: c9                      leave  
<  8048406: c3                      ret    
<  8048407: 66 90                   xchg   ax,ax
<  8048409: 66 90                   xchg   ax,ax
<  804840b: 66 90                   xchg   ax,ax
<  804840d: 66 90                   xchg   ax,ax
<  804840f: 90                      nop
< 
---
>  80483fe: 0f 9e c0                setle  al
>  8048401: 84 c0                   test   al,al
>  8048403: 75 e6                   jne    80483eb <main+0xf>
>  8048405: b8 00 00 00 00          mov    eax,0x0
>  804840a: c9                      leave  
>  804840b: c3                      ret    
>  804840c: 66 90                   xchg   ax,ax
>  804840e: 66 90                   xchg   ax,ax

任何机构都可以解释这种差异吗?

这里是文件:

反编译

080483dc <main>:
 80483dc:   55                      push   ebp
 80483dd:   89 e5                   mov    ebp,esp
 80483df:   83 ec 20                sub    esp,0x20
 80483e2:   c7 45 ec 00 00 00 00    mov    DWORD PTR [ebp-0x14],0x0
 80483e9:   eb 0f                   jmp    80483fa <main+0x1e>
 80483eb:   8b 45 ec                mov    eax,DWORD PTR [ebp-0x14]
 80483ee:   c7 44 85 f0 00 00 00    mov    DWORD PTR [ebp+eax*4-0x10],0x0
 80483f5:   00 
 80483f6:   83 45 ec 01             add    DWORD PTR [ebp-0x14],0x1
 80483fa:   83 7d ec 03             cmp    DWORD PTR [ebp-0x14],0x3
 80483fe:   7e eb                   jle    80483eb <main+0xf>
 8048400:   b8 00 00 00 00          mov    eax,0x0
 8048405:   c9                      leave  
 8048406:   c3                      ret    
 8048407:   66 90                   xchg   ax,ax
 8048409:   66 90                   xchg   ax,ax
 804840b:   66 90                   xchg   ax,ax
 804840d:   66 90                   xchg   ax,ax
 804840f:   90                      nop

反编译cpp

080483dc <main>:
 80483dc:   55                      push   ebp
 80483dd:   89 e5                   mov    ebp,esp
 80483df:   83 ec 20                sub    esp,0x20
 80483e2:   c7 45 ec 00 00 00 00    mov    DWORD PTR [ebp-0x14],0x0
 80483e9:   eb 0f                   jmp    80483fa <main+0x1e>
 80483eb:   8b 45 ec                mov    eax,DWORD PTR [ebp-0x14]
 80483ee:   c7 44 85 f0 00 00 00    mov    DWORD PTR [ebp+eax*4-0x10],0x0
 80483f5:   00 
 80483f6:   83 45 ec 01             add    DWORD PTR [ebp-0x14],0x1
 80483fa:   83 7d ec 03             cmp    DWORD PTR [ebp-0x14],0x3
 80483fe:   0f 9e c0                setle  al
 8048401:   84 c0                   test   al,al
 8048403:   75 e6                   jne    80483eb <main+0xf>
 8048405:   b8 00 00 00 00          mov    eax,0x0
 804840a:   c9                      leave  
 804840b:   c3                      ret    
 804840c:   66 90                   xchg   ax,ax
 804840e:   66 90                   xchg   ax,ax

根据要求,这是gcc -v的输出:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.7/lto-wrapper
Target: i686-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.2-2ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
Thread model: posix
gcc version 4.7.2 (Ubuntu/Linaro 4.7.2-2ubuntu1) 

【问题讨论】:

  • 那么您希望 C 和 C++ 生成 相同 机器指令吗?如果是这样,为什么?机器指令必须是等价的,而不是完全相同的。 所以问题是,它们是否等效?。但是,你知道足够的汇编来理解它们吗?
  • 我不敢相信一年半前还有一个几乎相同的问题:stackoverflow.com/questions/9338164/…
  • 这似乎是一个完全合理的问题,其中包含足够的代码来重现问题和遇到的具体问题。我希望它重新开放!
  • 另外,这个 GCC 错误报告(已修复)可能与此有关:gcc.gnu.org/bugzilla/show_bug.cgi?id=42027
  • 伙计们,你们抽过什么东西吗?这是一个零初始化循环,具有恒定的迭代次数(最好是在一个不使用的变量上,然后立即超出范围)。打开-O1 几乎肯定会去掉整个代码并将程序简化为int main(){return 0;},无论使用什么编译器或它可能有什么错误。甚至尝试从一个空的main 函数中预测某些东西有什么意义? :-)

标签: c++ c disassembly


【解决方案1】:

您可以通过多种方式使用汇编程序执行某些特定操作。尤其是当您进行任何可能密切依赖于目标 CPU 的优化时,汇编器可能有不明显的操作,这些操作基本上是为了使特定 CPU 工作得更快。即使你有相同的编译器和相同的优化标志,实际使用的编译器对于 C 和 C++ 仍然是不同的,因此它们可能会产生不同的代码。

C 程序集应该简单明了。 C++ 版本更有趣。 this link 解释了那里发生的事情。基本上,他们将 C 代码的直接“小于或等于跳转”转换为“根据小于或等于设置为零或非零,然后根据零或非零跳转”。

我不知道为什么 C++ 编译器会生成这样的代码。它可能会随着更高的优化级别而改变,或者甚至会在优化级别 0 时产生,因为它有助于默认 CPU 的分支预测在大多数情况下产生正确的预测。然后 C 编译器具有不同的默认行为。正如我已经说过的,实际的编译器是不同的,所以我实际上有点惊讶代码是相同的!

【讨论】:

    【解决方案2】:

    不同的语言使用不同的编译器前端,这可能会产生不同的指令流(在其他方面具有等效的语义效果)。

    如果您正在优化输出,则优化器很可能(尽管不能保证)将两个指令流减少到相同的输出......但不能保证,我不明白您为什么会期望任何。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-02
      • 1970-01-01
      • 2011-08-05
      • 1970-01-01
      • 2012-01-03
      • 2014-08-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多