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

如何在C#中通过P/Invoke调用创建结构体数组的C函数?

从C#调用动态分配结构体数组的C函数的正确方式

首先要明确:你提供的C函数本身存在参数传递错误,无法正确将分配的数组和大小返回给调用方,必须先修正C函数,再进行C#端的调用。

第一步:修正C函数的实现

原函数的核心问题:

  • size = 5 修改的是函数内的指针副本,不会影响调用方传入的变量值,必须改成 *size = 5
  • points = (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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:32:55