据我所知,调用约定依赖于编译器和体系结构。但C和C ++调用约定之间是否存在明显差异?
答案 0 :(得分:10)
但C和C ++调用约定之间是否存在明显差异?
一般来说,没有。 C ++被有意设计为尽可能与C兼容,特别是它在所有系统上使用C应用程序二进制接口。
但是C ABI不能满足C ++需要的许多功能(特别是重载,命名空间和函数模板),因此C ++编译器对函数名称进行了一些更改。这称为name mangling
因此,为了使C和C ++代码之间的函数调用起作用,必须将这些函数声明为extern "C"
,这将禁用名称修改并确保调用约定是C所期望的那些(但我希望这样后一方面自动出现,即使标准没有强制要求这样做。)
C ++还有成员函数(有时称为thiscall)的附加调用约定,它们在C中不存在。但是自由函数使用与C相同的调用约定,无论哪个是在给定的系统上。
答案 1 :(得分:6)
在任何标准中都没有要求C和C ++调用约定在给定编译器上是相同的,除了必须使用与C相同的调用约定调用声明为extern "C"
的C ++函数功能
这就是为什么指向函数的指针和带有相同参数和返回类型的带C指令的指针具有不同的类型。当调用该函数时,编译器可以从类型中知道调用它的调用约定,如果它们不同。
在实践中,我不认为我曾经故意处理过使用带有和不带C链接的自由函数之间不兼容的调用约定的示例。通常,C ++实现从其计划运行的系统的ABI采用其调用约定,以便生成系统的其他用户可以根据ABI理解的可链接对象(可执行文件和库)。
这不是必需的 - 标准不关心是否存在系统ABI,并且系统通常不关心如何在自包含的可执行文件[*]内进行调用。除非有一些特殊的理由不这样做,否则这样做是明智的。给定系统上的系统ABI可能会也可能不会提到C ++ - 如果没有,那么就非C语言链接函数而言,C ++实现是独立的,但正如我所说,使函数< em> can 让C链接使用相同的调用约定,就像它们确实有C链接一样。
我说“不兼容”而不是“不同”,因为当然有一些事情在C调用约定中没有提到但需要在C ++调用约定中指定。例如,如何传递指向成员函数的指针。很可能这是不被系统ABI固定下来,因此留给C ++实现。这样做的原因是将指向成员函数的指针实现到C ++实现,而不是系统ABI执行制造商认为是C ++编译器编写器的工作。
在调用约定完成之后,请注意,在具有或不具有C链接的自由函数之间,名称修改不可避免地存在差异。原因是C ++名称修改必须包含参数的类型(因为函数重载),而C名称修改不能(因为extern函数声明带有未指定的参数)。
[*]虽然我已经看过使用“错误”调用约定会破坏事物的例子。 ISTR是Windows移动设备,如果您将堆栈格式化为与操作系统预期的不同,那么某些硬件异常会占用设备,因为操作系统试图回溯违规线程的堆栈,而不能。所以至少在操作系统版本上,可能在2005年左右,如果你想要操作系统的诊断工作,那么你必须在内部使用Windows调用约定。或者无论如何,调用约定的部分与堆栈帧格式有关。当然,这完全是我们的搞砸了,但我不知道我们是否可以通过为硬件异常安装我们自己的处理程序来正确修复它,而不是通过首先不引起异常来解决它们。这确实意味着用户模式进程可以通过堆栈溢出来简单地降低操作系统,并且调试代码比在其他Window上更难。所以我们稍微指责了操作系统。
答案 2 :(得分:4)
据我所知,调用约定依赖于编译器和体系结构。
是
但C和C ++调用约定之间是否存在明显差异?
更好的问题是“有任何相似之处吗?”
是:
C ++应用程序中声明为extern "C"
的函数具有与该体系结构上的C函数相同的调用约定。
您对调用约定做出的任何其他假设都是推测性的,可能恰好相同,但您需要根据具体情况来担心。
答案 3 :(得分:3)
语言之间不同调用约定的整个概念超出了任何给定的语言(及其规范)。这应该是这样的,这些事情与任何值得它名字的语言的规范无关。基本上,你是对的,它是在规范的特定实现的领域 - 编译器体系结构。当然,在链接规范等给定语言的规范/标准中讨论的一些概念会影响调用约定,但它不是计划的结果。
ABI的职责是使这些概念标准化/形式化,然后人们没有义务尊重(并且可能因为丰富的实施方案变化很大)
实际上,规范需要忽略参数的传递方式,最后是堆栈或寄存器,两者的组合,分配的顺序等。这是从事实现的人员的工作和设计实际硬件的人,更重要的是指令集。
因此:标准化的C和C ++都没有任何关于如何实现内容的发言权。因此,语言必然没有区别。只是在他们应用的方式。这就是编译器架构的领域。
答案 4 :(得分:2)
没有C或C ++标准定义ABI。这是一个有意的设计决策,它允许编译器编写者自由地创建尽可能高效的实现。
随着时间的推移,名为cdecl的C调用约定已成为x86体系结构的事实标准。对于在AMD64机器上运行的C代码,存在类似的事实standards,但它们在UNIX和Windows操作系统之间有所不同。
在Unix机器上,有一些基于英特尔发布的Itanium C ++ ABI的通用C ++ ABI。但是,由于C ++的额外复杂性,同一编译器的不同versions甚至different compiler switches通常会生成符合不兼容ABI的机器代码。
通常可以依赖extern "C" { ... }
的使用来强制编译器使用事实上的C ABI来实现一个函数,但即使是C ++标准也不需要这样做。
如果您对当前C ++ ABI的全面描述感兴趣,那么您应该看看Agner Fog的"Calling conventions for different C++ compilers and operating systems"。