You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#与C++之间共享int等基础类型数据并解决交互异常?

C#与C++共享基础数据的实现方案

问题故障原因解释

你第二个方案抛出ExecutionEngineException确实是垃圾回收导致的:

  • C#中的值类型如果是类的实例字段,会分配在托管堆中,GC执行压缩时会移动对象的内存地址,你传给C++的指针不会跟着更新,就变成野指针
  • 如果值类型是方法内的局部变量,分配在栈上,方法执行结束后栈空间会被回收,C++持有的指针同样会失效
  • 你直接传ref int到非托管代码时,运行时只会在单次P/Invoke调用期间临时固定变量地址,调用返回后固定就解除了,后续C++再访问该地址必然出错

C#值类型的引用机制

  • 值类型默认存储位置:方法内局部值类型分配在栈,作为引用类型的字段、装箱后的值类型分配在托管堆
  • ref/out是托管指针,由GC跟踪地址变动,仅在托管上下文有效,直接传给非托管代码后GC不会感知非托管侧的持有状态
  • 只有显式固定的内存地址,才可以安全交给非托管代码长期读写
高性能实现方案

不需要依赖Marshal.ReadInt32/Marshal.WriteInt32,通过unsafe上下文直接操作指针即可达到和普通变量几乎一致的性能:

场景1:单次调用C++期间临时共享

使用fixed关键字在调用期间临时固定变量,调用结束后自动解除固定,无内存泄漏风险:

// 首先开启项目的unsafe代码编译选项
[DllImport("你的C++库路径")]
private static extern void 你的C++函数(int* sharedIntPtr);

unsafe
{
    int sharedInt = 0;
    // fixed块内sharedInt的地址不会被GC移动
    fixed (int* p = &sharedInt)
    {
        你的C++函数(p);
        // C#侧直接读写变量即可,不需要任何Marshal操作
        sharedInt = 100;
        Console.WriteLine(*p);
    }
}

场景2:程序生命周期内长期共享

两种可选方案,按需选择:

方案A:固定托管侧变量

int sharedInt = 0;
// 装箱并固定内存,GCHandleType.Pinned标记不会被GC移动
GCHandle handle = GCHandle.Alloc(sharedInt, GCHandleType.Pinned);
IntPtr sharedPtr = handle.AddrOfPinnedObject();
// 把sharedPtr传给C++作为int*使用

// C#侧读写方式(性能和普通变量一致)
unsafe
{
    int* p = (int*)sharedPtr;
    *p = 200;
    Console.WriteLine(*p);
}

// 程序退出前必须释放,否则会内存泄漏
handle.Free();

方案B:非托管内存分配(优化你的第一种方案)

你之前的方案性能差只是因为用了封装层厚的Marshal读写方法,直接转指针操作即可:

// 分配非托管内存,不受GC管理
IntPtr sharedPtr = Marshal.AllocHGlobal(sizeof(int));
// 把sharedPtr传给C++作为int*使用

// C#侧读写方式
unsafe
{
    int* p = (int*)sharedPtr;
    *p = 300;
    Console.WriteLine(*p);
}

// 程序退出前释放内存
Marshal.FreeHGlobal(sharedPtr);
关于包装结构体的说明

如果只需要共享单个int,不需要额外包装结构体。如果后续需要共享多个关联变量,可以定义顺序布局的结构体保证C#/C++内存布局一致:

// C#侧定义
[StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack值和C++对齐设置保持一致
public struct SharedData
{
    public int SharedInt;
    public float SharedFloat;
    public bool SharedFlag;
}
// C++侧对应定义
#pragma pack(push, 4)
struct SharedData
{
    int SharedInt;
    float SharedFloat;
    bool SharedFlag;
};
#pragma pack(pop)

结构体的传递、读写方式和上述单个int的操作完全一致。

内容的提问来源于stack exchange,提问作者directquest

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 00:15:00