Чому чому деякі люди кидають покажчик перед тим, як звільнити його?


167

Я працюю над старою базою коду і майже кожен виклик free () використовує роль аргументу. Наприклад,

free((float *)velocity);
free((float *)acceleration);
free((char *)label);

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

Чи є технічні причини використовувати ці ролі? Я навіть не бачу багато прагматичних причин використовувати їх. Який сенс нагадувати собі тип даних безпосередньо перед тим, як звільняти їх?

EDIT: Це запитання не є дублікатом іншого питання. Інше питання - це особливий випадок цього питання, який, на мою думку, є очевидним, якби близькі виборці прочитали всі відповіді.

Колофон: Я даю галочку "const відповідь", тому що це справжня причина, чому це може знадобитися зробити; проте відповідь про те, що це звичай до ANSI C (принаймні серед деяких програмістів), здається, є причиною його використання в моєму випадку. Тут багато хороших балів. Дякую за внесок.


13
"Який сенс нагадувати собі тип даних безпосередньо перед його звільненням?" Може, знати, скільки пам'яті буде звільнено?
m0skit0

12
@Codor Компілятор не робить операційну локалізацію, як це робить операційна система.
m0skit0

20
@ m0skit0 "Може бути, знати скільки пам'яті буде звільнено?" Вводити не потрібно, щоб знати, скільки безкоштовно. У ролях з цієї причини лише погана кодування.
user694733

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

66
У стародавні часи, коли динозаври ходили землею і писали програми програмування, я вважаю, що void*в попередньому стандарті C це було не тільки, а тільки char*. Отже, якщо ваші археологічні знахідки виявляють код, який передає параметр free (), я вважаю, що він повинен бути або з того періоду часу, або написаний істотою з того часу. Я не можу знайти жодного джерела для цього, тому не хочу відповідати.
Лундін

Відповіді:


171

Для усунення попереджень компілятора, якщо покажчики є, може знадобитися кастинг const. Ось приклад коду, який викликає попередження без викидання аргументу безкоштовно:

const float* velocity = malloc(2*sizeof(float));
free(velocity);

І компілятор (gcc 4.8.3) говорить:

main.c: In function main’:
main.c:9:5: warning: passing argument 1 of free discards const qualifier from pointer target type [enabled by default]
     free(velocity);
     ^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected void *’ but argument is of type const float *’
 extern void free (void *__ptr) __THROW;

При використанні free((float*) velocity);компілятор перестає скаржитися.


2
@ m0skit0 не пояснює, чому хтось би звернувся float*до звільнення. Я спробував free((void *)velocity);з gcc 4.8.3. Звичайно, це не спрацювало б із старовинним компілятором
Маносом Ніколаїдісом

54
Але навіщо вам динамічно розподіляти постійну пам'ять? Ви ніколи не могли ним скористатися!
Nils_M

33
@Nils_M - це спрощений приклад зробити точку зору. Те, що я зробив у фактичному коді функції, - це розподілити пам'ять non-const, призначити значення, надіслати покажчик const і повернути його. Тепер є вказівник на попередньо призначену пам'ять const, яку хтось повинен звільнити.
Манос Ніколаїдіс

2
Приклад : "Ці підпрограми повертають рядок у щойно змінену пам'ять, на яку вказує * stringValueP, яку ви зрештою повинні звільнити. Іноді функція ОС, яку ви використовуєте для звільнення пам'яті, оголошується взяти вказівник на щось незмінне як аргумент, тому що * stringValueP - покажчик на const.
Carsten S

3
Помилкове, якщо функція приймає в const char *pякості аргументу , а потім звільняє його, правильна річ , щоб зробити , це не акторський , pщоб char*перед викликом безкоштовно. Потрібно не оголошувати його як const char *pперше місце, оскільки воно модифікує *p і повинно бути оголошено відповідно. (А якщо він приймає покажчик константний замість покажчика на константні, int *const pвам не потрібно , щоб кинути , так як це на самому справі законним і , таким чином , працює відмінно без кидка.)
Ray

59

Попередній стандарт C не мав, void*але тільки char*, тому вам довелося передати всі передані параметри. Якщо ви зіткнетесь з давнім кодом С, то, можливо, ви знайдете такі касти.

Аналогічне запитання з посиланнями .

Коли перший стандарт C був випущений, прототипи Танос безкоштовно змінилося від того , char*на void*що у них ще є сьогодні.

І звичайно, в стандартному С такі касти зайві і просто шкодять читабельності.


23
Але навіщо ви ставите аргумент freeтому ж типу, який він є?
jwodder

4
@chux Проблема з попереднім стандартом полягає лише в тому, що жодних зобов'язань немає. Люди просто вказали на книгу K&R для канону, бо це було єдине, що вони мали. І як ми бачимо з кількох прикладів у другому виданні K&R, самі K&R плутаються з приводу того, як касти параметрів freeпрацюють у стандартному C (не потрібно робити це). Я не читав 1-го видання, тому не можу сказати, чи їх плутали і в 80-і до стандартних часів.
Лундінь

7
Попередній стандарт C не мав void*, але він також не мав прототипів функцій, тому кастинг аргументу freeвсе ще був непотрібним навіть у K&R (якщо вважати, що всі типи покажчиків даних використовували однакове представлення).
Ян Абботт

6
З кількох причин, зазначених у коментарях, я не думаю, що ця відповідь має сенс.
R .. GitHub СТОП ДОПОМОГА ICE

4
Я не бачу, як ця відповідь насправді відповість на щось відповідне. Оригінальне запитання стосується кастингу до інших типів, не тільки до char *. Який сенс було б без старих компіляторів void? Чого могли б досягти такі касти?
ANT

34

Ось приклад, коли вільний не зможе пройти без участі:

volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p);        // fail: warning C4090: 'function' : different 'volatile' qualifiers
free((int*)p);  // success :)
free((void*)p); // success :)

В C ви можете отримати попередження (отримали його у VS2012). У C ++ ви отримаєте помилку.

Рідкісні випадки в стороні, кастинг просто роздуває код ...

Редагувати: Я вважав, що void*не int*демонструвати провал. Він буде працювати так само, як int*буде перетворено void*неявно. Доданий int*код.


Зауважте, що в коді, розміщеному у запитанні, запити не для void *, а до float *та char *. Ці ролі не просто сторонні, вони помиляються.
Ендрю Генле

1
Питання насправді полягає в протилежному.
m0skit0

1
Я не розумію відповіді; в якому сенсі free(p)провалиться? Чи дасть це помилка компілятора?
Кодор

1
Це хороші моменти. constОчевидно, те саме стосується і покажчиків кваліфікаторів.
Лундін

2
volatileіснує з моменту стандартизації С, якщо не більше. Він не був доданий у C99.
R .. GitHub СТОП ДОПОМОГА ICE

30

Стара причина: 1. Використовуючи free((sometype*) ptr)код, явно визначається тип, вказівник якого слід розглядати як частину free()виклику. Явний виступ корисний, коли free()його замінюють на (зроби сам) DIY_free().

#define free(ptr) DIY_free(ptr, sizeof (*ptr))

Це DIY_free()був (є) спосіб, особливо в режимі налагодження, зробити аналіз часу виконання звільненого вказівника. Це часто поєднується з DIY_malloc()додаванням сентенцій, загальної кількості пам'яті і т. Д. Моя група використовувала цю методику протягом багатьох років, перш ніж з'являться новіші інструменти. Він зобов’язував, щоб предмет, який було вільно, передано типу, був спочатку розподілений.

  1. Зважаючи на багато годин, витрачених на відстеження проблем із пам’яттю тощо, невеликі хитрощі, такі як кастинг типу free'd, допоможуть у пошуку та звуженні налагодження.

Сучасні: уникати constта volatileпопереджувати, як звертаються Манос Ніколаїдіс @ та @egur . Думав , що я хотів би відзначити ефекти 3 класифікаторів : const, volatile, і restrict.

[редагувати] Додано char * restrict *rp2за коментарем @R ..

void free_test(const char *cp, volatile char *vp, char * restrict rp, 
    char * restrict *rp2) {
  free(cp);  // warning
  free(vp);  // warning
  free(rp);  // OK
  free(rp2);  // warning
}

int main(void) {
  free_test(0,0,0,0);
  return 0;
}

3
restrictє лише невипуском через те, де він розміщений - він впливає на об’єкт, rpне вказаний на тип. Якби ви натомість мали char *restrict *rp, то це мало б значення.
R .. GitHub СТОП ДОПОМОГА ICE

16

Ось ще одна альтернативна гіпотеза.

Нам кажуть , що програма була написана пре-C89, що означає , що він не може працювати навколо якої - то невідповідність з прототипом free, тому що не тільки там немає такого поняття , як constні void *до C89, не було такого поняття , як прототип функції до C89. stdlib.hсама по собі була винаходом комітету. Якби заголовки системи freeвзагалі намагалися декларувати , вони зробили б це так:

extern free();  /* no `void` return type either! */

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

Однак це все ще не означає, що потрібно було аргументувати freeбільшість компіляторів K&R. Така функція

free_stuff(a, b, c)
    float *a;
    char *b;
    int *c;
{
    free(a);
    free(b);
    free(c);
}

повинні були бути складені правильно. Отже, я думаю, що тут у нас є програма, написана для впорядкування помилкового компілятора для незвичного середовища: наприклад, середовища, де sizeof(float *) > sizeof(int)компілятор не використовує відповідну умову виклику для покажчиків, якщо ви не кинете їх у точку дзвінка.

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


1
Перший тайм я повністю згоден. А друга половина - це інтригуюча та правдоподібна думка.
chux

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.