Відповіді:
Відповідно до технічних характеристик, malloc (0) поверне або "нульовий вказівник, або унікальний покажчик, який можна успішно передати в free ()".
Це, в основному, дозволяє нічого не виділяти, але все-таки передавати змінну "художник" на виклик до 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)
повернуто дійсний вказівник, то malloc()
повернення NULL
означає "провал" завжди, і 0
це вже не окремий випадок, який є більш послідовним.
malloc
неможливості отримати пам'ять визначені реалізацією, реалізація може просто визначити, що розподіли розміру-0 завжди незадовільні ( ENOMEM
), і тепер malloc(0)
повернення 0 (з errno==ENOMEM
) є послідовним. :-)
realloc
вказівник повернувся malloc(0)
? Ви можете realloc((char*)NULL)
?
поведінка malloc (0) є специфічною для реалізації. Бібліотека може повернути NULL або мати звичайну поведінку malloc, не виділяючи пам'яті. Що б це не робило, воно має бути десь задокументоване.
Зазвичай він повертає покажчик, який є дійсним та унікальним, але НЕ повинен бути відмежований. Також зауважте, що він МОЖЕТ споживати пам'ять, хоча насправді нічого не виділив.
Можна перерозподілити ненульовий покажчик malloc (0).
Бути дослідно malloc (0), однак, не дуже корисно. В основному використовується, коли динамічний розподіл дорівнює нулю байтів, і ви не потурбувались про його перевірку.
malloc()
повинна десь зберігати "інформацію про ведення господарства" (наприклад, цей розмір блоку, виділеного, наприклад, та інші допоміжні дані). Отже, якщо malloc(0)
він не повернеться NULL
, він використовуватиме пам'ять для зберігання цієї інформації, а якщо ні free()
, то буде витоком пам'яті.
malloc()
не поверне, або повернеться NULL
.
malloc(0)
. Однак у тій самій реалізації стандартної бібліотеки С realloc(ptr, 0)
звільняє ptr
та повертає NULL.
На цій сторінці є відповідь в іншому місці, яка починається з "malloc (0) поверне дійсну адресу пам'яті, діапазон якої буде залежати від типу вказівника, на який виділяється пам'ять". Це твердження є невірним (у мене недостатньо репутації, щоб коментувати цю відповідь безпосередньо, тому не можу прямо тут коментар).
Зробити malloc (0) не автоматично виділить пам'ять правильного розміру. Функція malloc не знає, на що ти кидаєш її результат. Функція malloc покладається виключно на номер розміру, який ви наводите як аргумент. Вам потрібно зробити malloc (sizeof (int)), щоб отримати достатньо місця для зберігання int, наприклад, не 0.
malloc(0)
для мене не має сенсу, якщо код не спирається на поведінку, специфічну для реалізації. Якщо код призначений для переносу, то він повинен враховувати той факт, що повернення NULL malloc(0)
не є невдачею. То чому б просто не призначити NULL так artist
чи інакше, оскільки це дійсно вдалий результат, і це менше коду, і не змусить ваших програмістів з технічного обслуговування зайняти час на його з'ясування?
malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)
або, malloc(some_variable_which_might_be_zero)
можливо, вони могли б використовувати їх, хоча знову ж таки, ви повинні бути обережнішими, щоб не трактувати повернення NULL як збій, якщо значення 0, але розмір 0 повинен бути в порядку.
Тут є багато напівправдивих відповідей, тож ось важкі факти. Сторінка довідки malloc()
говорить:
Якщо розмір дорівнює 0, то malloc () повертає або NULL, або унікальне значення вказівника, яке згодом може бути успішно передано вільному ().
Це означає, що немає абсолютно жодної гарантії того, що результат malloc(0)
або унікальний, або не NULL. Єдина гарантія надається визначенням free()
, знову ж таки, ось що говорить сторінка:
Якщо ptr - NULL, жодна операція не виконується.
Отже, що б не malloc(0)
повернулося, це можна сміливо передати free()
. Але так може NULL
вказувати.
Отже, писати artist = malloc(0);
нічим не краще, ніж писати artist = NULL;
malloc(0)
можна повернути, скажімо, 0x1, і free()
міг би перевірити особливий випадок 0x1 так само, як це стосується 0x0.
NULL
або унікальний покажчик". натомість "або нульовий вказівник, або вказівник на виділений простір". Не існує єдиної вимоги. OTOH, повертаючи унікальне спеціальне значення, може порушити код, який розраховує на унікальні значення. Можливо, питання кутового випадку для SO.
man
може також документувати форму, визначену реалізацією, що використовується в * nix. У цьому випадку це не так, але це все ще не є канонічним джерелом для генерала С.
Чому ти не повинен цього робити ...
Оскільки значення повернення 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, де я взяв приклад для подальшого читання.
Правда, я ніколи цього не бачив, це перший раз, коли я бачив цей синтаксис, можна сказати, класичний випадок перевиконання функції. У зв’язку з відповіддю Рида, я хотів би зазначити, що існує подібна річ, яка виглядає як перевантажена функція realloc
:
realloc(foo, size);
. Коли ви переходите в не вказівник NULL і розміром від нуля до realloc, realloc веде себе так, ніби ви викликали free (...)realloc(foo, size);
,. Коли ви переходите у вказівник NULL, а розмір не дорівнює нулю, Realloc веде себе так, ніби ви викликали malloc (…)Сподіваюся, це допомагає, З найкращими побажаннями, Томе.
malloc (0) поверне NULL або дійсний покажчик, який може бути правильно переданий у вільний. І хоча здається, що пам'ять, на яку він вказує, марна, або з неї неможливо записати чи прочитати, це не завжди відповідає дійсності. :)
int *i = malloc(0);
*i = 100;
printf("%d", *i);
Ми очікуємо помилки сегментації тут, але дивно, що ця кількість надрукує 100! Це тому, що malloc насправді просить величезний шматок пам'яті, коли ми дзвонимо malloc вперше. Кожен виклик malloc після цього використовує пам'ять від цього великого шматка. Лише після того, як цей величезний шматок закінчиться, просять нову пам’ять.
Використання malloc (0): якщо ви потрапляєте в ситуацію, коли ви хочете, щоб наступні дзвінки malloc були швидшими, виклик malloc (0) повинен зробити це за вас (за винятком крайових випадків).
*i
може не збій у вашому випадку, але все-таки невизначена поведінка. Слідкуйте за носовими демонами!
malloc(0)
це не згадується. У тих реалізаціях, де він повертає значення, яке не стосується NULL, особливо у збірці DEBUG, воно, ймовірно, виділяє БОЛЬШЕ, ніж ви просили, і дає вам вказівник на минуле його внутрішній заголовок. Це дозволяє відчути фактичне використання пам’яті, якщо ви отримаєте це до і після серії розподілів. наприклад: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;
або деякі такі.
malloc(0)
. Скажіть, будь ласка, на яку главу ви також посилаєтесь? Надання точної цитати також було б непогано.
У Windows:
void *p = malloc(0);
виділить буфер нульової довжини на локальній купі. Повернений покажчик - це дійсний покажчик купи.malloc
в кінцевому рахунку дзвінки, HeapAlloc
використовуючи за замовчуванням кучу виконання C, яка потім дзвонить RtlAllocateHeap
і т.д.free(p);
використовує HeapFree
для звільнення буфера 0 довжини на купі. Якщо його не звільнити, це призведе до витоку пам'яті.Насправді це дуже корисно, і (очевидно, IMHO), дозволена поведінка повернення покажчика NULL порушена. Динамічний покажчик корисний не тільки тим, на що він вказує, але й тим, що його адреса є унікальною. Повернення NULL видаляє це друге властивість. У всіх вбудованих програм, які я програю (досить часто насправді), є така поведінка.
Не впевнений, згідно з деяким я знайденим випадковим вихідним кодом malloc , вхід 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 тощо.
Відповідно до відповіді Рід Копсі та сторінки чоловіка 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 знаків, оскільки воно оголошено неподписаним int.
malloc()
нічого не знає про акторський склад (що насправді цілком зайве у С). Визначення зворотного значення malloc(0)
буде викликати невизначене поведінку.
Тут просто виправити помилкове враження:
artist = (char *) malloc(0);
ніколи не повернеться NULL
; це не те саме, що artist = NULL;
. Написати просту програму і порівняти artist
з NULL
. if (artist == NULL)
неправдиво і if (artist)
є правдою.