C语言没有定义ABI。事实上,它竭尽全力避免定义ABI。和我一样花费了大部分时间在8位字节、二进制补码算术和平坦地址空间下使用C语言编程的人们,在阅读当前C标准中复杂的语言时通常会感到惊讶。
例如,看看有关指针的内容。标准并没有像“指针是一个地址”这样简单地说明,因为那样会对ABI进行假设。特别是,它允许指针存在于不同的地址空间并具有不同的宽度。
ABI是一种从语言的执行模型映射到特定机器/操作系统/编译器组合的方式。在语言规范中定义它是没有意义的,因为那会导致某些架构上的C实现被排除在外。
原则上,C语言没有标准的ABI,但在实践中,这很少有影响:你需要做你的操作系统供应商所做的事情。
以x86 Windows上的调用约定为例,Windows API使用所谓的“标准”调用约定(stdcall)。因此,任何想要与操作系统进行接口编程的编译器都需要实现它。然而,stdcall不支持所有的C90语言特性(如调用没有原型的函数、可变参数函数等)。由于微软提供了一个C编译器,第二个调用约定是必要的,称为“C”调用约定(cdecl)。大多数在Windows上的C编译器将其作为默认的调用约定,因此是可互操作的。
原则上,C++也可能出现同样的情况,但由于C++ ABI(包括调用约定)必须更加复杂,编译器供应商并未就单一的ABI达成共识,但仍然可以通过回退到“extern “C””来实现互操作性。
C的ABI是与平台相关的 - 它涵盖了诸如寄存器分配和调用约定等明显特定于特定处理器的问题。以下是一些示例:
x86有许多调用约定,在Windows下扩展声明使用哪一个。嵌入式Linux的平台ABI也随着时间的推移而改变,导致不兼容的用户空间。请参见此处 ARM Linux端口的一些历史记录,显示了转换到新ABI中的问题。
C语言也不行,对吧?
对
在任何给定的平台上,C语言几乎都可以使用。如果缺少一种语言作为跨语言通信的共通语言,则将无法使用。
几乎可能指的是由C编译器供应商选择的特定体系结构默认值被用于其他语言中。因此,如果Keil的ARM C编译器将使用从左到右的小端参数排序和堆栈传递参数以及一些预定寄存器来返回值,则来自其他编译器的extern "C"将假定与该方案兼容。
虽然这样的协议可能被认为是ABI的一部分,但与JVM浏览器沙箱等托管执行上下文不同,它本身远非完整的标准ABI。
<stdarg.h>
使得大多数程序不需要依赖这种约定,但它仍然被使用了很多年,因为它简单并且效果还不错。虽然没有“官方”文件将其确立为跨平台“标准”,但大多数针对栈向下增长的机器的编译器都是这样工作的,导致比今天存在更高水平的一致性。C语言没有标准的ABI。这可以很容易地说明所有使用的调用约定(cdecl、fastcall和stdcall)。每种约定都是不同的ABI。
extern "C"
来编写C ++,和/或使用相同的编译器对链接的可执行文件进行编译。 - Steve Jessop