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

Что возвращает malloc (0)?

Что возвращает malloc(0)? Будет ли ответ таким же для realloc(malloc(0),0)?

#include<stdio.h>
#include<malloc.h>
int main()
{
        printf("%p\n", malloc(0));
        printf("%p\n", realloc(malloc(0), 0));
        return 0;
}

Выход из linux gcc:

[email protected]:~$ gcc -Wall mal.c
[email protected]:~$ ./a.out
0x9363008
(nil)
[email protected]:~$

Выход всегда меняется для malloc(0). Это стандартный ответ? И почему кто-нибудь заинтересован в получении такого указателя, кроме академических исследований?

EDIT:

Если malloc(0) возвращает фиктивный указатель, то как работает следующее:

int main()
{
    void *ptr = malloc(0);
    printf("%p\n", realloc(ptr, 1024));
    return 0;
}

EDIT:

Следующий код выводит "возможный" для каждой итерации. Почему это не должно терпеть неудачу?

#include<stdio.h>
#include<malloc.h>
int main()
{

        int i;
        void *ptr;
        printf("Testing using BRUTE FORCE\n");
        for (i=0; i<65000; i++)
        {
                ptr = malloc(0);
                if (ptr == realloc(ptr, 1024))
                        printf("Iteration %d: possible\n", i);
                else
                {
                        printf("Failed for iteration %d\n", i);
                        break;
                }
        }
        return 0;
}
4b9b3361

Ответ 1

Другие ответили, как работает malloc(0). Я отвечу на один из вопросов, которые вы задали, на которые еще не ответили (я думаю). Вопрос о realloc(malloc(0), 0):

Что возвращает malloc(0)? Будет ли ответ таким же для realloc(malloc(0),0)?

В стандарте говорится об realloc(ptr, size):

  • if ptr NULL, он ведет себя как malloc(size),
  • в противном случае (ptr не NULL), он отменяет старый указатель объекта на ptr и возвращает указатель на новый выделенный буфер. Но если size равно 0, C89 говорит, что эффект эквивалентен free(ptr). Интересно, что я не могу найти это утверждение в проекте C99 (n1256 или n1336). В C89 единственное разумное значение, возвращаемое в этом случае, будет NULL.

Итак, есть два случая:

  • malloc(0) возвращает NULL в реализации. Тогда ваш вызов realloc() эквивалентен realloc(NULL, 0). Это эквивалентно malloc(0) сверху (и в этом случае NULL).
  • malloc(0) возвращает не NULL. Тогда вызов эквивалентен free(malloc(0)). В этом случае malloc(0) и realloc(malloc(0), 0) не эквивалентны.

Обратите внимание, что здесь есть интересный случай: во втором случае, когда malloc(0) возвращает un NULL при успехе, он все равно может возвращать NULL для указания отказа. Это приведет к вызову типа realloc(NULL, 0), который будет эквивалентен malloc(0), который может возвращать или не возвращать NULL.

Я не уверен, что упущение в C99 является надзором или если это означает, что в C99 realloc(ptr, 0) для не NULL ptr не эквивалентен free(ptr). Я просто попробовал это с помощью gcc -std=c99, и приведенное выше эквивалентно free(ptr).

Изменить: я думаю, я понимаю, в чем заключается ваша путаница:

Посмотрите на фрагмент кода вашего примера:

ptr = malloc(0);
if (ptr == realloc(ptr, 1024))

Вышеупомянутое не совпадает с malloc(0) == realloc(malloc(0), 1024). Во втором вызов malloc() выполняется дважды, тогда как в первом вы передаете ранее выделенный указатель на realloc().

Сначала проанализируйте первый код. Предполагая, что malloc(0) не возвращает NULL при успешном завершении, ptr имеет допустимое значение. Когда вы выполняете realloc(ptr, 1024), realloc() в основном дает вам новый буфер с размером 1024, а ptr становится недействительным. Соответствующая реализация может возвращать тот же адрес, что и тот, который уже находится в ptr. Таким образом, ваше условие if может вернуть true. (Однако обратите внимание, что значение ptr после realloc(ptr, 1024) может быть undefined.)

Теперь задаем вопрос: malloc(0) == realloc(malloc(0), 1024). В этом случае предположим, что и malloc(0) на LHS и RHS возвращаются не NULL. Тогда они гарантированно будут отличаться. Кроме того, возвращаемое значение из malloc() на LHS еще не было free() d, поэтому любые другие malloc(), calloc() или realloc() могут не вернуть это значение. Это означает, что если вы написали свое условие как:

if (malloc(0) == realloc(malloc(0), 1024)
    puts("possible");

вы не увидите possible на выходе (если оба malloc() и realloc() не выполняются и не возвращаются NULL).

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

int main(void)
{
    void *p1;
    void *p2;

    p1 = malloc(0);
    p2 = realloc(p1, 1024);
    if (p1 == p2)
        puts("possible, OK");

    /* Ignore the memory leaks */
    if (malloc(0) == realloc(malloc(0), 1024))
        puts("shouldn't happen, something is wrong");
    return 0;
}

В OS X мой код ничего не выводил, когда я запускал его. В Linux он печатает possible, OK.

Ответ 2

malloc(0) Реализация определена в отношении C99.

От C99 [Раздел 7.20.3]

Порядок и смежность хранилища, выделенные последовательными вызовами calloc, malloc и realloc не определены. Указатель возвращается, если распределение успешно присваивается соответствующим образом, так что он может быть назначен указателю на любой тип объекта а затем используется для доступа к такому объекту или массиву таких объектов в выделенном пространстве (пока пространство явно не освобождено). Время жизни выделенного объекта расширяется от распределения до освобождения. Каждое такое распределение должно указывать указатель на объект, не связанный с каким-либо другим объектом. Указатель возвращает точки начала (младший байт адрес) выделенного пространства. Если пространство не может быть выделено, нулевой указатель вернулся. Если размер запрашиваемого пространства равен нулю, поведение - Определено: возвращается нулевой указатель, или поведение выглядит так, как будто размер ненулевое значение, за исключением того, что возвращаемый указатель не должен использоваться для доступа к объекту.

Ответ 3

В C89 malloc (0) зависит от реализации - я не знаю, зафиксировал ли C99 это или нет. В С++, используя:

char * p = new char[0];

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

Что касается этого, то это удобно для некоторых алгоритмов и означает, что вам не нужно мутировать ваш код с помощью тестов для нулевых значений.

Ответ 4

Стандарт C99

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

Ответ 5

comp.lang.c FAQ имеет следующее сказать:

В стандарте ANSI/ISO говорится, что он может сделать либо; поведение (см. вопрос 11,33). Портативный код должен либо заботиться о том, чтобы не вызывать malloc (0), либо быть подготовленный для возможности вернуться.

Таким образом, лучше избегать использования malloc(0).

Ответ 6

См. C99, раздел 7.20.3:

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

Это справедливо для всех трех функций распределения (т.е. calloc(), malloc() и realloc()).

Ответ 7

Один момент, о котором никто не думал говорить, в вашей первой программе - realloc с длиной 0 - это то же самое, что и free.

на странице руководства Solaris:

Функция realloc() изменяет размер выделенного блока      от ptr до size байтов и возвращает указатель на (возможно перемещенный) блок. Содержимое будет оставаться неизменным до      меньше новых и старых размеров. Если ptr - NULL, realloc()     ведет себя как malloc() для указанного размера. Если size - 0     и ptr не является нулевым указателем, указанное пространство      доступный для дальнейшего размещения приложением, хотя      не возвращается в систему. Память возвращается в систему      только после прекращения подачи заявки.

Если кто-то не знает, что это может быть источником плохого сюрприза (случилось со мной).

Ответ 8

Я думаю, это зависит. Я проверил источники Visual Studio 2005 и увидел это в функции _heap_alloc:

if (size == 0)
    size = 1;

Я думаю, что во многих случаях вам может понадобиться действительный указатель, даже если он запрашивает нулевые байты. Это связано с тем, что это непротиворечивое поведение упрощает проверку ваших указателей, потому что: если у вас есть указатель, отличный от NULL, это нормально; если у вас есть указатель NULL, у вас, вероятно, есть проблема. Поэтому я думаю, что большинство реализаций вернут действительный указатель даже при запросе нулевых байтов.

Ответ 9

Если malloc (0) возвращает фиктивный указатель, то как работает следующее:

void *ptr = malloc(0);

printf("%p\n", realloc(ptr, 1024));

Я не знаю, что вы подразумеваете под "фиктивным указателем". Если malloc(0) возвращает не-NULL, то ptr является допустимым указателем на блок памяти размером 0. Реализация malloc сохраняет эту информацию конкретным образом. realloc знает (конкретный вариант реализации) способ выяснить, что ptr указывает на блок памяти размером 0.

(Как malloc/realloc/free сделать это специфично для реализации. Одна из возможностей состоит в том, чтобы выделить на 4 байта больше, чем требуется, и сохранить размер непосредственно перед блоком памяти. В этом случае ((int *)ptr)[-1] даст размер блока памяти, который равен 0. Вы никогда не должны делать этого из своего кода, он используется только для realloc и free).

Ответ 10

Мы реализовали структуры malloc для встроенного кода с заголовком (и необязательным трейлером). Заголовок может содержать дополнительную информацию для отладки, такую ​​как дескриптор задачи, который его назначил. Кроме того, мне нравятся флаги/границы, такие как 0xA5A5A5A5 и 0x5A5A5A5A, чтобы определить, кто-то где-то переписал границы своего распределения (ов) памяти. Сохраняя списки свободных и используемых блоков, можно также периодически проверять целостность кучи и предотвращать операции (например, free() нераспределенной памяти), которые могут привести к "взрыву".