【问题标题】:Difference Between C and C++ Executables?C 和 C++ 可执行文件之间的区别?
【发布时间】:2013-02-03 21:41:40
【问题描述】:

我知道 C 和 C++ 程序之间的源代码存在差异 - 这不是我要问的。

我也知道这会因 CPU 和操作系统而异,具体取决于编译器。

我正在自学 C++,并且我看到了许多对两种语言都可以使用的库的引用。这让我开始思考——两种语言的二进制可执行文件之间是否存在显着差异?

为了让两者都能轻松使用库,我认为它们必须在可执行级别上相似。

在很多情况下,人们可以检查可执行文件并判断它是由 C 还是 C++ 源代码创建的?或者二进制文件会非常相似吗?

【问题讨论】:

  • 您真正想问的是,任何一种语言的编译器如何导出函数(以及更普遍的符号)...
  • @Tango:二进制文件之间没有真正的区别;它们都是本机代码,以相同的格式存储。
  • 可执行文件与用于创建它的语言没有任何关系。如果你有两个程序,一个用 C 语言,另一个用 C++ 语言,它们都做完全相同的事情,那么它们的可执行文件没有理由不能完全相同。然而,编译器往往会留下一些东西,使可执行文件可以识别为是用某种语言编写的——但这根本没有必要。
  • 一般来说,这些人是正确的。但是,C 和 C++ 都不需要编译;相反,它们很可能被解释。解释只是另一种翻译方法,它转化为行为,而不是另一种编程语言。
  • 您正在寻找的是 ABI。一个快速的谷歌会告诉你你需要知道的一切。

标签: c++ c executable binaries


【解决方案1】:

在大多数情况下,是的,这很容易。以下只是我经常看到的一些线索,足以轻松记住它们:

  1. C++ 程序通常会以至少几个可见的符号被破坏而告终。
  2. C++ 程序通常会有至少几个对虚函数的调用,这通常与您在 C 中看到的代码截然不同。
  3. 许多 C++ 编译器实现了 C++ 调用约定,特别考虑将 this 指针传递给 C++ 成员函数。同样,由于 this 指针在 C 中根本不存在,因此您很少会看到直接模拟(尽管在某些情况下,它们将使用相同的约定来传递一些其他指针,因此您需要注意这一点一)。

【讨论】:

    【解决方案2】:

    一个可执行文件就是一个可执行文件,不管它是用什么语言编写的。如果它是为目标架构构建的,它将在该架构上运行。

    C 和 C++ 编译代码之间(可以说)最重要的区别,以及与可以针对 C 和 C++ 可执行文件链接的库相关的区别在于 name mangling. 基本上:编译库时,它会导出一组符号(函数名、导出的变量等),链接到该库的可执行文件可以使用这些符号。这些符号的命名方式是相当特定于编译器/链接器的,如果使用不兼容约定的链接器链接后续可执行文件,则符号将无法正确解析。此外,C 和 C++ 的约定略有不同。上面链接的维基百科文章有更多细节;可以这么说,在头文件中声明导出符号时,您通常会看到如下结构:

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /* exported declarations here */
    
    #ifdef __cplusplus
    }
    #endif
    

    __cplusplus 是仅在编译 C++ 代码时定义的预处理器宏。这里的想法是,当在 C++ 中使用标头时,编译器被指示使用 C 命名导出符号的方式(在“extern "C" { /* foo */ }”块内,因此库可以在 C 和 C++ 中正确链接。

    【讨论】:

    • 另外,探戈不是交际舞! :-P
    【解决方案3】:

    在实践中,C 程序(或 C++ 程序)很少只是纯标准 C(或 C++)(例如 C99 标准没有扫描目录的意思)。所以程序使用额外的库。

    在 Linux 上,大多数二进制文件都是动态链接的。使用ldd 命令查找。

    如果二进制文件链接到 stdc++ 库,则源代码可能是 C++。

    如果只链接libc.so库,源代码可能只有C(但您可以静态链接libstdc++.a库)。

    您还可以使用处理二进制文件的工具(例如,objdumpreadelfstringsnm 在 Linux 上...)来查找有关它们的更多信息。

    【讨论】:

      【解决方案4】:

      C 和 C++ 编译器生成的代码通常是相同的代码。有两个重要的区别:

      • 名称修改:每个函数和全局变量在编译时变成一个符号。在 C 中,这些符号的名称与源代码中的名称相同。在 C++ 中,它们被稍微修改以允许多态代码
      • 调用约定:如果在 C++ 中调用方法,this 指针将作为隐藏的第一个参数传递。其他约定也可能不同,例如 C 中不存在的引用调用

      您可以使用这样的块来让 C++ 编译器生成与 C 兼容的代码:

      extern "C" {
          /* code */
      }
      

      【讨论】:

        【解决方案5】:

        认为我可以通过读取反汇编的二进制代码来判断是 C++ 还是 C [对于我熟悉的处理器架构,x86、x86_64 和 ARM]。但实际上,没有太大区别,您必须看起来很难确定。

        要寻找的迹象是“间接调用”(function pointer calls via a table)和this-指针。尽管 C 可以有 pointer to struct 参数并且经常使用函数指针,但它通常不像 C++ 那样设置。此外,您有时会注意到,编译器采用指向结构的指针并添加一个小的偏移量——这将删除继承类的外层。这也可以在 C 中发生,但不会那么普遍/独特。

        只看二进制文件 [除非你可以“在头脑中进行反汇编”会困难得多——尤其是如果它被剥离了符号——这就像那个可以告诉你旧黑胶唱片上的古典音乐是什么的人通过查看曲目进行记录[隐藏标签] - 大多数人都无法做到这一点,即使它们“很好”。

        【讨论】:

          猜你喜欢
          • 2013-05-16
          • 2012-04-07
          • 2011-01-29
          • 1970-01-01
          • 2019-04-08
          • 1970-01-01
          • 2010-11-08
          • 1970-01-01
          相关资源
          最近更新 更多