<p>最安全的做法是显式将指针转为 void:传 int 等具体类型易因对齐或大小假设导致崩溃;必须用 static_cast<void*> 而非 C 风格转换;配合 sizeof 传递长度;避免传栈上临时对象地址;const 正确性取决于 C 接口声明;内存生命周期由 C++ 全权管理;结构体需满足 trivially_copyable 且布局兼容。</p>

传 void* 指针给 C 库是最安全的起点
C 语言库函数通常只认 void*,不接受 int*、double* 等具体类型指针。直接传带类型的指针(比如 int*)过去,虽然编译可能通过,但若 C 库内部做了指针算术或强制转换,容易因对齐、大小假设不一致而崩溃。
正确做法是显式转成 void*:
int data = 42; some_c_function(&data); // ❌ C 函数声明可能是 void some_c_function(void* ptr); some_c_function(static_cast<void*>(&data)); // ✅ 显式转换,意图清晰
- 永远用
static_cast<void*>,不用 C 风格(void*)&data—— 后者在模板或 const 场景下易出错 - 如果 C 函数需要原始字节长度(如
memcpy类接口),必须同步传入sizeof(data)或明确字节数,C 端无法推断 - 避免传栈上临时对象地址(如
some_c_function(&std::string("abc")[0])),生命周期不可控
const 正确性必须由 C 接口定义决定
C 库函数是否修改你传入的内存,不能靠 C++ 侧加 const 来“保护”。C 接口声明里写的是 void* ptr 还是 const void* ptr,决定了你能否传 const 指针过去。
例如:
立即学习“C++免费学习笔记(深入)”;
// 假设 C 头文件声明: void process_data(void* buf, size_t len); // 允许修改 void log_data(const void* buf, size_t len); // 承诺不修改
- 对
process_data:即使你传const int* p,也得先去掉 const:const_cast<void*>(static_cast<const void*>(p)),否则编译失败 - 对
log_data:可直接传static_cast<const void*>(p),更安全;若误传非 const 指针,C++ 编译器不会报错,但语义上已失守 - 别指望 C 函数“自觉不改”——它声明为
void*就代表它可能写,没声明const就当它会写
内存生命周期必须由 C++ 侧全权管理
C 库几乎从不接管内存所有权。它拿到 void* 后,只是读写那一片区域,不会 free、不会延长生命周期、也不会复制数据(除非文档明确说它会 deep copy)。
- 不要传局部变量地址给异步 C 回调(如 libuv 的
uv_queue_work),回调执行时栈帧早已销毁 - 堆内存要用
new/malloc分配,并确保 C 函数返回后,C++ 侧仍持有有效指针并负责delete或free - 如果 C 库要求你提供“用户数据指针”(如
user_data参数),且该指针会在回调中被使用,务必保证该指针指向的对象在整个回调生命周期内有效(常见做法:用std::shared_ptr管理,把裸指针传入,回调里再还原)
数组和结构体传递要警惕对齐与填充
C++ 类/结构体传给 C 库前,必须确认它是 trivially_copyable 且布局兼容。尤其注意:std::vector 不是 POD,不能直接传 &vec[0] 给期望连续数组的 C 函数(除非 vec.size() > 0)。
- 数组:用
vec.data()(C++11)或&vec[0](仅当!vec.empty())获取首地址,传static_cast<void*>(vec.data()) - 结构体:加
#pragma pack(1)或alignas可能破坏 ABI;优先用static_assert(std::is_standard_layout_v<MyStruct>)+static_assert(std::is_trivially_copyable_v<MyStruct>)做编译期检查 - 跨语言结构体字段顺序、padding、整数符号(
intvsuint32_t)必须严格一致;建议 C 头里用stdint.h类型,C++ 侧用<cstdint>对应类型
最常被忽略的是:C 库文档里写的“pointer to struct”往往隐含“该 struct 必须按 C ABI 布局”,而 C++ 编译器对空基类优化、虚函数表等会悄悄改变布局。真要传复杂结构,先用 offsetof 和 sizeof 打印验证,别信直觉。


















