结构体字段对齐不一致是互操作最常见坑:C++默认8字节对齐,C#需显式指定Pack值(如Pack=1)并与C++的#pragma pack一致,且字段顺序必须完全相同。

结构体字段对齐不一致导致读写错位
这是最常踩的坑:C++ 侧 struct 默认按 8 字节对齐,而 C# 的 StructLayout 默认是 LayoutKind.Sequential 但不对齐方式做约束,结果字段偏移错乱,Marshal.PtrToStructure 解出来的值全是垃圾。
必须显式指定对齐方式,且和 C++ 编译器保持一致(常见为 #pragma pack(1) 或 __declspec(align(4))):
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyData
{
public byte flag;
public int value; // offset = 1, not 4
public double time; // offset = 5, not 8
}
- 若 C++ 用
#pragma pack(4),C# 就得写Pack = 4 - 字段顺序必须完全一致,不能靠
FieldOffset手动调——容易漏改一处就全崩 - 含指针或句柄的字段(如
IntPtr),别用unsafe直接映射成int*,否则跨平台或 x64/x86 切换时出错
传递指针参数时忘记用 ref/out 或固定内存
调用 C++ 函数形如 void process_data(MyData* p),在 C# 中如果只传结构体变量,实际上传的是副本地址,C++ 修改后 C# 看不到变化;更糟的是,若结构体含引用类型(如 string),直接传会触发 GC 移动内存,C++ 拿到野指针。
分两种情况处理:
- 输入输出都走栈:用
ref(对应 C++ 非 const 指针),确保结构体是 blittable 类型 - 需长期持有或含非 blittable 字段(如字符串):用
GCHandle.Alloc(obj, GCHandleType.Pinned)固定内存,并传handle.AddrOfPinnedObject() - 千万别对局部结构体变量用
&myStruct再转IntPtr——栈地址函数返回后即失效
char* / const char* 在 C# 中该用 string 还是 IntPtr?
取决于 C++ 函数是否修改内容、是否需要释放内存、以及字符串来源。
- C++ 接收
const char*仅读取:C# 用string参数,P/Invoke 自动转 UTF-8(需加[MarshalAs(UnmanagedType.LPStr)]) - C++ 返回新分配的
char*(如char* get_message()):C# 必须用IntPtr接收,再用Marshal.PtrToStringAnsi(ptr)转字符串,最后调 C++ 提供的free_message(ptr)释放——别依赖 GC - C++ 写入你提供的缓冲区(如
void fill_name(char* buf, int len)):C# 传StringBuilder,并设Capacity,P/Invoke 自动处理可写内存
回调函数传入 C++ 后被 GC 回收
注册 C++ 回调(如 set_callback(void (*cb)(int)))时,C# 传一个委托,但若没强引用,GC 可能在任意时刻回收它,C++ 再调用就 crash。
必须把委托存为类字段(避免被回收),且用 Marshal.GetFunctionPointerForDelegate 转函数指针:
private MyCallbackDelegate _callback;
private IntPtr _callbackPtr;
<p>public void Register()
{
_callback = (val) => { Console.WriteLine(val); };
_callbackPtr = Marshal.GetFunctionPointerForDelegate(_callback);
set_callback(_callbackPtr);
}</p>
- 委托签名必须与 C++ 函数指针严格一致(
CallingConvention.Cdecl常见,别用默认StdCall) - 不要在回调里调用托管堆分配操作(如 new、LINQ),容易触发 GC 或死锁
- 卸载 DLL 前记得清空回调,有些 C++ 库会在析构时尝试调用已失效的指针
底层互操作真正难的不是语法,而是内存生命周期的归属权——谁分配、谁释放、谁固定、谁 pin,一旦错配,表现就是随机 crash 或数据错乱,调试时往往看不出关联。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











