删除 C# 不安全指针

Posted

技术标签:

【中文标题】删除 C# 不安全指针【英文标题】:Deleting C# Unsafe Pointers 【发布时间】:2013-08-12 22:30:01 【问题描述】:

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

【问题讨论】:

【参考方案1】:

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

但是

一般来说,如果您进行 PInvoke 调用,那么指针应该是 IntPtr

如果使用fixed(或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

使用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# 不安全指针的主要内容,如果未能解决你的问题,请参考以下文章

何时在 C#/.NET 中使用指针?

C# 中是不是有像 C++ 这样的指针?安全吗?

C# 中的不安全代码

C#中对指针操作报错解决方法

指向变量的 C# 指针 - 我可以避免吗? [复制]

C# 指针学习笔记之指针类型