Просто увидел этот code:
artist = (char *) malloc(0);
и мне было интересно, зачем это делать?
В соответствии со спецификациями malloc (0) вернет либо "нулевой указатель, либо уникальный указатель, который можно успешно передать свободному ( )".
Это в основном позволяет вам ничего не выделять, но все равно передавать переменную "artist" на вызов free() без проблем. Для практических целей это в значительной степени то же самое, что и делать:
artist = NULL;
Стандарт 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
.
malloc (0) поведение специфично для реализации. Библиотека может возвращать NULL или иметь регулярное поведение malloc без выделения памяти. Как бы то ни было, он должен быть где-то документирован.
Обычно он возвращает указатель, который является действительным и уникальным, но НЕ должен быть разыменован. Также обратите внимание, что он МОЖЕТ потреблять память, даже если она фактически ничего не выделяет.
Можно переадресовать указатель не null malloc (0).
Наличие malloc (0) verbatim не очень полезно. Он в основном используется, когда динамическое распределение равно нулю, и вы не хотели его проверять.
В другом месте на этой странице появляется ответ "malloc (0) вернет действительный адрес памяти и диапазон которого будет зависеть от типа указателя, которому выделяется память". Это утверждение неверно (у меня недостаточно репутации, чтобы прокомментировать этот ответ напрямую, поэтому не могу поставить этот комментарий прямо под ним).
Выполнение malloc (0) не будет автоматически распределять память правильного размера. Функция malloc не знает, к чему вы делаете свой результат. Функция malloc полагается исключительно на число размеров, которое вы даете в качестве аргумента. Вам нужно сделать malloc (sizeof (int)), чтобы получить достаточно места для хранения int, например, а не 0.
Я знаю, что это было пять лет, но мой ответ может что-то добавить, и у меня есть репутация, чтобы поддерживать.
malloc(0);
может использоваться в ситуациях, когда вы не знаете точной длины того, что происходит. Если результат с нулевой длиной также является возможным результатом, использовать начальный размер с минимальным выделенным размером не нужно. Например, буферы. Если вы читаете переменное количество байтов из своего буфера, может расти. Вы можете увеличить свой буфер с помощью realloc()
. Но realloc нуждается в указателе на память, который уже есть malloc
ed или calloc
ed, а затем используется malloc(0);
.
char* buffer = malloc(0);
int bytesread = 0;
do
{
//read n bytes from somewhere
buffer = realloc(buffer, bytesread + n); //expand the buffer
//check it is properly allocated
//copy the newly read bytes to the buffer
bytesread += n;
}
while (there_is_still_a_chance_of_reading_more_bytes);
//do something with the complete buffer
free(buffer); //does not really need if(buffer);
Конечно. есть и другие соображения: если вы знаете количество прочитанных байтов или число будет большим, повторное чтение небольших чисел байтов и использование memset
не очень эффективно.
malloc(0)
не имеет для меня никакого смысла, если только код не полагается на поведение, специфичное для реализации. Если код предназначен для переносимости, он должен учитывать тот факт, что возврат NULL из malloc(0)
не является ошибкой. Итак, почему бы просто не назначить NULL для artist
в любом случае, так как это действительный успешный результат и меньше кода, и не заставит ваших программистов обслуживания время от времени выяснять это?
malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)
или malloc(some_variable_which_might_be_zero)
, возможно, могли бы использовать их, хотя снова вам нужно проявлять особую осторожность, чтобы не рассматривать возврат NULL как сбой, если значение равно 0, но размер 0 должен быть в порядке.
Здесь есть много верных ответов, так что вот тяжелые факты. В man-странице для malloc()
говорится:
Если размер равен 0, malloc() возвращает либо NULL, либо уникальное значение указателя, которое может быть позже успешно передается в free().
Это означает, что нет абсолютно никакой гарантии, что результат malloc(0)
является либо уникальным, либо не NULL. Единственная гарантия обеспечивается определением free()
, опять же, вот что говорится в man-странице:
Если ptr равно NULL, операция не выполняется.
Итак, независимо от того, что возвращает malloc(0)
, его можно безопасно передать в free()
. Но так же может быть указатель NULL
.
Следовательно, запись artist = malloc(0);
не лучше, чем запись artist = NULL;
По общему признанию, я никогда раньше этого не видел, это первый раз, когда я видел этот синтаксис, можно сказать, классический случай переполнения функции. В связи с ответом Рида я хотел бы указать, что есть аналогичная вещь, которая выглядит как перегруженная функция realloc
:
realloc(foo, size);
. Когда вы передаете указатель не-NULL и размер нуля для realloc, realloc ведет себя так, как если бы вы вызывали бесплатный (...)realloc(foo, size);
. Когда вы передаете указатель NULL, а размер не равен нулю, realloc ведет себя так, как если бы вы вызывали malloc (...)Надеюсь, это поможет, С наилучшими пожеланиями, Том.
Почему вы не должны этого делать...
Так как значение возврата 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, где я привел пример выше для дальнейшего чтения.
Чтобы ответить на поставленный вопрос: нет причин для этого
malloc (0) вернет NULL или действительный указатель, который можно корректно передать бесплатно. И хотя кажется, что память, на которую указывает она, бесполезна или ее нельзя записать или прочитать, это не всегда так.:)
int *i = malloc(0);
*i = 100;
printf("%d", *i);
Мы ожидаем здесь ошибку сегментации, но удивительно, что это печатает 100! Это связано с тем, что malloc фактически запрашивает огромный кусок памяти, когда мы вызываем malloc в первый раз. Каждый вызов malloc после этого использует память из этого большого фрагмента. Только после того, как этот огромный кусок закончился, запрашивается новая память.
Использование malloc (0): если вы находитесь в ситуации, когда вы хотите, чтобы последующие вызовы malloc были быстрее, вызов malloc (0) должен сделать это за вас (кроме случаев с краем).
В Windows:
void *p = malloc(0);
будет выделять буфер нулевой длины в локальной куче. Возвращаемый указатель является допустимым указателем кучи.malloc
в конечном итоге вызывает HeapAlloc
, используя кучу времени выполнения C, которая затем вызывает RtlAllocateHeap
и т.д.free(p);
использует HeapFree
, чтобы освободить буфер 0 длины в куче. Не освобождая его, это приведет к утечке памяти.На самом деле это весьма полезно, и (очевидно, ИМХО) допустимое поведение возврата NULL-указателя нарушено. Динамический указатель полезен не только для того, на что он указывает, но также для факта, что его адрес уникален. Возвращение NULL удаляет это второе свойство. Все встроенные malloc, которые я программирую (довольно часто на самом деле), имеют такое поведение.
Не уверен, согласно некоторому случайному файлу malloc source, который я нашел, ввод 0 приводит к возвращаемому значению NULL. Так что это безумный способ установить указатель художника на NULL.
http://www.raspberryginger.com/jbailey/minix/html/lib_2ansi_2malloc_8c-source.html
Вот анализ после запуска с помощью инструмента проверки памяти 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 и т.д.
В ответ на Reed Copsey и man-страницу malloc я написал несколько примеров для тестирования. И я узнал, что malloc (0) всегда придаст ему уникальную ценность. См. Мой пример:
char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
puts("Got a null pointer");
else
puts("Got a valid pointer");
Выход будет "Получен действительный указатель", что означает, что ptr
не является нулевым.
malloc(0)
вернет действительный адрес памяти и диапазон которого будет зависеть от типа указателя, который выделяется памятью. Также вы можете назначать значения для области памяти, но это должно быть в диапазоне с типом используемого указателя. Вы также можете освободить выделенную память. Я объясню это на примере:
int *p=NULL;
p=(int *)malloc(0);
free(p);
Приведенный выше код будет хорошо работать в компиляторе gcc
на машине Linux. Если у вас есть 32-битный компилятор, вы можете предоставить значения в целочисленном диапазоне, то есть от -2147483648 до 2147483647. То же самое относится и к символам. Обратите внимание: если тип объявленного указателя изменяется, диапазон значений будет изменяться независимо от malloc
typecast, т.е.
unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);
p
будет принимать значение от 0 до 255 из char, поскольку объявлено беззнаковым int.
Просто чтобы исправить ложное впечатление здесь:
artist = (char *) malloc(0);
никогда не вернется NULL
; это не то же самое, что artist = NULL;
. Напишите простую программу и сравните artist
с NULL
. if (artist == NULL)
false, а if (artist)
- true.