从托管.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
相关产品推荐
相关产品推荐

