Marshal.AllocHGlobal(0) - 为什么不返回 IntPtr.Zero

cog*_*el0 4 .net c# marshalling

只是想了解这是否有意义以及其中的意义在哪里。

Marshal.AllocHGlobal(int cb)在非托管内存中分配指定数量的字节。

但为什么Marshal.AllocHGlobal(0)实际上会返回一个IntPtrnot IntPtr.Zero?当我使用完 0 字节后,我是否应该释放分配的 0 字节?

我看不到这个实现背后的逻辑,有人可以解释一下吗?

Tho*_*kow 6

1.为什么分配了0字节Marshal.AllocHGlobal不返回?IntPtr.Zero

\n

Marshal.AllocHGlobalLocalAlloc在内部调用WinAPI 函数WinBase.h

\n

至于为什么Marshal.AllocHGlobal(0)不返回IntPtr.Zero:\nLocalAlloc仅在分配期间失败的情况下返回NULL(C# 等效: )。\n这也可以在源代码IntPtr.Zero中看到:

\n
IntPtr pNewMem = Win32Native.LocalAlloc_NoSafeHandle(LMEM_FIXED, unchecked(numBytes));\n\nif (pNewMem == IntPtr.Zero) {\n    throw new OutOfMemoryException();\n}\nreturn pNewMem;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

2. 为什么分配 0 字节会返回(有效)内存地址?

\n

文档提到了以下的返回值LocalAlloc

\n
\n

如果函数成功,返回值是新分配的内存对象的句柄。

\n

如果函数失败,则返回值为NULL

\n
\n

现在,LocalAlloc 仅当\xe2\x80\xa1uBytes为负数时才会失败;正值或零值都没有问题。

\n

这意味着分配始终会成功\xe2\x80\xa1,并且如果您尝试分配 0 字节,您将始终收到有效的指针。

\n

\xe2\x80\xa1 失败还有其他原因,例如内存不足。为简单起见,本解释中省略了它们。

\n
\n

3. 我应该释放分配的内存Marshal.AllocHGlobal(0)吗?

\n

的签名LocalAlloc是这样的:

\n
DECLSPEC_ALLOCATOR HLOCAL LocalAlloc(\n  UINT   uFlags,\n  SIZE_T uBytes\n);\n
Run Code Online (Sandbox Code Playgroud)\n

文档指出

\n
\n

如果 [ uBytes] 为零且uFlags参数指定LMEM_MOVEABLE,则该函数返回标记为已丢弃的内存对象的句柄。

\n
\n

由于某种原因,Marshal.AllocHGlobal(0)没有通过LMEM_MOVEABLE而是相反LMEM_FIXED

\n

该文档缺乏有关此特定案例的信息。运行测试(见下文)表明内存实际上正在被分配,并且您肯定需要释放内存,如下所示:

\n
IntPtr zeroBytesPtr = Marshal.AllocHGlobal(0);\n\n// Do stuff with the pointer.\n\nMarshal.FreeHGlobal(zeroBytesPtr);\n
Run Code Online (Sandbox Code Playgroud)\n

如果Marshal.AllocHGlobal改为传递LMEM_MOVEABLE,则无需在任何地方释放指针。

\n
\n

至于测试:

\n
while(true) {\n    void* v = LocalAlloc(LMEM_FIXED, 0);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

为循环的每次迭代分配内存并每次返回一个新地址,而

\n
while(true) {\n    void* v = LocalAlloc(LMEM_MOVEABLE, 0);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

仅分配内存一次并每次返回相同的地址

\n

这表明为什么分配的内存Marshal.AllocHGlobal必须被释放(因为它使用LMEM_FIXED),因为每次调用都会分配一个新的内存对象。

\n