删除 C# 不安全指针
Posted
技术标签:
【中文标题】删除 C# 不安全指针【英文标题】:Deleting C# Unsafe Pointers 【发布时间】:2013-08-12 22:30:01 【问题描述】:我知道在 C# 中使用 /unsafe
标志,您可以使用指针。在 C/C++ 中,要删除指针,您将分别使用 free(pointer);
和 delete pointer;
。但是,如何使用 C# 指针实现相同的效果?
【问题讨论】:
【参考方案1】:这取决于。您使用free
和delete
来释放由malloc
和new
分配的内存。
但是
一般来说,如果您进行 PInvoke 调用,那么指针应该是 IntPtr
。
如果使用fixed
(或GCHandle
)获取托管对象的指针,则内存是从GC内存中分配的
fixed
块,或释放 GCHandle
),GC 将返回处理它
对于通过 .NET Marshal
方法分配的内存,您可以使用互补的 Free
方法
对于从本地方法接收的内存,您必须使用“正确的”本地方法来释放它。
.NET 接收到的固定内存示例:
int[] arr = new int[5];
fixed (int* p = arr)
// here arr is fixed in place and it won't be freed/moved by gc
// here arr is un-fixed and the GC will manage it
或者,几乎等效(但安全性稍差,因为取消固定是手动完成的)
GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
int* p2 = (int*)handle.AddrOfPinnedObject();
// here arr is fixed in place and it won't be freed/moved by gc
handle.Free();
// here arr is un-fixed and the GC will manage it
使用Marshal.AllocCoTaskMem
(注意Marshal.AllocCoTaskMem
调用Windows API的CoTaskMemAlloc
,所以你可以同时使用@ 987654337@ 和 Windows API CoTaskMemFree
释放它):
// allocating space for 1000 chars
char* p3 = (char*)Marshal.AllocCoTaskMem(1000 * sizeof(char));
// here you can use p3
// and here you free it
Marshal.FreeCoTaskMem((IntPtr)p3);
或使用Marshal
支持的另一个分配器(这是 Windows API 通常使用的分配器):
// allocating space for 1000 chars
char* p4 = (char*)Marshal.AllocHGlobal(1000 * sizeof(char));
// here you can use p4
// and here you free it
Marshal.FreeHGlobal((IntPtr)p4);
假设您有一些本机代码,可以让您访问一些内存以保存一些数据:
static extern IntPtr GetSomeMemoryFromSomeWinApi();
static extern void FreeSomeMemoryFromSomeWinApi(IntPtr ptr);
你这样使用:
IntPtr p5 = GetSomeMemoryFromSomeWinApi();
// here you have some memory received from some native API
// and here you free it
FreeSomeMemoryFromSomeWinApi(p5);
在这种情况下,您的库必须为您提供Free
方法,因为您不知道内存是如何分配的,但有时您的库文档会告诉您内存是通过特定分配器分配的,所以你使用那种类型的释放器来释放它,比如
Marshal.FreeCoTaskMem(p5);
如果 API 是某个 COM 对象。
Marshal
类甚至具有BSTR
的分配器(COM 对象使用的 Unicode 字符串。它们的长度预先确定)
string str = "Hello";
char *bstr = (char*)Marshal.StringToBSTR(str);
Marshal.FreeBSTR((IntPtr)bstr);
它们有特殊处理,因为它们的“真实”起始地址类似于 (bstr - 2)(它们的长度前面有一个 Int32
)
关键是分配器的数量就像沙漠的一粒沙和天上的星星一样多。它们中的每一个(除了.NET 的标准之一,new
使用的那个)都有一个相应的释放器。他们像夫妻一样去。他们不会与其他人混在一起。
最后一点,如果您编写混合的 .NET/native C 或 C++ 代码,则必须公开一些调用 它们的 free
/delete
的 C/C++ 方法,因为他们的 free
/delete
是他们的 C/C++ 库的一部分,而不是操作系统的一部分。
【讨论】:
这里的一切都很棒,除了建议 PInvoke 指针应该是 IntPtr。使用不安全的 struct * 并允许编译器对指针的类型进行类型检查要安全得多。 IntPtr 是一个漏洞,通过将 PInvoke 参数视为 IntPtr/void*,使 VB 和所谓的“安全”代码能够做非常不安全的事情。【参考方案2】:.NET 6 中有一个新功能可以使用 C API 分配本机内存,即使用新的 NativeMemory。使用这种新方法,您可以(必须)轻松删除分配的内存:
using System.Runtime.InteropServices;
unsafe
byte* buffer = (byte*)NativeMemory.Alloc(100);
NativeMemory.Free(buffer);
【讨论】:
以上是关于删除 C# 不安全指针的主要内容,如果未能解决你的问题,请参考以下文章