C++调用DLL时传裸指针必须显式传长度,因DLL无法获知数组大小;禁用std::vector等STL类型直接传参以防ABI不兼容;注意const正确性、结构体对齐及CRT一致性。

传递普通数组时必须显式传长度
C++ 调用 DLL 中的函数,如果参数是 int* 或 float* 这类裸指针,DLL 函数本身**完全不知道数组长度**。你不能指望它自动识别 sizeof(arr) —— 那在调用方可能是 24,在 DLL 里就是 8(指针大小)。
常见错误现象:Access violation reading location,或数据截断、越界读写。
实操建议:
- DLL 导出函数签名必须包含长度参数,例如:
extern "C" __declspec(dllexport) void process_data(float* data, int len); - 调用方确保
len是真实有效元素个数,不是sizeof(array);若用std::vector<float></float>,传&vec[0]和vec.size(); - 避免传栈上局部数组地址后函数返回——DLL 可能还在用,而栈内存已失效。
用 std::vector 传参前得先确认 ABI 兼容性
直接把 std::vector<int>&</int> 当参数传进 DLL 几乎必然崩溃,因为 std::vector 的内存布局、分配器、异常处理机制在不同编译器(MSVC / MinGW)、不同运行时(/MD vs /MT)、甚至不同版本之间都不保证一致。
立即学习“C++免费学习笔记(深入)”;
使用场景:仅当 DLL 和 EXE **完全同环境构建**(同 VS 版本、同运行时选项、同架构),且明确不对外发布时,才可谨慎尝试。
实操建议:
- 默认不要传
std::vector、std::string等 STL 类型到 DLL 接口; - 若真要共享容器,改用 COM 接口或跨语言 ABI(如 C 接口 + 内存管理约定);
- 更稳妥的做法:DLL 提供
alloc_buffer和free_buffer两个 C 风格函数,由 DLL 分配/释放内存,调用方只管填数据。
const float* 传入 DLL 时要注意 const 正确性
如果 DLL 函数声明为 void read_only(const float* data, int len),调用方传 const float arr[1024] 没问题,但若传 float arr[1024],也完全合法——C++ 允许非 const 到 const 的隐式转换。
容易踩的坑:
- DLL 内部若对
const float*做了const_cast并修改,属于未定义行为,可能触发写保护异常(尤其在只读页映射下); - 某些打包工具(如 Dependency Walker)会误报 “const 不匹配”,实际是误报,不用管;
- 若 DLL 函数内部确实需要修改数据,就别加
const——接口契约比语法修饰更重要。
结构体数组传参要检查内存对齐和字段偏移
传 MyStruct* 数组进 DLL,如果结构体含 double、__m128 或自定义 #pragma pack,DLL 和调用方的字段偏移可能不一致,导致数据错位、读取乱码。
性能影响:不对齐访问在某些 CPU 上会显著降速,甚至触发 EXCEPTION_DATATYPE_MISALIGNMENT(x86-64 通常容忍,但 ARM 强制对齐)。
实操建议:
- DLL 接口头文件中,用
#pragma pack(push, 8)显式控制对齐,并在两端保持一致; - 用
static_assert(offsetof(MyStruct, field) == 16, "offset mismatch")在编译期校验; - 避免在结构体里混用
bool和uint64_t——不同编译器对bool大小处理不同(1 字节 or 4 字节)。
最常被忽略的是:DLL 和主程序链接的 CRT(C Runtime)是否一致。哪怕只是数组长度传错一个,也可能在 DLL 里引发堆损坏,后续任何 malloc 或 new 都可能崩——这种问题不会立刻报错,而是延后爆发。


















