ABI:为什么源码能编译,二进制却不能互相调用?
TL;DR
ABI 是二进制世界的调用合同,规定参数传递、对象布局、符号命名和异常处理。本文区分 ABI 与 API,解释动态库、C++ 兼容性和跨语言绑定为什么不能只看函数签名。
ABI 是二进制世界的调用合同,规定参数传递、对象布局、符号命名和异常处理。本文区分 ABI 与 API,解释动态库、C++ 兼容性和跨语言绑定为什么不能只看函数签名。

源码能编译通过,并不代表两个二进制模块就能互相调用。它们还必须同意一套更底层的规则:参数放在哪里、返回值如何传递、对象怎么布局、函数如何命名、异常怎样跨边界传播。这套规则就是 ABI,Application Binary Interface,应用二进制接口。
ABI 像二进制世界的合同
源代码层面,两个函数都可以叫 process;到了链接阶段,编译器必须把它们变成可区分的符号。C++ 还涉及命名修饰、虚函数表、继承对象布局、构造与析构,以及异常处理。只要编译器、标准库、编译选项或目标平台对这些规则的理解不一致,程序就可能链接失败,或者更危险地,在运行时悄悄读错内存。
C 之所以常被用作跨语言接口,是因为它的 ABI 规则相对简单、稳定、覆盖面广。C++ 的对象模型更复杂,不同编译器之间往往不能随意传递带有复杂布局的类。工程上常见的做法是把边界收窄为 C 风格函数、明确宽度的整数、指针和由调用方约定的内存管理。
ABI 和 API 有什么区别
API 是程序员看到的接口:函数名、参数语义、返回值和文档。ABI 是机器看到的接口:寄存器、栈、符号、布局和调用约定。API 不变,重新编译后 ABI 也可能变化;API 变化很小,ABI 却可能因为结构体增加字段而不兼容。
这也是动态库升级需要谨慎的原因。二进制兼容通常要求旧程序无需重新编译也能继续调用新库。稳定 ABI 的设计,往往会隐藏内部结构、提供不透明句柄,并用版本化函数或能力查询控制演进。
AI 编程为什么值得了解
AI 生成跨语言绑定时,很容易只关注函数签名,却忽略所有权、对齐、异常、线程安全和释放责任。理解 ABI 后,你会知道“能编译”只是第一关,真正要确认的是两边对每一字节的解释是否一致。
读者应该记住
API 面向人,ABI 面向二进制。跨语言调用、插件系统、动态库、操作系统接口和编译器升级,最终都要落到 ABI 这份底层合同上。



