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

从托管.NET代码调用C函数时如何避免Access violation执行位置错误?

问题分析与解决

核心问题

  • 类型不匹配:.NET中声明的out int[] c无法直接对应C的int** c。托管数组是CLR管理的对象,而C函数需要的是原生指针的地址,直接传递会导致Marshal层处理错误,传递无效的指针地址,最终在malloc时触发访问违规。
  • 返回类型错误:C++函数返回void,但.NET声明成int,这会导致栈平衡问题,即使没直接崩溃也会引发潜在错误。
  • 内存管理问题:C++中用malloc分配的内存无法被.NET自动回收,必须手动调用原生函数释放,否则会造成内存泄漏。

修复步骤

1. 修正C++代码(新增内存释放函数)

原C++函数逻辑没问题,但需新增导出函数用于释放malloc分配的内存,同时修正main函数中内存释放的错误:

#include <stdio.h>
#include <stdlib.h>

extern "C" {
    __declspec(dllexport)
    void addInArray(int** c, int* a, int* b, int size)
    {
        *c = (int*)malloc(sizeof(int) * size);
        for (size_t i = 0; i < size; i++)
        {
            (*c)[i] = a[i] + b[i];
        }
    }

    __declspec(dllexport)
    void freeArray(int* arr)
    {
        free(arr);
    }
}

int main()
{
    int size = 10;
    int* a = new int[size];
    int* b = new int[size];
    int* c;
    for (size_t i = 0; i < size; i++)
    {
        a[i] = rand() % 100;
        b[i] = rand() % 100;
    }
    addInArray(&c, a, b, size);
    for (size_t i = 0; i < size; i++)
    {
        printf("%d = %d + %d\n", c[i], a[i], b[i]);
    }
    delete[] a; // 修正:new[]分配的内存需用delete[]释放
    delete[] b;
    free(c);
}

2. 修正.NET的DllImport声明与调用逻辑

用IntPtr接收C++返回的原生指针,再手动Marshal成托管数组,调用完成后必须释放原生内存:

using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("ArrayCreation.exe", CallingConvention = CallingConvention.Cdecl)]
    public static extern void addInArray(out IntPtr c, int[] a, int[] b, int size);

    [DllImport("ArrayCreation.exe", CallingConvention = CallingConvention.Cdecl)]
    public static extern void freeArray(IntPtr arr);

    static void Main(string[] args)
    {
        int size = 10;
        IntPtr cPtr;
        int[] a = new int[size];
        int[] b = new int[size];
        Random random = new Random();
        for (int i = 0; i < size; i++)
        {
            a[i] = random.Next(100);
            b[i] = random.Next(100);
        }

        // 调用原生函数
        addInArray(out cPtr, a, b, size);

        // 将原生指针转换为托管数组
        int[] c = new int[size];
        Marshal.Copy(cPtr, c, 0, size);

        // 输出结果
        for (int i = 0; i < size; i++)
        {
            Console.WriteLine($"{c[i]} = {a[i]} + {b[i]}");
        }

        // 释放原生分配的内存,避免泄漏
        freeArray(cPtr);
    }
}

关键说明

  • 调用约定:C++中extern "C"默认使用Cdecl调用约定,.NET必须显式指定CallingConvention.Cdecl,否则会导致栈不平衡。
  • 内存安全:原生malloc分配的内存不在CLR托管堆中,必须调用原生freeArray函数释放,否则会造成内存泄漏。
  • 类型转换:通过Marshal.Copy安全复制原生数组内容到托管数组,避免直接操作原生指针的风险。

内容的提问来源于stack exchange,提问作者Michał Okulewicz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 09:46:37