2010-01-25 4 views
52

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

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

Выход из Linux НКУ:

[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; 
} 
+0

@ tommieb75: это помогает ответить на исходный вопрос. Сказав это, этот вопрос состоит из двух частей, а вторая часть интереснее, а не обман. См. Мой ответ на этот вопрос. –

ответ

43

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

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

Стандарт говорит, что это о realloc(ptr, size):

  • если 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) возвращается Непро- NULL на успех, он все еще может вернуться NULL, чтобы указать отказ. Это приведет к вызову типа: realloc(NULL, 0), который будет эквивалентен malloc(0), который может возвращать или не возвращать NULL.

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

Edit: Я думаю, я понимаю, что ваша путаница:

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

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), это может быть неопределенное поведение.)

Теперь задаваемый вопрос: 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.

+0

@Alok: Отличная работа. Я проверил условие на Linux, если (malloc (0) == realloc (malloc (0), 1024). Он терпит неудачу каждый раз! –

+0

Когда вы говорите «сбой», вы имеете в виду программу выше отпечатков «не должно произойти, что-то неверно "? –

+0

« Стандарт говорит об этом в realloc (ptr, size): если ptr имеет значение NULL, он ведет себя как malloc (size), иначе (ptr не NULL), он отменяет старый указатель объекта на ptr и возвращает указатель на новый выделенный буфер ». Я чувствую, что я должен упомянуть, что' realloc' выполняет только «free (ptr)», если область памяти должна была быть перемещена, а если id нет, не выполняется удаление, расширение. По крайней мере, это то, что я понимаю из «man'-страницы». Если область, на которую была указана ссылка, была перемещена, свободен (ptr). » –

15

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

char * p = new char[0]; 

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

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

+1

@Neil: 'malloc (0)' то же самое в C99. –

+0

@Alok: это просто ложно. C99 позволяет 'malloc (0)' возвращать 0, и хорошие реализации делают это. –

+6

@R. Я думаю, дело в том, что C99 не требует * реализации для возврата '0' или не' '' для 'malloc (0)'. –

5

С99 стандарт

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

5

comp.lang.c FAQ имеет the following сказать:

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

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

+1

Но тот же указатель, возвращаемый malloc (0), если не NULL, может быть использован realloc(), чтобы указать на некоторое допустимое расположение памяти. Подобно realloc (malloc (0), 1024); –

+3

@Manav: Он также работает с NULL тоже: realloc (NULL, 1024) 'такой же, как' malloc (1024) ' – Hasturkun

+0

Может ли malloc (0) и realloc (malloc (0), 1024) возвращать одинаковые указатели? ?? if (malloc (0) == realloc (malloc (0), 1024) printf ("possible"); –

32

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

От С99 [раздел 7.20.3]

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

+0

Если возвращен не-NULL-указатель, то в чем его использование: зачем они реализуют такое поведение? –

+1

it не имеет смысла возвращать ненулевой указатель, но также делает malloc (0), спецификация оставляет это открытым, поэтому компилятор может выбрать не обрабатывать malloc (0) специально, так что все остальные mallocs (поскольку проверка на 0 исключена) –

+0

Простота. В некоторых случаях она может упростить базовую библиотеку времени выполнения, если она не требует специальной обработки этого специального случая. :) – jalf

3

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

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

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

2

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

if (size == 0) 
    size = 1; 

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

+0

Я часто видел общую настройку типа 'size = (size + WORD_LEN)% WORD_LEN;' Это удерживает блоки в выровненном по куче слово , что часто увеличивает время доступа (особенно для блочных передач). – TMN

+1

Блоки в куче будут больше, чем выровнены в большинстве систем; как правило, все блоки будут выровнены на кратность 8 байтам; многие будут выровнены на кратное 16 байт. Возвращаемое значение должно быть достаточно хорошо выровнено для любого использования. Для многих систем это означает, что если он используется для двойника, он должен быть выровнен на кратность 8 байтам; для некоторых систем длинный двойной может быть 16 байтов и т. д. И хотя чипы Intel могут обрабатывать неверные данные, это приводит к снижению производительности, поэтому malloc() et al должен - и избегать. –

0

Если таНос (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).

+0

@ user9876: «Если malloc (0) возвращает не-NULL, то ptr является действительным указателем на блок памяти с нулевым размером.» ... Как вы выделяете действительный блок памяти размером «ноль», как вы сказали в Ваше сообщение? –

+0

Как это делает ваше пользовательское приложение? Вы вызываете malloc (0) (если вы находитесь в системе, где возвращается не-NULL). Как система реализует это? Это немного похоже на вопрос, как вы можете иметь файл на диске с размером нуля. Он по-прежнему является допустимым файлом на диске, со всеми обычными метаданными (записи в каталогах и т. Д.), Он имеет только нулевой размер. – user9876

3

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

со страницы человека Solaris:

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

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

+0

Забавно, я упомянул об этом в двойном вопросе здесь ... http://stackoverflow.com/questions/2022335/whats-the-point-in-malloc0/2133585#2133585 – t0mm13b

0

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

Смежные вопросы