8

我知道/unsafe在 C# 中使用标志,您可以使用指针。在 C/C++ 中删除您将分别使用free(pointer);和的指针delete pointer;。但是,如何使用 C# 指针实现相同的效果?

4

2 回答 2

32

这取决于。您使用freeand来释放使用anddelete分配的内存。mallocnew

通常,如果您进行 PInvoke 调用,则指针应为IntPtr.

如果使用fixed(or GCHandle) 获取托管对象的指针,则内存是从 GC 内存中分配的

  • 对于 GC 的内存,当您取消固定该内存(退出fixed块或释放GCHandle)时,GC 将返回处理它
  • 对于通过 .NETMarshal方法分配的内存,您可以使用补充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调用CoTaskMemAllocWindows API,因此您可以同时使用Marshal.FreeCoTaskMemWindows 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++ 库的一部分,而不是操作系统的一部分.

于 2013-08-11T14:30:28.217 回答
0

.NET 6 中有使用 C API 分配本机内存的新功能,即使用新的 NativeMemory。使用这种新方法,您可以(必须)轻松删除分配的内存:

using System.Runtime.InteropServices;

unsafe
{
    byte* buffer = (byte*)NativeMemory.Alloc(100);

    NativeMemory.Free(buffer);
}
于 2021-08-24T05:17:58.530 回答