我知道/unsafe
在 C# 中使用标志,您可以使用指针。在 C/C++ 中删除您将分别使用free(pointer);
和的指针delete pointer;
。但是,如何使用 C# 指针实现相同的效果?
2 回答
这取决于。您使用free
and来释放使用anddelete
分配的内存。malloc
new
但
通常,如果您进行 PInvoke 调用,则指针应为IntPtr
.
如果使用fixed
(or GCHandle
) 获取托管对象的指针,则内存是从 GC 内存中分配的
- 对于 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
通过使用从“本机”池(通过 COM 对象通常使用的分配器)分配一些内存的示例Marshal.AllocCoTaskMem
(注意Marshal.AllocCoTaskMem
调用CoTaskMemAlloc
Windows API,因此您可以同时使用Marshal.FreeCoTaskMem
Windows APICoTaskMemFree
来释放它):
// 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 的标准之一,由 .NET 使用的那个new
)都有一个相应的释放器。他们像夫妻一样去。他们不与其他人混在一起。
最后一点,如果您编写混合的 .NET/native C 或 C++ 代码,则必须公开一些调用其 free
/的 C/C++ 方法delete
,因为它们free
/delete
是其 C/C++ 库的一部分,而不是操作系统的一部分.
.NET 6 中有使用 C API 分配本机内存的新功能,即使用新的 NativeMemory。使用这种新方法,您可以(必须)轻松删除分配的内存:
using System.Runtime.InteropServices;
unsafe
{
byte* buffer = (byte*)NativeMemory.Alloc(100);
NativeMemory.Free(buffer);
}