В чем смысл malloc (0)?


Ответы:


121

Согласно спецификациям, malloc (0) вернет либо «нулевой указатель, либо уникальный указатель, который может быть успешно передан в free ()».

Это в основном позволяет вам ничего не выделять, но по-прежнему без беспокойства передавать переменную "artist" в вызов free (). Для практических целей это почти то же самое, что:

artist = NULL;

51
Лично я считаю, что установка значения NULL - лучшая кроссплатформенная стратегия, поскольку free () гарантированно (по спецификации) отлично работает с NULL в качестве входных данных.
Рид Копси

2
Как упоминал К. Росс, некоторые платформы технически могут возвращать здесь указатель (это «уникальный указатель, который может быть передан бесплатно»), но если вы рассматриваете это как char *, это может дать вам недопустимый, неограниченный символ. Опасно полагаться на это в кроссплатформенных ситуациях.
Рид Копси

9
На самом деле хотелось бы, чтобы в спецификациях было сказано «благополучно перешел на realloc»
--.-

1
@NSAddict "пустая структура, где sizeof вернет 0", пожалуйста, приведите пример, звучит как расширение языка.
chux

1
@hanshenrik Кто сказал, что ты не можешь? realloc()позволяет передавать любой действительный указатель, возвращаемый malloc(). Должно хватить.
glglgl 03

51

Стандарт C (C17 7.22.3 / 1) говорит:

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

Таким образом, malloc(0)может быть возвращен NULLдействительный указатель, который не может быть разыменован . В любом случае вполне допустимо называтьfree() к нему.

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

Глядя на код в ссылке, я считаю, что у автора было два заблуждения:

  • malloc(0)всегда возвращает действительный указатель , и
  • free(0) плохо.

Таким образом, он удостоверился, что эта artistи другие переменные всегда имеют какое-то «действительное» значение. Комментарий говорит так: // these must always point at malloc'd data.


10
Тот факт, что он зависит от реализации, делает его более или менее полностью бесполезным - это одна из самых дрянных частей стандарта C, и многие из комитетов по стандартам (например, П. Дж. Плоджер) жаловались на это.

10
Я согласен. Если malloc(0)возвращен действительный указатель, то malloc()возврат NULLвсегда означает «сбой» и 0больше не является особым случаем, что более согласованно.
Алок Сингхал,

1
Поскольку обстоятельства mallocотказа получить память определяются реализацией, реализация могла бы просто определить, что выделения размера 0 всегда неудовлетворительны ( ENOMEM), и теперь malloc(0)возврат 0 (с errno==ENOMEM) согласован. :-)
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE

6
Сможете ли вы reallocуказатель вернуть malloc(0)? Ты можешь realloc((char*)NULL)?
Брейден Бест,

3
@Braden Best Да и тем и другим.
chux

11

Поведение malloc (0) зависит от реализации. Библиотека может возвращать NULL или иметь обычное поведение malloc без выделенной памяти. Что бы он ни делал, это нужно где-то задокументировать.

Обычно он возвращает действительный и уникальный указатель, но НЕ должен разыменовываться. Также обратите внимание, что он МОЖЕТ потреблять память, даже если на самом деле ничего не выделял.

Можно перераспределить ненулевой указатель malloc (0).

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


9
malloc()должен где-то хранить «служебную информацию» (например, этот размер выделенного блока и другие вспомогательные данные). Таким образом, если malloc(0)не возвращается NULL, он будет использовать память для хранения этой информации, а если не free()d, будет утечка памяти.
Алок Сингхал,

Реализации malloc выполняют ведение записей, которые могут добавлять определенный объем данных на каждый указатель, возвращаемый сверх запрошенного размера.
user7116 07

1
Потребляемая и выделенная память не означают одно и то же. В этом случае большая часть реализации вернет уникальный указатель. Это означает, что часть адресного пространства нужно пожертвовать ради этого указателя. В зависимости от распределителя это может означать, что он выделит 1 байт или больше.
Coincoin

1
Библиотека может делать все, что хочет - ну, она может либо вернуть уникальный указатель, который никто другой malloc()не вернет, либо вернуть NULL.
Алок Сингхал

1
@jldupont: по крайней мере, библиотека времени выполнения Microsoft C возвращает уникальный указатель для malloc(0). Однако в той же реализации стандартной библиотеки C realloc(ptr, 0)освобождает ptrи возвращает NULL.
Medinoc

5

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

Выполнение malloc (0) не приведет к автоматическому выделению памяти правильного размера. Функция malloc не знает, к чему вы приводите ее результат. Функция malloc полагается исключительно на число размера, которое вы указываете в качестве аргумента. Вам нужно сделать malloc (sizeof (int)), чтобы получить достаточно памяти для хранения int, например, а не 0.


4

malloc(0)не имеет для меня никакого смысла, если только код не зависит от поведения, специфичного для реализации. Если код предназначен для переносимости, то он должен учитывать тот факт, что возврат NULL malloc(0)не является ошибкой. Так почему бы просто не присвоить NULLartist любом случае , поскольку это допустимый успешный результат, меньше кода и не заставит ваших программистов по обслуживанию тратить время на его выяснение?

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


3

Здесь есть много полуправдивых ответов, так что вот неопровержимые факты. На странице руководства malloc()сказано:

Если size равен 0, то malloc () возвращает либо NULL, либо уникальное значение указателя, которое позже может быть успешно передано в free ().

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

Если ptr равен NULL, никакая операция не выполняется.

Итак, что бы ни malloc(0)вернулось, его можно смело передавать free(). Но может и NULLуказатель.

Следовательно, писать artist = malloc(0); ничем не лучше, чем писать. artist = NULL;


1
Жаль, что реализации не разрешено возвращать ненулевой, неуникальный указатель. Таким образом, malloc(0)можно было бы вернуть, скажем, 0x1, и free()можно было бы иметь специальную проверку 0x1, как и для 0x0.
Тодд Леман,

3
@Todd Lehman Реализация может делать то, что вы предлагаете. Спецификация C не указывает, что результат должен быть « NULLили уникальный указатель». вместо этого «либо нулевой указатель, либо указатель на выделенное пространство». Нет никакого уникального требования. OTOH, возвращающий неуникальное специальное значение может нарушить код, который рассчитывает на уникальные значения. Возможно, это угловой вопрос для SO.
chux - Reinstate Monica

manможет также задокументировать определяемую реализацией форму, используемую в * nix. В данном случае это не так, но это все еще не канонический источник для генерала С.
Лундин

@Lundin Верно. Но справочные страницы намного более доступны, чем стандарт C, а справочные страницы в системах GNU / Linux обычно достаточно хорошо документируют, каким стандартам следует реализация. Наряду с информацией о том, какие части соответствуют какому стандарту, если они отличаются. У меня такое чувство, что они оба хотят быть точными и рекламировать каждый бит, являющийся расширением GNU ...
cmaster -

3

Почему тебе не следует этого делать ...

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

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

size_t size;

/* Initialize size, possibly by user-controlled input */

int *list = (int *)malloc(size);
if (list == NULL) {
  /* Handle allocation error */
}
else {
  /* Continue processing list */
}

См. Страницу «Безопасное кодирование» из CERT Coding Standards, где я взял приведенный выше пример для дальнейшего чтения.


Ссылка была перемещена: wiki.sei.cmu.edu/confluence/display/c/…
Cacahuete Frito

2

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

  • foo не равно NULL, а размер равен нулю, realloc(foo, size); . Когда вы передаете в realloc указатель, отличный от NULL, и нулевой размер, realloc ведет себя так, как если бы вы вызвали free (…)
  • foo имеет значение NULL, а размер не равен нулю и больше 1 realloc(foo, size);,. Когда вы передаете NULL-указатель и размер не равен нулю, realloc ведет себя так, как если бы вы вызвали malloc (…)

Надеюсь, это поможет, С уважением, Том.


1

Чтобы на самом деле ответить на поставленный вопрос: нет причин для этого


0

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

int *i = malloc(0);
*i = 100;
printf("%d", *i);

Мы ожидаем здесь ошибки сегментации, но, что удивительно, это выводит 100! Это потому, что malloc на самом деле запрашивает огромный кусок памяти, когда мы вызываем malloc в первый раз. Каждый последующий вызов malloc использует память из этого большого фрагмента. Только после того, как этот огромный кусок закончится, запрашивается новая память.

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


1
Запись в *iможет не завершиться в вашем случае, но, тем не менее, это неопределенное поведение. Остерегайтесь носовых демонов!
Джон Дворжак

1
Да. Это правда. Это зависит от реализации. Я проверил это на MaxOS X и некоторых дистрибутивах Linux. На других платформах не пробовал. При этом концепция, которую я описал, была описана в книге "Язык программирования C" Брейна Кернигана и Денниса Ричи.
Сагар Бхосале 01

Знаю: супер поздний комментарий по этому вопросу. Но иногда об malloc(0)этом не упоминается. В тех реализациях, где он возвращает значение, отличное от NULL, особенно в сборке DEBUG, он, скорее всего, выделяет БОЛЬШЕ, чем вы просили, и дает вам указатель на только что прошедший его внутренний заголовок. Это позволяет вам почувствовать фактическое использование памяти, если вы получите это до и после ряда распределений. например: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;или что-то подобное.
Джесси

Я читал "Язык программирования C" Брейна Кернигана и Денниса Ричи и не помню, чтобы в нем что-то говорилось malloc(0). Не могли бы вы сказать, о какой главе вы тоже говорите? Было бы неплохо указать точную цитату.
Андрей Беньковский

0

В Windows:

  • void *p = malloc(0);выделит буфер нулевой длины в локальной куче. Возвращенный указатель является действительным указателем кучи.
  • mallocв конечном итоге вызывает HeapAllocиспользование кучи времени выполнения C по умолчанию, которая затем вызывает RtlAllocateHeapи т. д.
  • free(p);используется HeapFreeдля освобождения буфера нулевой длины в куче. Если его не освободить, это приведет к утечке памяти.

0

На самом деле это довольно полезно, и (очевидно, ИМХО) разрешенное поведение возврата указателя NULL нарушено. Динамический указатель полезен не только тем, на что он указывает, но и тем, что его адрес уникален. Возвращение NULL удаляет это второе свойство. Все встроенные маллоки, которые я программирую (на самом деле довольно часто), имеют такое поведение.



-2

Вот анализ после работы с инструментом проверки памяти valgrind.

==16740== Command: ./malloc0
==16740==
p1 = 0x5204040
==16740==
==16740== HEAP SUMMARY:
==16740==     in use at exit: 0 bytes in 0 blocks
==16740==   total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
==16740==
==16740== All heap blocks were freed -- no leaks are possible

и вот мой пример кода:

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

int main()
{
   //int i;
   char *p1;

   p1 = (char *)malloc(0);
   printf("p1 = %p\n", p1);

   free(p1);

   return 0;

}

По умолчанию выделено 1024 байта. Если я увеличу размер malloc, выделенные байты увеличатся на 1025 и так далее.


-3

Согласно ответу Рида Копси и странице руководства malloc, я написал несколько примеров для тестирования. И я обнаружил, что malloc (0) всегда будет давать ему уникальное значение. См. Мой пример:

char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

Результатом будет «Получен действительный указатель», что означает ptrне ноль.


-6

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

int *p=NULL;
p=(int *)malloc(0);
free(p);

Приведенный выше код будет нормально работать в gccкомпиляторе на машине Linux. Если у вас 32-битный компилятор, вы можете указать значения в целочисленном диапазоне, например, от -2147483648 до 2147483647. То же самое относится и к символам. Обратите внимание, что при изменении типа объявленного указателя диапазон значений изменится независимо от mallocприведенного типа, т.е.

unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);

p примет значение от 0 до 255 char, поскольку объявлено беззнаковым int.


5
Креллан прав, указывая на то, что этот ответ неверен: malloc()ничего не знает о приведении (что на самом деле полностью избыточно в C). Разыменование возвращаемого значения malloc(0)вызовет неопределенное поведение.
cmaster - восстановить монику

-6

Просто чтобы исправить ложное впечатление здесь:

artist = (char *) malloc(0);никогда не вернется NULL; это не то же самое, что artist = NULL;. Напишите простую программу и сравните artistс NULL. if (artist == NULL)ложно и if (artist)верно.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.