只是想了解这是否有意义以及其中的意义在哪里。
Marshal.AllocHGlobal(int cb) 在非托管内存中分配指定数量的字节。

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

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

最佳答案

1、为什么Marshal.AllocHGlobal分配了0个字节不返回IntPtr.ZeroMarshal.AllocHGlobalLocalAlloc 内部调用 WinAPI 函数 WinBase.h
至于为什么 Marshal.AllocHGlobal(0) 不返回 IntPtr.Zero :LocalAlloc 仅在分配过程中出现故障时返回 NULL(C# 等效项:IntPtr.Zero)。
这也可以在 source code 中看到:

IntPtr pNewMem = Win32Native.LocalAlloc_NoSafeHandle(LMEM_FIXED, unchecked(numBytes));

if (pNewMem == IntPtr.Zero) {
    throw new OutOfMemoryException();
}
return pNewMem;

2. 为什么分配 0 字节会返回一个(有效的)内存地址?
documentation 说明了 LocalAlloc 的返回值:

现在,LocalAlloc 只有在‡ uBytes 为负 时才会失败;它对正值或零值都没有问题。
这意味着分配将始终成功‡并且如果您尝试分配 0 个字节,您将始终收到一个有效的指针。
‡ 还有其他失败的原因,例如内存不足。为简单起见,本解释中省略了它们。

3. 我应该释放 Marshal.AllocHGlobal(0) 分配的内存吗?LocalAlloc 的签名是这样的:
DECLSPEC_ALLOCATOR HLOCAL LocalAlloc(
  UINT   uFlags,
  SIZE_T uBytes
);
documentation 指出

出于某种原因,Marshal.AllocHGlobal(0) 执行 不是 传递 LMEM_MOVEABLE 而是 LMEM_FIXED
该文档缺少有关此特定案例的信息。运行测试(见下文)表明实际上正在分配内存,您肯定需要释放内存,如下所示:
IntPtr zeroBytesPtr = Marshal.AllocHGlobal(0);

// Do stuff with the pointer.

Marshal.FreeHGlobal(zeroBytesPtr);
如果 Marshal.AllocHGlobal 改为传递 LMEM_MOVEABLE,则不需要在任何地方释放指针。

至于测试:
while(true) {
    void* v = LocalAlloc(LMEM_FIXED, 0);
}
为循环的每次迭代分配内存并每次返回一个新地址,而
while(true) {
    void* v = LocalAlloc(LMEM_MOVEABLE, 0);
}
只分配一次内存并在每次 时返回相同的地址。
这表明为什么必须释放 Marshal.AllocHGlobal 分配的内存(因为它使用 LMEM_FIXED ),因为每次调用都会分配一个新的内存对象。

关于c# - Marshal.AllocHGlobal(0) - 为什么这不返回 IntPtr.Zero,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52073336/

10-11 19:19