Чому повернути значення, що повертається безкоштовно, недійсне?


82

Я читаю книгу ( Програмування з POSIX Threads від Butenhof, 1997), яка використовує C, і я натрапив на наступний рядок:

(void)free(data);

Ось dataлише вказівник на виділену структуру,

data = malloc(sizeof(my_struct_t));

Чому результат freeкинуто void?

З мого розуміння С, це, мабуть, не має сенсу з двох причин:

  • Безкоштовна функція вже повертається void
  • Код не використовує значення, що повертається (він навіть не присвоюється змінній)

Книга була написана в 1997 році. Це якась спадщина?

Автор згадує, що приклади були запущені на Digital Unix 4.0d, але я досі не можу уявити собі причину коли-небудь передавати результат функції, якщо ви не збираєтесь використовувати цей результат.


Деякі можливі пояснення можна знайти тут: stackoverflow.com/questions/689677/…
Тимбо

3
З цікавості, яка дата виходу вашої книги С? (Що це за книга?) Якщо це було раніше приблизно 1995 року, це може бути певне виправдання - стандартні компілятори C раніше не були всюдисущими. Якщо він опублікований після цього і все ще містить акторський склад (і не пояснює чому), турбуйтеся про те, які інші шкідливі звички він вас навчає. Отримайте новітню книгу!
Джонатан Леффлер


3
@JonathanLeffler, як згадувалося в моїй оригінальній публікації, книга була опублікована в 1997 році і використовувала UNIX 4.0d. Книга "Програмування за допомогою ниток POSIX" Девіда Р. Бутенгофа. Поки що вона була дуже інформативною і написана одним з оригінальних учасників стандарту ниток POSIX.
Адам Джонстон

6
Я використовував свою копію цього минулого тижня - так, це все ще корисно. Це було написано на прикладі "всюдисущого стандарту С" (я сказав "про 1995 рік"). "UNIX 4.0d" звучить як Digital UNIX - саме там працював Butenhof, і передмова згадує це. Ставтесь до акторського складу free()як до дивацтва у книзі, яку вам не потрібно наслідувати. Це було напіврелевантним колись давно, але вже не актуальне.
Джонатан Леффлер

Відповіді:


100

Якщо ми говоримо про стандартну freeфункцію, то її прототипом є

void free(void *ptr);

Тому акторський склад є абсолютно марним.
Зараз деякі спекуляції.

Автор, можливо, забув включити stdlib.hзаголовок, декларуючи цей прототип, тому компілятор припускає тип повернення його як int. Тепер під час статичного аналізу цього коду компілятор попереджав про невикористане повернене значення того, що він вважає voidнефункціональним. Такі застереження зазвичай замовчуються шляхом додавання анотації до void.


50
Але зауважте, що якщо акторський склад був введений з причини, про яку думали, то використовувати його для заглушення попередження невірно . У такому випадку компілятор присвоює інший тип, freeніж він є насправді, в результаті чого виклик має невизначену поведінку (припустимо, семантика C90, коли виклик незадекларованої функції по суті не демонструє UB у всіх випадках). На практиці правдоподібно, що це призведе до сумлінної поведінки в деяких системах. Правильне рішення - надати правильну заяву функції.
Джон Боллінгер

11
Зокрема, приклади в "Програмування за допомогою ниток POSIX" неодноразово не містять відповідних стандартних заголовків. Можливо, це була якась погана практика автора, вони могли використовувати нестандартну настройку компілятора, яка включала всі стандартні файли за замовчуванням.
Лундін

74

Це була б спадщина!

Перш ніж був стандарт С, free()функція мала б (неявно) тип int- тому що ще не було надійного типу voidдля повернення. Значення не повернуто

Коли код був вперше модифікований для роботи зі стандартними компіляторами C, він, ймовірно, не включав <stdlib.h>(тому що він не існував раніше стандарту). Старий код писав extern char *malloc();(можливо, без extern) для функцій розподілу (аналогічно для calloc()і realloc()), і не потрібно було декларувати free(). І тоді код приведе повернення значення правильному типу - тому що це було потрібно принаймні в деяких системах (включаючи ту, на якій я вивчив C).

Десь пізніше (void)доданий склад доданий для того, щоб сказати компілятору (або, що більше ймовірно, lint), що "повернене значення free()свідомо ігнорується", щоб уникнути скарги. Але було б краще додати <stdlib.h>і нехай його декларація extern void free(void *vp);скаже lintабо компілятору, що ігнорувати значення не було.

JFTR: Ще в середині 80-х років ICL Perq спочатку був архітектурою, орієнтованою на слова, і char *адреса для місця розташування пам’яті була дуже різною цифрою від вказівника «what_else» на те саме місце. Важливо було char *malloc()якось заявити ; важливо було передати результат від нього будь-якому іншому типу вказівника. Акторський склад фактично змінив число, яке використовує процесор. (Також було дуже радісно, ​​коли основна пам'ять в наших системах була оновлена ​​з 1 МіБ до 2 МіБ - оскільки ядро ​​використовувало приблизно 3/4 МіБ, це означало, що користувацькі програми могли використовувати 1 1/4 МіБ перед тим, як створювати пейджинг тощо).


9
Я щойно відкрив копію K&R, 1-го видання, що містить реалізацію free()на с. 177, що неявно повертається int.
ex nihilo

9
Звичайно - voidдодавались до деяких систем (Unix System III, можливо) до виходу стандарту, але це не було частиною C, коли було написано K&R 1st Edn (1978). Функція, яка не повертала значення, була оголошена без типу повернення (що означало повернення int), і поки ви не використовували значення, яке не поверталося, не виникало проблем. Стандарт C90 повинен був вважати такий вид коду дійсним - він би невдало провалився, якби не стандарт. Але C99 видалив intправила "неявної " та "неявної функції оголошення". Не весь код у світі наздогнав.
Джонатан Леффлер

5
Оп стверджує, що книга була написана в 1997 році. Те, про що ви тут говорите, дуже рано є стандартним "K&R C", і малоймовірно, що хтось би написав книгу про це взагалі. Єдиною такою книгою, яка мені відома, було справді перше видання K&R.
Лундін

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

Хтось використовує (void)акторський склад для printf()??
Луїс Колорадо

11

Цей акторський склад не потрібен. Це, мабуть, не було б тоді, як C було стандартизовано у вигляді C89.

Якби це було, це було б через неявну декларацію . Зазвичай це означало, що людина, що пише код, забула, #include <stdlib.h>і використовувався статичний аналізатор. Це не найкращий шлях, і набагато краща ідея була б просто #include <stdlib.h>замість цього. Ось деякі формулювання C89 щодо неявного декларування:

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

extern int identifier();

з’явився.

Але це дивно, оскільки вони не дають результату mallocжодного, mallocі freeзнаходяться в одному файлі заголовка.

Можливо також, що це просто помилка чи якийсь спосіб сказати читачеві, що freeне дає результату.


5
Тільки тому, що мова стає стандартизованою, не означає, що всі миттєво оновлюють свою ланцюжок інструментів та код, щоб відповідати їй. Це може бути правдоподібно для старого типу "K&R" C, що він тримався ще близько 8 років. Проте я погоджуюся, що дивно, що інструмент для статичного аналізу вимагатиме відтворення, freeале не для malloc.
dan04

4
@ dan04 Ви зазвичай використовуєте результат malloc;) Я не прихильник писати такі речі, як (void) printf (...), щоб зупинити компілятор, плюючи попередження, але "повинен компілювати без будь-яких попереджень, навіть дурних" - це щось таке трапляється в багатьох проектах.
Річардб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.