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
相关产品推荐
相关产品推荐

