C 和 C++ 调用约定有什么区别

What are the differences between C, and C++ calling conventions?

本文关键字:什么 区别 约定 调用 C++      更新时间:2023-10-16

据我所知,调用约定取决于编译器和架构。但是 C 和 C++ 调用约定之间有什么明显的区别吗?

但是 C 和 C++ 调用约定之间有什么明显的区别吗?

一般来说,没有。C++被有意设计为尽可能与C兼容,特别是它在所有系统上使用C应用程序二进制接口。

但是 C ABI 不能满足C++需要的许多功能(特别是重载、命名空间和函数模板),因此C++编译器对函数的名称进行了一些更改。这叫名重整

因此,为了使 C 和 C++ 代码之间的函数调用正常工作,必须将此类函数声明为 extern "C" 禁用名称重整并确保调用约定是 C 所期望的(但我希望后一方面会自动出现,即使标准没有强制要求这样做)。

C++还为成员函数(有时称为thiscall)提供了其他调用约定,这些约定在C中不存在。但是自由函数使用与 C 相同的调用约定,无论给定系统上的调用约定如何。

这两个标准中都没有要求 C 和 C++ 调用约定在给定编译器上相同,除了声明extern "C"必须使用与 C 函数相同的调用约定调用C++

函数。

这就是为什么具有相同参数和返回类型的指向函数的指针到函数和具有 C 链接的指针具有不同的类型。调用函数时,编译器可以从类型中知道使用哪种调用约定调用它(如果它们不同)。

在实践中,我认为我从来没有故意处理过一个在有和没有 C 链接的自由函数之间使用不兼容的调用约定的例子。通常,C++实现采用其计划运行的系统的 ABI 的调用约定,以便生成系统的其他用户可以根据 ABI 理解的可链接对象(可执行文件和库)。

这不是必需的 - 标准不关心是否存在系统ABI,系统通常不关心如何在自包含可执行文件中进行调用[*]。除非有一些特别的理由不这样做,否则这样做是明智的。给定系统上的系统 ABI 可能会也可能不会提及C++ - 如果没有,那么就非 C 链接函数而言,C++实现是独立的,但正如我所说,让可能具有 C 链接的函数使用相同的调用约定通常是明智的,就好像它们确实具有 C 链接一样。

我说"不兼容"而不是"不同",因为当然有些事情在 C 调用约定中没有提到,但需要在C++调用约定中指定。例如,如何传递指向成员函数的指针。这很可能不是由系统ABI确定的,因此留给C++实现。这样做的原因是将指针到成员函数的实现留给C++实现,而不是系统 ABI 执行制造商认为是C++编译器编写者工作的事情。

随着调用约定的消失,请注意,名称重整在有或没有 C 链接的自由函数之间不可避免地是不同的。原因是C++名称重整必须包含参数的类型(因为函数重载),而 C 名称重整不能包含(因为具有未指定参数的 extern 函数声明)。

[*] 虽然我见过使用"错误"调用约定确实会破坏事情的例子。ISTR 一种 Windows 移动设备,如果格式化堆栈的方式与操作系统的预期不同,则某些硬件异常会取出设备,因为操作系统尝试回溯违规线程的堆栈,但无法。因此,至少在该操作系统版本上,可能是在2005年左右,如果您希望操作系统的诊断正常工作,则必须在内部使用Windows调用约定。或者无论如何,调用约定中与堆栈帧格式相关的部分。当然,这完全是我们搞砸了,但我不知道我们是否可以通过为硬件异常安装我们自己的处理程序来正确修复它,而不是通过首先不导致异常来解决这些问题。不过,这确实意味着用户模式进程可能会因堆栈溢出而使操作系统瘫痪,而且调试我们的代码比其他Windows更难。所以我们稍微责怪操作系统。

据我所知,调用约定取决于编译器和架构。

是的。

但是 C 和 C++ 调用约定之间有什么明显的区别吗?

更好的问题是"有什么相似之处吗?

是的:

C++ 应用程序中声明extern "C"的函数具有该体系结构上的 C 函数使用的相同调用约定。

您对调用约定所做的任何其他假设都是推测性的,可能碰巧是相同的,但您需要根据具体情况来担心这一点。

语言之间不同的调用约定的整个概念超出了任何给定语言(及其规范)。这就是它应该的样子,这些事情与任何名副其实的语言的规范无关。基本上,你是对的,它属于规范的特定实现领域 - 编译器架构。当然,在给定语言的规范/标准(如链接规范)中讨论的一些概念可能会影响调用约定,但这不是计划的结果

ABI的职责是标准化/形式化这些概念,再说一次,人们没有义务尊重(这可能是因为实现的丰富多彩的调色板差异很大)

实际上,规范需要忘记参数是如何传递的,无论是最终出现在堆栈还是寄存器上,两者的组合,分配顺序等。这是从事实现工作的人和设计实际硬件的人的工作,更重要的是指令集。

因此:无论是C还是标准化的C++,在如何实现东西方面都没有任何发言权。因此,语言之间一定没有固有的区别。仅以应用它们的方式。这就是编译器体系结构的领域。

没有 C 或C++标准定义 ABI。这是一个有意的设计决策,它允许编译器编写者自由地创建尽可能高效的实现。

随着时间的推移,一种名为 cdecl 的 C 调用约定已成为 x86 架构上的事实标准。在AMD64机器上运行的C代码也有类似的事实标准,但它们在UNIX和Windows操作系统之间有所不同。

在Unix机器上,有一些基于英特尔发布的Itanium C++ ABI的通用C++ ABI。然而,由于C++的额外复杂性,同一编译器的不同版本甚至不同的编译器开关经常产生符合不兼容 ABI 的机器代码。

通常可以依赖extern "C" { ... }的使用来强制编译器使用事实上的C ABI实现函数,但即使这样也不是C++标准所要求的。

如果您对当前C++ ABI的全面描述感兴趣,那么您应该查看Agner Fog的"不同C++编译器和操作系统的调用约定"。