extern "c" 是跨语言调用唯一可靠起点,所有接口必须经 c abi 中转;参数返回值限 c 兼容类型,结构体需显式定义且无虚函数;rust/go 需配对内存管理函数确保所有权明确。

用 extern "C" 暴露 C 兼容接口是唯一靠谱起点
所有跨语言调用都必须经过 C ABI 这一层中转,C++ 的 name mangling 和异常机制会直接让 Rust/Go 无法识别函数。不加 extern "C",连链接都会失败。
实操建议:
- 只在头文件里用
extern "C"声明函数,不要包裹整个实现文件 - 函数参数和返回值只能是 C 兼容类型:
int、double、const char*、void*;避免std::string、std::vector、引用、重载 - 如果要传结构体,必须用
struct显式定义,并加extern "C"声明,且不能含虚函数、非 POD 成员 - Rust 的
#[no_mangle]和 Go 的//export注释也得配合同步处理
Rust 调用 C++ 时,cc crate 和 bindgen 怎么配合用
bindgen 只负责生成 Rust 的 FFI 绑定声明,它不编译 C++;真正编译和链接靠 cc crate(或手动写 build.rs 调用系统编译器)。两者缺一不可。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
-
bindgen生成的函数签名里出现__0后缀 —— 头文件没用extern "C"包裹,导致 C++ mangling 残留 - Rust 编译报
undefined reference to 'xxx'——cc没正确编译 C++ 源码,或链接了 C++ 标准库(需加-lstdc++或-lc++) - 运行时 panic: “attempted to leave type `
std::string’ uninitialized” —— Rust 尝试 drop 了 C++ 分配但未导出释放逻辑的内存
关键点:C++ 侧必须提供 create_xxx() 和 destroy_xxx() 配对函数,Rust 用 Box::from_raw/Box::into_raw 管理生命周期。
Go 调用 C++ 需绕过 cgo 的 C-only 限制
cgo 只认 C,不支持 C++ 语法。你不能在 /* #include "xxx.h" */ 里直接 include C++ 头文件,也不能在 import "C" 上方写 extern "C" 块。
正确路径只有一条:用一层纯 C 包装胶水代码。
- 新建
cpp_wrapper.h和cpp_wrapper.cpp,里面用extern "C"导出 C 函数,内部调用真正的 C++ 类和方法 - Go 的
/* #include "cpp_wrapper.h" */只引用这个 C 头,不碰任何 C++ 关键字 -
CGO_LDFLAGS必须显式加上-lstdc++(Linux/macOS)或-lc++(macOS Clang),否则链接失败 - Go 中接收
*C.char后,要用C.GoString转换,且不能直接 free —— 释放必须由 C++ 侧提供free_string这类函数
字符串和内存所有权最容易引发静默崩溃
跨语言边界的字符串不是“传过去就能用”,而是谁分配、谁释放、谁编码(UTF-8?locale?)都要提前约定死。一个 const char* 指针背后,可能藏着栈变量、静态缓冲区、堆内存三种生命周期。
实操建议:
- 避免返回局部
std::string.c_str()—— 函数返回后指针悬空 - 如需返回字符串,C++ 侧用
malloc分配,Rust/Go 调用后调用对应free;或改用输出参数(char* buf, size_t buf_len) - Rust 中用
CStr::from_ptr读取前,先确认 C++ 侧以\0结尾;Go 中C.GoString会复制,但若 C++ 返回的是非 null-terminated 字节数组,就得用C.GoStringN - 所有跨语言传递的结构体指针,必须配套
new_XXX/delete_XXX函数,禁止在 Go/Rust 里unsafe自由转换
边界越模糊的地方,越容易在压测或特定输入下突然崩掉——比如某个字符串恰好含 \0,或者某次分配刚好触发了 C++ 侧的 move 语义,而 Rust 还以为自己持有原对象。










