C和C ++可执行文件之间的区别?

时间:2013-02-03 21:41:41

标签: c++ c executable binaries

我知道C和C ++程序的源代码存在差异 - 这是我所要求的。

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

我正在自学C ++,我看过很多可以被两种语言使用的库。这让我开始思考 - 两种语言的二进制可执行文件之间是否存在显着差异?

对于两者都容易使用的库,我认为它们必须在可执行级别上相似。

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

5 个答案:

答案 0 :(得分:6)

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

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

答案 1 :(得分:4)

可执行文件是一个可执行文件,无论它是用什么语言编写的。如果它是为目标体系结构构建的,它将在体系结构上运行。

(可以说)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 ++中正确链接

答案 2 :(得分:2)

我可以通过阅读反汇编的二进制代码[对于我熟悉的处理器架构,x86,x86_64和ARM]来判断某些东西是C ++还是C。但实际上,没有太大区别,你必须要非常难以确定。

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

只看二进制文件[除非你能“在头脑中进行反汇编”将会更加困难 - 特别是如果它被剥去了符号 - 那就像那个可以告诉你古典音乐在老式乙烯基上是什么的人通过查看[隐藏标签]的曲目记录 - 不是大多数人可以做的事情,即使他们“很好”。

答案 3 :(得分:1)

实际上,C程序(或C ++程序)很少只是纯粹的标准C(或C ++)(例如,C99标准没有意义来扫描目录)。所以程序使用额外的库。

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

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

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

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

答案 4 :(得分:1)

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

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

您可以使用这样的块让C ++编译器生成与C:

兼容的代码
extern "C" {
    /* code */
}