Який сенс малок (0)?


Відповіді:


121

Відповідно до технічних характеристик, malloc (0) поверне або "нульовий вказівник, або унікальний покажчик, який можна успішно передати в free ()".

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

artist = NULL;

51
Особисто я вважаю, що встановлення NULL є кращою стратегією між платформами, оскільки безкоштовно () гарантується (за специфікацією), щоб добре працювати на NULL в якості вхідних даних.
Рід Копсей

2
Як згадував К. Росс, деякі платформи технічно можуть повернути вказівник сюди (це "унікальний покажчик, який можна передати безкоштовно"), але якщо ви ставитесь до цього як до char *, це може дати вам недійсний, не припинений статут. На це може бути небезпечно покладатися на кросплатформні ситуації.
Рід Копсей

9
Дуже хочеться, щоб специфікації сказали також "благополучно передано Reallock" також -.-
hanshenrik

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

1
@hanshenrik Хто каже, що ви не можете? realloc()дозволяє передавати будь-який дійсний покажчик, повернутий malloc(). Має вистачити.
glglgl

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
Той факт, що це залежить від впровадження, робить його більш-менш марним - це один із біт crappier стандарту C, і досить багато комітетів зі стандартів (наприклад, PJ Plauger) стогнали про це.

10
Я згоден. Якщо malloc(0)повернуто дійсний вказівник, то malloc()повернення NULLозначає "провал" завжди, і 0це вже не окремий випадок, який є більш послідовним.
Алок Сінгал

1
Оскільки обставини mallocнеможливості отримати пам'ять визначені реалізацією, реалізація може просто визначити, що розподіли розміру-0 завжди незадовільні ( ENOMEM), і тепер malloc(0)повернення 0 (з errno==ENOMEM) є послідовним. :-)
R .. GitHub ЗАСТАНОВИТЬ ДІЙ

6
Чи можете ви reallocвказівник повернувся malloc(0)? Ви можете realloc((char*)NULL)?
Бреден Кращий

3
@Braden Кращий Так для обох.
chux

11

поведінка malloc (0) є специфічною для реалізації. Бібліотека може повернути NULL або мати звичайну поведінку malloc, не виділяючи пам'яті. Що б це не робило, воно має бути десь задокументоване.

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

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

Бути дослідно malloc (0), однак, не дуже корисно. В основному використовується, коли динамічний розподіл дорівнює нулю байтів, і ви не потурбувались про його перевірку.


9
malloc()повинна десь зберігати "інформацію про ведення господарства" (наприклад, цей розмір блоку, виділеного, наприклад, та інші допоміжні дані). Отже, якщо malloc(0)він не повернеться NULL, він використовуватиме пам'ять для зберігання цієї інформації, а якщо ні free(), то буде витоком пам'яті.
Алок Сінгал

Реалізації Malloc виконують ведення запису, який може додавати певну кількість даних за покажчиком, що повертається поверх потрібного розміру.
user7116

1
Споживана пам'ять та виділена пам'ять - це не те саме. У цьому випадку більшість реалізацій поверне унікальний покажчик. Це означає, що частина адресного простору потрібно пожертвувати цим вказівником. Залежно від розподільника, це фактично може означати, що він виділить 1 байт або більше.
Coincoin

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

1
@jldupont: Принаймні бібліотека Microsoft C Run-Time повертає унікальний покажчик для malloc(0). Однак у тій самій реалізації стандартної бібліотеки С realloc(ptr, 0)звільняє ptrта повертає NULL.
Медінок

5

На цій сторінці є відповідь в іншому місці, яка починається з "malloc (0) поверне дійсну адресу пам'яті, діапазон якої буде залежати від типу вказівника, на який виділяється пам'ять". Це твердження є невірним (у мене недостатньо репутації, щоб коментувати цю відповідь безпосередньо, тому не можу прямо тут коментар).

Зробити malloc (0) не автоматично виділить пам'ять правильного розміру. Функція malloc не знає, на що ти кидаєш її результат. Функція malloc покладається виключно на номер розміру, який ви наводите як аргумент. Вам потрібно зробити malloc (sizeof (int)), щоб отримати достатньо місця для зберігання int, наприклад, не 0.


4

malloc(0)для мене не має сенсу, якщо код не спирається на поведінку, специфічну для реалізації. Якщо код призначений для переносу, то він повинен враховувати той факт, що повернення NULL malloc(0)не є невдачею. То чому б просто не призначити NULL так artistчи інакше, оскільки це дійсно вдалий результат, і це менше коду, і не змусить ваших програмістів з технічного обслуговування зайняти час на його з'ясування?

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


3

Тут є багато напівправдивих відповідей, тож ось важкі факти. Сторінка довідки malloc()говорить:

Якщо розмір дорівнює 0, то malloc () повертає або NULL, або унікальне значення вказівника, яке згодом може бути успішно передано вільному ().

Це означає, що немає абсолютно жодної гарантії того, що результат 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 - Відновіть Моніку

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

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

3

Чому ти не повинен цього робити ...

Оскільки значення повернення malloc залежить від реалізації, ви можете отримати вказівник NULL або іншу адресу. Це може призвести до створення переповнення буфера купи, якщо код обробки помилок не перевіряє і розмір, і повернене значення, що призводить до проблем із стабільністю (збої) або ще гірших проблем безпеки.

Розглянемо цей приклад, коли подальший доступ до пам'яті через повернуту адресу зіпсує розмір купі iff дорівнює нулю, а реалізація повертає значення, яке не має 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, де я взяв приклад для подальшого читання.


Посилання було переміщено: wiki.sei.cmu.edu/confluence/display/c/…
Cacahuete Frito

2

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

  • Foo не є NULL , і розмір дорівнює нулю, realloc(foo, size);. Коли ви переходите в не вказівник NULL і розміром від нуля до realloc, realloc веде себе так, ніби ви викликали free (...)
  • foo - NULL, а розмір не дорівнює нулю і перевищує 1 realloc(foo, size);,. Коли ви переходите у вказівник NULL, а розмір не дорівнює нулю, Realloc веде себе так, ніби ви викликали malloc (…)

Сподіваюся, це допомагає, З найкращими побажаннями, Томе.


1

Справді відповісти на поставлене запитання: немає жодних підстав для цього


0

malloc (0) поверне NULL або дійсний покажчик, який може бути правильно переданий у вільний. І хоча здається, що пам'ять, на яку він вказує, марна, або з неї неможливо записати чи прочитати, це не завжди відповідає дійсності. :)

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

Ми очікуємо помилки сегментації тут, але дивно, що ця кількість надрукує 100! Це тому, що malloc насправді просить величезний шматок пам'яті, коли ми дзвонимо malloc вперше. Кожен виклик malloc після цього використовує пам'ять від цього великого шматка. Лише після того, як цей величезний шматок закінчиться, просять нову пам’ять.

Використання malloc (0): якщо ви потрапляєте в ситуацію, коли ви хочете, щоб наступні дзвінки malloc були швидшими, виклик malloc (0) повинен зробити це за вас (за винятком крайових випадків).


1
Письмовий виклик *iможе не збій у вашому випадку, але все-таки невизначена поведінка. Слідкуйте за носовими демонами!
Джон Дворак

1
Так. Це правда. Це конкретно для реалізації. Я перевірив це на MaxOS X та деяких дистрибутивах Linux. Я не пробував цього на інших платформах. Сказавши це, концепція, яку я описав, була описана в книзі "Мова програмування на С" від Брейн Керніган та Денніс Річі.
Sagar Bhosale

Я знаю: надто пізно коментувати це питання. Але іноді про використання malloc(0)це не згадується. У тих реалізаціях, де він повертає значення, яке не стосується NULL, особливо у збірці DEBUG, воно, ймовірно, виділяє БОЛЬШЕ, ніж ви просили, і дає вам вказівник на минуле його внутрішній заголовок. Це дозволяє відчути фактичне використання пам’яті, якщо ви отримаєте це до і після серії розподілів. наприклад: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;або деякі такі.
Джессі Чизгольм

Я читав "Мову програмування на С" від Брейн Керніган та Денніса Річі, і не пам'ятаю, щоб про це нічого говорили malloc(0). Скажіть, будь ласка, на яку главу ви також посилаєтесь? Надання точної цитати також було б непогано.
Андрей Беньковский

0

У Windows:

  • void *p = malloc(0);виділить буфер нульової довжини на локальній купі. Повернений покажчик - це дійсний покажчик купи.
  • mallocв кінцевому рахунку дзвінки, HeapAllocвикористовуючи за замовчуванням кучу виконання C, яка потім дзвонить RtlAllocateHeapі т.д.
  • free(p);використовує HeapFreeдля звільнення буфера 0 довжини на купі. Якщо його не звільнити, це призведе до витоку пам'яті.

0

Насправді це дуже корисно, і (очевидно, IMHO), дозволена поведінка повернення покажчика 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. Те саме стосується і символів. Зверніть увагу, що якщо тип оголошеного вказівника змінено, то діапазон значень буде змінюватися незалежно від malloctypecast, тобто

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

p буде приймати значення від 0 до 255 знаків, оскільки воно оголошено неподписаним int.


5
Креллан має рацію, вказуючи, що ця відповідь неправильна: malloc()нічого не знає про акторський склад (що насправді цілком зайве у С). Визначення зворотного значення 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.