如何在C#中通过P/Invoke调用创建结构体数组的C函数?
从C#调用动态分配结构体数组的C函数的正确方式
首先要明确:你提供的C函数本身存在参数传递错误,无法正确将分配的数组和大小返回给调用方,必须先修正C函数,再进行C#端的调用。
第一步:修正C函数的实现
原函数的核心问题:
size = 5修改的是函数内的指针副本,不会影响调用方传入的变量值,必须改成*size = 5points = (Points*) malloc(...)修改的是函数内的数组指针副本,调用方的指针不会被更新,必须用二级指针传递数组指针,让函数能直接修改调用方的指针值
修正后的C函数(需补充内存释放函数,避免非托管内存泄漏):
#include <stdlib.h> struct Point { int x, y; }; // 用二级指针接收数组指针,用指针解引用修改size的值 void GetPoints(struct Point** points, int* size) { *size = 5; // 分配内存并赋值给调用方的指针 *points = (struct Point*)malloc(sizeof(struct Point) * *size); // 填充示例数据 for (int i = 0; i < *size; i++) { (*points)[i].x = i; (*points)[i].y = i * 2; } } // 提供释放内存的函数,C#无法直接调用free void FreePoints(struct Point* points) { free(points); }
第二步:C#端的调用实现
1. 定义对应结构体
用StructLayout确保C#结构体的内存布局和C语言完全一致:
using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] public struct Point { public int x; public int y; }
2. 导入C库函数
用DllImport导入编译好的C动态库(替换YourNativeLib.dll为你的实际库文件名):
public static class NativeLib { [DllImport("YourNativeLib.dll")] public static extern void GetPoints(out IntPtr pointsPtr, ref int size); [DllImport("YourNativeLib.dll")] public static extern void FreePoints(IntPtr pointsPtr); }
3. 调用函数并处理内存
调用时用IntPtr接收非托管内存地址,再将其转换为C#结构体数组,使用完成后必须释放非托管内存:
using System; public class Program { public static void Main() { IntPtr pointsPtr = IntPtr.Zero; int arraySize = 0; // 调用C函数获取数组 NativeLib.GetPoints(out pointsPtr, ref arraySize); try { // 将非托管内存转换为C#结构体数组(.NET Core 3.0+/.NET 5+支持批量转换) Point[] points = new Point[arraySize]; Marshal.PtrToStructure(pointsPtr, points); // 示例:遍历输出数组内容 foreach (var p in points) { Console.WriteLine($"Point: x={p.x}, y={p.y}"); } } finally { // 必须释放非托管内存,避免内存泄漏 if (pointsPtr != IntPtr.Zero) { NativeLib.FreePoints(pointsPtr); } } } }
关键注意事项
- 必须通过二级指针传递数组指针,否则C函数无法将分配的内存地址返回给C#
- 非托管内存必须由C函数提供的释放接口来释放,不能在C#中直接处理
- 确保C和C#的结构体内存布局一致(
LayoutKind.Sequential),避免因字段对齐问题导致数据错误
内容的提问来源于stack exchange,提问作者user23637006
相关产品推荐
相关产品推荐

