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

C# unsafe上下文:固定字符串数组并获取各元素char*指针

在C# unsafe上下文一次性固定字符串数组并获取char*指针的解决方案

为什么你的尝试代码无法工作

你写的fixed (string* ptrArray = arrStrings)是固定了字符串引用数组的内存地址,防止数组本身被GC移动,但数组中存储的只是字符串对象的引用,并非字符数据。ptrArray[1]是第二个字符串的引用,&ptrArray[1]的类型是string**(指向字符串引用的指针),和结构体需要的char*(指向字符数据的指针)类型不匹配,因此无法直接赋值给pszText。

正确实现方案

要一次性完成所有字符串的固定并复用指针,有两种可靠方式:

方案1:使用GCHandle固定字符串对象(推荐)

通过GCHandle将每个字符串对象固定在内存中,确保GC不会移动它们,之后可以安全地获取每个字符串的char*指针并复用。

string[] arrStrings = new string[20];
// 初始化字符串数组
for (int i = 0; i < arrStrings.Length; i++)
{
    arrStrings[i] = $"Item {i}";
}

GCHandle[] pinningHandles = new GCHandle[arrStrings.Length];
char** stringCharPtrs = stackalloc char*[20]; // 栈上分配指针数组,适合小容量场景

try
{
    // 批量固定字符串并保存char*指针
    for (int i = 0; i < arrStrings.Length; i++)
    {
        pinningHandles[i] = GCHandle.Alloc(arrStrings[i], GCHandleType.Pinned);
        stringCharPtrs[i] = (char*)pinningHandles[i].AddrOfPinnedObject();
    }

    // 执行20次操作,直接复用预存的char*指针
    for (int i = 0; i < arrStrings.Length; i++)
    {
        lvItem.pszText = stringCharPtrs[i];
        // 此处执行你的lvItem相关逻辑
    }
}
finally
{
    // 必须释放所有GCHandle,避免内存泄漏
    foreach (var handle in pinningHandles)
    {
        if (handle.IsAllocated)
        {
            handle.Free();
        }
    }
}

方案2:提前批量fixed字符串(适合短期复用)

如果你的操作在同一个方法内完成且不会长时间持有指针,可以批量fixed每个字符串一次,将指针存入数组后复用。注意:fixed块结束后,字符串对象不再被固定,但如果后续操作不会触发GC(比如操作非常快速),指针暂时有效;但更稳妥的还是用方案1。

string[] arrStrings = new string[20];
// 初始化字符串数组
for (int i = 0; i < arrStrings.Length; i++)
{
    arrStrings[i] = $"Item {i}";
}

char** stringCharPtrs = stackalloc char*[20];

// 批量fixed每个字符串并保存指针
for (int i = 0; i < arrStrings.Length; i++)
{
    fixed (char* charPtr = arrStrings[i])
    {
        stringCharPtrs[i] = charPtr;
    }
}

// 执行操作,复用指针
for (int i = 0; i < arrStrings.Length; i++)
{
    lvItem.pszText = stringCharPtrs[i];
    // 执行你的逻辑
}

注意事项

  • 如果字符串是编译时常量(比如直接写的字面量"Hello"),可以直接获取指针而无需fixed/GCHandle,因为它们存储在只读内存中,不会被GC移动。
  • 使用stackalloc分配的内存在方法返回时自动释放,适合小容量数组;如果数组很大,改用Marshal.AllocHGlobal分配堆内存,记得最后用Marshal.FreeHGlobal释放。
  • 必须确保所有GCHandle被释放,否则会造成内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 17:13:28