Питання Чи я відкидаю результат malloc?


В це питання, хтось запропонував в a коментар що я повинен ні кинути результат malloc, тобто

int *sieve = malloc(sizeof(int) * length);

а не:

int *sieve = (int *) malloc(sizeof(int) * length);

Чому це так?


2043
2018-03-03 10:13


походження


Крім того, більш ретельним є написати сито = malloc (sizeof * sieve * length); - William Pursell
stackoverflow.com/q/7545365/168175 - Flexo♦
@KarolyHorvath це не єдина причина. Інший (і найважливіший, IMHO) - зробити код більш гнучким (але не менш безпечним або надійним), якщо тип sieve змінюється від int до, скажімо, плаваючого. malloc( sizeof *sieve * length ); буде працювати незалежно від типу, а лиття потребує технічного обслуговування, не додає нічого і може створювати важко для відстеження помилок. - MestreLion
@MestreLion: "і може утворити важко відслідковувати помилки" - все, що вам потрібно зробити, це увімкнути попередження компіляції, і це ніколи станеться. ОТОХ, якщо ти цього не робиш ... ти дійсно цього заслужив. і знову ж таки, я не захисник її використання .. все, що я кажу, що це насправді не так важливо ... це все в основному нешкідливі :) - Karoly Horvath
Дивись також Зокрема, що небезпечно щодо відтворення результатів malloc()? - Jonathan Leffler


Відповіді:


Немає; ви ні віддай результат, оскільки:

  • Це непотрібно, як void * в цьому випадку автоматично і безпечно підвищується до будь-якого іншого типу вказівника.
  • Він додає безлад до коду, видалення не дуже зручно читати (особливо якщо тип покажчика довгий).
  • Це змушує вас повторювати себе, що, як правило, погано.
  • Це може сховати помилку, якщо ви забули включити <stdlib.h>. Це може спричинити аварії (або, що ще гірше, ні призведе до катастрофи до кінця, в деякій зовсім іншій частині коду). Розглянемо, що відбувається, якщо покажчики та цілі числа різняться; тоді ви сховаєте попередження, відливши та може втратити біти повернутої адреси. Примітка. Починаючи з C11, неявні функції виходять з C, і ця точка більше не є актуальною, оскільки не існує автоматичного припущення, що повертаються незадекларовані функції int.

Як роз'яснення, зверніть увагу, що я сказав "ти не кидаєш", а не "ти не маєш" треба щоб кинути ". На мій погляд, це неможливість включити акторський твір, навіть якщо ви це правильно. Просто не має ніяких переваг для цього, однак велика кількість потенційних ризиків, а також відтворення означає, що ви не знаєте про ризики.

Також зауважте, як зазначають коментатори, що наведені вище розмови стосуються прямих C, а не C ++. Я дуже твердо вірю в C і C ++ як окремі мови.

Щоб додати далі, ваш код необов'язково повторює інформацію про тип (int), що може призвести до помилок. Краще скасувати покажчик, який використовується для збереження зворотного значення, щоб "зафіксувати" обидва ці об'єкти:

int *sieve = malloc(length * sizeof *sieve);

Це також рухає length на фронт для збільшеної видимості, і знімає надлишкові круглі дужки sizeof; Вони потрібні лише коли аргументом є ім'я типу. Багато людей, здається, не знають (або ігнорують) це, що робить їх код більш розмовним. Пам'ятай: sizeof це не функція! :)


Під час руху length на фронт може збільшити видимість в деяких рідкісних випадках, слід також звернути увагу, що в загальному випадку краще б написати вираз як:

int *sieve = malloc(sizeof *sieve * length);

Оскільки зберігання sizeof По-перше, в даному випадку, забезпечується, що множення виконується щонайменше size_t математика

Порівняйте: malloc(sizeof *sieve * length * width) проти malloc(length * width * sizeof *sieve) другий може переповнити length * width коли width і length менші типи, ніж size_t.


1917
2018-03-03 10:17



@unwind Ні, навіть за строгим C-кодом, я використовую трансляцію для мобільності. Те, що я маю на увазі під час переносу, є портативним для старих версій стандарту c, де void * не отримують просування. Я цитую з "Мова програмування C" K & R: "У C правильний метод полягає в тому, щоб оголосити, що malloc повертає вказівник на void, а потім явним чином примушує покажчик у потрібний тип з атрибутом". Причина, я нав'язую таку вимогу, полягає в тому, що у вас не завжди є вибір того, який компілятор ви можете використовувати. - chacham15
@ chacham15 Хоча були і попередні версії C мова де був потрібний кидок (K & R C не мав void* тип!), ці версії ніколи не були стандарт. C89 (перша стандартизована версія) вимагала цього malloc повернутися void* і це void* бути неявно конвертованим. - jamesdlin
@Сомен Це, можливо, занадто незрозуміло; без включення типу повернення int буде прийнято, що може бути менше, ніж void *, тому призначення може сформувати попередження, яке було б пригнічено відтворенням. Побачити це питання наприклад. - unwind
@unwind Більшість компіляторів попередить вас про неявно визначену функцію ... (тому я не бачу небезпеки трансляції) - DarthRubik
@ n.m. Добре. Я думаю, це погано припустити, що кожен, хто читає тут, має певний компілятор. Крім того, оскільки C11 вся концепція "неявної функції" не була, я не знав цього. Тим не менш, я не бачу точки додавання безглуздого актора. Ви також робите це int x = (int) 12; просто, щоб все зрозуміло? - unwind


У C вам не потрібно віддавати повернену значення malloc. Покажчик на порожнечу повертається malloc автоматично перетворюється на правильний тип. Однак, якщо ви хочете, щоб ваш код було скомпілювати за допомогою компілятора C ++, потрібний акт. Переважна альтернатива серед спільноти полягає у використанні наступного:

int *sieve = malloc(sizeof *sieve * length);

який додатково звільняє вас від необхідності турбуватися про зміну правої сторони виразу, якщо ви коли-небудь зміните тип sieve.

Лиття погано, як зазначають люди. Спеціально покажчик скидає.


327
2018-03-03 10:17



@MAKZ я б це стверджував malloc(length * sizeof *sieve) робить його схожим sizeof є змінною - так я думаю malloc(length * sizeof(*sieve)) більш читабельна. - Michael Anderson
І malloc(length * (sizeof *sieve)) все ще читається. ІМХО. - Toby Speight
@ Майкл Андерсон () випустіть в сторону, зверніть увагу, що ваш запропонований стиль переключив порядок. Розглянемо, коли кількість елементів обчислюється як length*width, зберігаючи sizeof Спочатку в цьому випадку страховка множення виконується принаймні size_t математика Порівняйте malloc(sizeof( *ptr) * length * width) проти malloc(length * width * sizeof (*ptr)) - другий може переповнити length*width коли width,length є менші типи, що size_t. - chux
@chux це не є очевидним, але відповідь була відредагована так, що мій коментар менш доречний - оригінальна пропозиція була malloc(sizeof *sieve * length) - Michael Anderson
C не C ++. Прикидаючись, що вони, в кінцевому підсумку, призведуть до плутанини та смутку. Якщо ви використовуєте C + +, то також випадок C-стиля теж поганий (якщо ви не використовуєте дуже старий компілятор C ++). І static_cast>()(або reinterpret_cast<>() ) не сумісний з будь-яким діалектом C. - David C.


ви робити кидай, тому що:

  • Це робить ваш код більш портативний між C і C + +, і, як свідчить досвід, багато хто програмісти стверджують, що вони пишуть в C, коли вони дійсно пишуть у C ++ (або C, а також локальних розширень компілятора).
  • Не вдалося це зробити може сховати помилку: зверніть увагу на всі приклади СУ, які можуть плутати, коли писати type * проти type **.
  • Ідея, що вона не дозволяє вам помітити, що ви не змогли #include відповідний файл заголовка пропускає ліс для дерев. Це те ж саме, що "не хвилюйся над тим, що не вдалося попросити компілятора поскаржитися на те, що не бачив прототипи, - це жорстокий stdlib.h - важлива річ, яку потрібно пам'ятати!"
  • Це змушує додаткова пізнавальна перехресна перевірка. Він поміщає (нібито) потрібний тип поруч із арифметикою, яку ви виконуєте, для вихідного розміру цієї змінної. Я впевнений, що ви можете зробити так, що це показує malloc() помилки трапляються набагато швидше, коли є акт. Як і твердження, анотації, які показують намір зменшити помилки.
  • Повторюючи себе таким чином, що машина може перевірити, це найчастіше чудово ідея Фактично, це те, що є твердженням, і це використання атаки є твердженням. Затвердження все ще є найбільш загальним методом для коректного отримання коду, оскільки Тьюрінг виробив ідею так багато років тому.

292
2018-02-14 16:15



@ulidtko Якщо ви не знали, то можна написати код, який складається як з C, так і з C ++. Насправді більшість файлів заголовків подібні до цього, і вони часто містять код (макроси та вбудовані функції). Маючи а .c/.cpp файл для компіляції, тому що обидва вони не дуже корисні, але однією справою є додавання C ++ throw Підтримка при складанні з C + + компілятором (але return -1; коли компілюється з компілятором C, або будь-який інший). - hyde
Якщо хтось зробив виклики malloc вбудованим у заголовок, я не був би вражений, #ifdef __cplusplus та extern "C" {} для цієї роботи, не додаючи додаткових копій. - paulm
Ну, точка 1 не має значення, оскільки C! = C ++, інші точки також тривіальні, якщо ви використовуєте змінна в твоїй malloc дзвонити: char **foo = malloc(3*sizeof(*foo)); якщо цілком повноцінний: 3 покажчика на покажчиках символів. потім петлі і роби foo[i] = calloc(101, sizeof(*(foo[i])));. Виділити масив з 101 символів, акуратно ініціалізований до нулів. Ніякого кидка не потрібно. змінити декларацію на unsigned char або будь-який інший тип, у цьому відношенні, і ви все ще добре - Elias Van Ootegem
Коли я навчив, я отримав це, там він приходить! Фантастичний відповідь. Вперше тут у StackOverflow я наведу два протилежні відповіді! +1 Ні, ти не кидаєш, а +1, так, ти кидаєш! ЛОЛ. Ви, хлопці, приголомшливі. І для мене та моїх учнів я розумів: я кидаю. Різновиди помилок, зроблених учнями, легше виявляються під час відтворення. - Dr Beco
@ Лєушенко: повторювати себе таким чином, що не можна перевірити машиною чи місцевою інспекцією, це погано. Повторювання себе таким чином, що можна перевірити за допомогою таких засобів, є менш поганим. Дано struct Zebra *p; ... p=malloc(sizeof struct Zebra);, malloc не може уникнути розповсюдження інформації про тип p, але ні компілятор, ні місцевий код перевірки не виявить жодної проблеми, якщо один тип змінився, але інший не. Змініть код на p=(struct Zebra*)malloc(sizeof struct Zebra); і компілятор здибає, якщо тип лити не збігається p, і місцевий інспекція покаже ... - supercat


Як інші заявили, це не потрібно для C, але для C ++. Якщо ви думаєте, що збираєтеся скомпілювати свій C-код за допомогою компілятора C ++, з якогось причин можна використовувати макрос, наприклад:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

Таким чином, ви все одно можете писати це дуже компактно:

int *sieve = NEW(int, 1);

і він буде компілювати для C і C ++.


148
2018-03-03 11:17



Оскільки ви все-таки використовуєте макрос, чому б ви не використовували new у визначенні C ++? - Hosam Aly
Тому що немає підстав для цього. Це, головним чином, для програм C, які складаються з компілятором C ++. Якщо ви збираєтеся використовувати "новий", єдине, що ви отримуєте - це проблеми. Тоді вам потрібен також макрос безкоштовно. І вам потрібен макрос для звільнення масиву, диференціація якої не існує в C. - quinmars
Не кажучи вже про те, чи це не ви, хто звільняє пам'ять, але, можливо, бібліотеку C, яку ви використовуєте, тощо. Багато можливих проблем без користі. - quinmars
@ Хос: Так, це безперечно є. Якщо ви використовуєте new ви повинні використовувати delete і якщо ви використовуєте malloc() ти повинен ти free(). Ніколи не змішувати їх. - Graeme Perrow
Якщо хтось буде використовувати цей підхід, зателефонуйте до макросу NEW це, мабуть, погана ідея, оскільки ресурс ніколи не повертається, використовуючи delete(або DELETE), тому ви змішуєте свій словниковий запас. Замість цього називайте це MALLOC, точніше CALLOC в цьому випадку буде більше сенсу. - mah


Від Вікіпедія

Переваги кастингу

  • Включаючи cast може дозволити програмі C або функції скомпілювати як C ++.

  • Литі допускають до 1989 року версії malloc, які спочатку повернули символ *.

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

Недоліки для відливання

  • Відповідно до стандарту ANSI C, голосування є зайвим.

  • Додавання відтворення може приховати відмову включення заголовка stdlib.h, в   який знайдено прототип для malloc. За відсутності а   прототип для malloc, стандарт вимагає, щоб компілятор C   Припустимо, malloc повертає int. Якщо не було броненостей, це попередження   видається, коли це ціле число присвоюється покажчику; однак, з   це лиття, це попередження не виробляється, приховуючи помилку. Напевно   архітектури та моделі даних (наприклад, LP64 на 64-бітових системах, де   довгий і покажчики 64-bit і int 32-бітні), ця помилка може   насправді призводять до невизначеної поведінки, як це неявно оголошується   malloc повертає 32-бітове значення, тоді як фактично визначена функція   повертає 64-розрядне значення. Залежно від виклику конвенцій і пам'яті   макет, це може призвести до розбиття стеків. Це питання менш імовірно   щоб залишатися непоміченими в сучасних компіляторах, оскільки вони рівномірно виробляють   попередження про те, що незареєстрована функція була використана, тому попередження буде   все ще з'являються. Наприклад, поведінка GCC за умовчанням - показати a   попередження про те, що називається "несумісна неявна декларація вбудованого"   функція "незалежно від того, присутній чи ні.

  • Якщо тип дзеркала змінюється при його декларації, можна   Крім того, потрібно змінити всі лінії, де викликається і відправляється malloc.

Хоча Метод malloc без відливання є найкращим методом, і найбільш досвідчені програмісти вибирають його, ви повинні використовувати той, який вам подобається, знаючи про проблеми.

i.e .: Якщо вам потрібно скомпілювати програму C як C ++ (хоча це окрема мова), ви повинні використовувати malloc з відливанням


96
2017-10-09 21:23



Що "Кастинг може допомогти розробнику виявляти невідповідності в розбитті типу, якщо змінюється тип покажчика призначення, особливо якщо покажчик оголошений далеко від malloc()дзвонити"означає? Не могли б ви навести приклад? - Cool Guy
@Крутий хлопець: Див раніше коментар на іншу відповідь. Але зверніть увагу, що p = malloc(sizeof(*p) * count) Ідіом автоматично збирає зміни в типі, тому вам не потрібно отримувати попередження та міняти щось щось. Таким чином, це не є реальною перевагою, а найкращою альтернативою для невиправлення. - Peter Cordes
Це правильна відповідь: Є плюси і мінуси, і це зводиться до питання смаку (якщо код не повинен скомпілювати як C ++ - тоді акт є обов'язковим). - Peter A. Schneider
Точка 3 є спокійною, оскільки якщо тип дескриптора змінюється при його декларації, слід перевірити кожен примірник malloc, realloc та вільного inolving цього типу. Кастинг змусить вас це робити. - Michaël Roy


У C ви можете неявно перетворити пустий покажчик на будь-який інший вид вказівника, тому кидатися не потрібно. Використання одного може запропонувати випадковому спостерігачеві, що є якісь причини, чому потрібна одна особа, що може бути оманливим.


94
2018-03-03 10:18





Ви не відкидаєте результат malloc, тому що це додає безглуздий безлад до вашого коду.

Найпоширеніша причина того, чому люди віддають результат malloc, полягає в тому, що вони не знають, як працює мова С. Це попереджувальний знак: якщо ви не знаєте, як працює певний мовний механізм, тоді ні Вгадай. Подивіться на це або попросіть про переповнення стек.

Деякі коментарі:

  • Порожній покажчик може бути перетворено в / з будь-якого іншого типу вказівника без явного відтворення (C11 6.3.2.3 та 6.5.16.1).

  • Проте, C + + не допускатиме пряму трансляцію void* і інший тип покажчика. Отже, в C ++, виклик був би правильним. Але якщо ви програмуєте в C ++, то ви повинні використовувати new а не malloc (). І ви ніколи не повинні компілювати код C за допомогою компілятора C ++.

    Якщо вам потрібно підтримувати як C, так і C ++ з тим же вихідним кодом, використовуйте компілятор, щоб відзначити відмінності. Не намагайтеся наситити обидві стандарти мови з тим самим кодом, оскільки вони несумісні.

  • Якщо компілятор C не може знайти функцію, оскільки ви забули включити заголовок, ви отримаєте помилку компілятора / посилання на це. Так що якщо ви забули включити <stdlib.h> це не biggie, ви не зможете побудувати свою програму.

  • На стародавніх компіляторах, які йдуть за версією стандарту, яким більше 25 років, забуваючи включити <stdlib.h> призведе до небезпечної поведінки. Оскільки в цьому стародавньому стандарті функції без видимого прототипу неявно перетворюють тип повернення на int. Відразу вилучення результату з malloc явно призведе до приховання цієї помилки.

    Але це дійсно не питання. Ви не використовуєте 25-річний комп'ютер, так чому б ви використали 25-річний компілятор?


83
2018-03-20 15:53



"безглуздий хаос" - це звільняюча гіпербола, яка спричиняє будь-яку можливість переконати когось, хто ще не згоден з вами. Залишається звичайно не безглуздим; Відповіді Рон Бурк і Каз роблять аргументи на користь кастингу, про що я дуже згоден. Незалежно від того, чи зацікавлені сторони вважають це більш важливим питанням, про яке ви говорите. Для мене ваші турботи виглядають порівняно незначно порівняно з їхніми. - Don Hatch
"Порожній покажчик може бути перетворено в / з будь-якого іншого типу вказівника без явного відтворення" не підтримується в 6.3.2.3. Можливо, ви думаєте про "покажчик на будь-який тип об'єкта"? "недійсний покажчик" і "покажчик на функцію" не так легко конвертуються. - chux
Дійсно, посилання було неповним. Відповідна частина для "імпліцієнта" - це правило простого призначення 6.5.16.1. "один операнд є покажчиком на тип об'єкта, а інший є покажчиком на кваліфіковану або некваліфіковану версію порожнечі". Я додав цю посилання на відповідь на повноту. - Lundin


У C ви отримаєте неявне перетворення з void* на будь-який інший (дані) покажчик.


82
2018-03-03 10:16



@Jens: ОК, може бути, більш правильною формулюванням є "неявне перетворення". Як і використання інтегральної змінної у виразі з плаваючою комою. - EFraim
@EFraim Це фактично призведе до трансляції, а неявного до цього. - Mad Physicist


Кастинг значення, яке повертає malloc() зараз не потрібен, але я хотів би додати одну точку, яку, здається, ніхто не вказав:

У стародавні часи, тобто раніше ANSI C забезпечує void * як загальний тип покажчиків char * є типом для такого використання. У цьому випадку програвання може припинити попередження компілятора.

Довідка: C FAQ


62
2018-06-09 17:31



Завершення попереджень компілятора - це погана ідея. - Albert van der Horst
@AlbertvanderHorst Не, якщо ви це робите, вирішивши конкретну проблему, то попереджаєте вас про це. - Dan Bechard
@ Дан Якщо, вирішуючи точну проблему, мається на увазі перезаписання підпрограми для повернення сучасних ANSI C-типів замість char *, я згоден. Я б не назвав це замиканням компілятора. Не надавайте менеджерів, які наполягають на тому, що немає попереджень компілятора, замість того, щоб використовувати їх під час кожного перекомпіляції, щоб знайти можливі проблеми. Гротедж Альберт - Albert van der Horst