1. Почему Marshal.AllocHGlobal
не возвращает IntPtr.Zero
, если выделено 0 байтов?
Marshal.AllocHGlobal
внутренне вызывает функцию WinAPI LocalAlloc
с WinBase.h
.
Почему Marshal.AllocHGlobal(0)
не возвращает IntPtr.Zero
:
LocalAlloc
возвращает только NULL
(эквивалент C #: IntPtr.Zero
) в случае сбоя во время выделения.
Это также можно увидеть в исходном коде :
IntPtr pNewMem = Win32Native.LocalAlloc_NoSafeHandle(LMEM_FIXED, unchecked(numBytes));
if (pNewMem == IntPtr.Zero) {
throw new OutOfMemoryException();
}
return pNewMem;
2. Почему выделение 0 байтов возвращает (действительный) адрес памяти?
В документации говорится о возвращаемом значении LocalAlloc
:
Если функция завершается успешно, возвращаемое значение является дескриптором вновь выделенного объекта памяти.
В случае сбоя функции возвращается значение NULL
.
Теперь LocalAlloc
терпит неудачу, только если ‡ uBytes
отрицательно ; у него нет проблем с положительными или нулевыми значениями.
Это означает, что распределение всегда будет успешным ‡, и вы всегда получите правильный указатель, если попытаетесь выделить 0 байтов.
‡ Существуют и другие причины неудачи, например, недостаточно памяти. Для простоты они были опущены в этом объяснении.
3. Должен ли я освободить память, выделенную на Marshal.AllocHGlobal(0)
?
Подпись LocalAlloc
такова:
DECLSPEC_ALLOCATOR HLOCAL LocalAlloc(
UINT uFlags,
SIZE_T uBytes
);
Документация гласит, что
если [uBytes
] равно нулю, а параметр uFlags
указывает LMEM_MOVEABLE
, функция возвращает дескриптор объекта памяти, помеченного как отброшенный.
По какой-то причине Marshal.AllocHGlobal(0)
делает не pass 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
), потому что новый объект памяти выделяется при каждом вызове.