Подтвердить что ты не робот

Если `malloc (0)` возвращает ненулевой указатель, могу ли я передать это `free`?

Я читал обсуждение того, как malloc ведет себя, когда вы запрашиваете блок с нулевым размером.

Я понимаю, что поведение malloc(0) определяется реализацией, и предполагается либо вернуть нулевой указатель, либо ненулевой указатель, который я тем не менее не должен получать. (Это имеет смысл, поскольку нет гарантии, что он указывает на любую полезную память.)

Однако, если a получить такой недопустимый ненулевой указатель, я могу передать его на free обычным способом? Или это незаконно, поскольку указатель, который я получаю из malloc(0), может не указывать на выделенный выделенный блок памяти?

Конкретно, имеет ли следующий код корректное поведение:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* x = (int*) malloc(0);
    if (x == NULL) {
        printf("Got NULL\n");
        return 0;
    } else {
        printf("Got nonnull %p\n", x);
    }
    free(x); // is calling `free` here okay?
}
4b9b3361

Ответ 1

Стандарт C99 (на самом деле РГ14/N1124. Проект Комитета - 6 мая 2005 г. ISO/IEC 9899: TC2) говорит о malloc():

Указатель возвращает точки в начало (младший байт адрес) выделенного пространства. Если пространство не может быть выделено, нулевой указатель вернулся. Если размер запрашиваемого пространства равен нулю, поведение определяется реализацией: либо возвращается нулевой указатель, либо поведение такое, как если бы размер был некоторым ненулевое значение, за исключением того, что возвращаемый указатель не должен использоваться для доступа к объекту

и около free():

В противном случае, если аргумент не соответствует указателю, ранее возвращенному функцией calloc, malloc или realloc, или если пространство было освобождено вызовом free или realloc, поведение undefined.

IEEE Std 1003.1-2008 (POSIX), выпуск 2016 говорит о free():

Функция free() должна вызывать пространство, на которое указывает ptr, освобождаться; то есть, доступный для дальнейшего распределения. Если ptr нулевой указатель, никаких действий не будет. В противном случае, если аргумент не соответствует указателю, ранее возвращенному функцией в POSIX.1-2008 который выделяет память, как если бы malloc(), или если пространство было освобожденный вызовом free() или realloc(), поведение undefined.

Итак, независимо от того, что возвращает *alloc(), вы можете перейти к free().

Что касается текущих реализаций malloc():

FreeBSD использует предоставленный jemalloc, который делает

void *
je_malloc(size_t size)
{
    void *ret;
    size_t usize JEMALLOC_CC_SILENCE_INIT(0);

    if (size == 0)
        size = 1;
    [...]

В то время как Apple libmalloc делает

void *
szone_memalign(szone_t *szone, size_t alignment, size_t size)
{
    if (size == 0) {
        size = 1; // Ensures we'll return an aligned free()-able pointer
    [...]

GLIBC также изменяет запрошенный размер; он использует вызов этого макроса с запрошенным размером в байтах в качестве параметра для выравнивания размера с определенной границей или просто минимального размера выделения:

#define request2size(req)                                       \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?         \
    MINSIZE :                                                   \
    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

Ответ 2

Да, на самом деле вы должны сделать это, чтобы избежать вероятной утечки памяти.

Система malloc обычно возвращает скрытый блок управления в пространстве непосредственно перед указателем с информацией, такой как размер распределения. Если размер выделения равен нулю, этот блок все еще существует и занимает память, если malloc возвращает ненулевое значение.