В C, почему некоторые люди бросают указатель, прежде чем освобождать его?

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

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

где каждый указатель имеет соответствующий (и соответствующий) тип. Я не вижу смысла делать это вообще. Это очень старый код, поэтому мне осталось интересно, есть ли это K & R. Если это так, я действительно хочу поддержать старые компиляторы, которые могут потребовать этого, поэтому я не хочу их удалять.

Есть ли техническая причина использовать эти приведения? Я даже не вижу прагматичной причины использовать их. Какой смысл напоминать себе тип данных прямо перед его освобождением?

EDIT: Этот вопрос не является дубликатом другого вопроса. Другой вопрос - это особый случай этого вопроса, который, я думаю, очевиден, если бы те, кто был заинтересован, читали все ответы.

Colophon: я даю "const answer" галочку, потому что это настоящая причина, почему это может потребоваться; тем не менее, ответ на вопрос о том, что он является обычным ANSI C (по крайней мере, среди некоторых программистов), по-видимому, является причиной его использования в моем случае. Здесь много хороших моментов. Спасибо за ваши вклады.

Ответ 1

Может потребоваться кастинг для разрешения предупреждений компилятора, если указатели const. Вот пример кода, который вызывает предупреждение без внесения аргумента free:

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

Предварительно стандартная C не имела void*, а только char*, поэтому вам нужно было передать все переданные параметры. Если вы столкнетесь с древним кодом C, вы можете найти такие отливки.

Подобный вопрос со ссылками.

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

И, конечно же, в стандартном C, такие отбрасывания являются излишними и просто удобочитаемыми.

Ответ 3

Здесь пример, в котором free будет без кавычек:

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). В С++ вы получите сообщение об ошибке.

Редкие случаи в стороне, кастинг просто раздувает код...

Edit: Я отказался от void* not int*, чтобы демонтировать сбой. Он будет работать так же, как int* будет неявно преобразован в void*. Добавлен код int*.

Ответ 4

Старая причина: 1. Используя free((sometype*) ptr), код явно о типе, указатель следует рассматривать как часть вызова free(). Явное приведение полезно, когда free() заменяется на (do-it-yourself) DIY_free().

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

A DIY_free() был (есть) способ, особенно в режиме отладки, выполнять анализ времени выполнения освобождаемого указателя. Это часто сопрягается с DIY_malloc(), чтобы добавить аргументы, подсчет использования глобальной памяти и т.д. Моя группа использовала этот метод в течение многих лет, прежде чем появятся более современные инструменты. Он обязывал, чтобы элемент, который был свободен, был отлит к типу, был изначально выделен.

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

Современный: избегая предупреждений const и volatile, как описано Manos Nikolaidis @ и @egur. Думаю, я бы заметил эффекты трех классификаторов: const, volatile и restrict.

[edit] Добавлено 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;
}

Ответ 5

Вот еще одна альтернативная гипотеза.

Нам говорят, что программа была написана до C89, что означает, что она не может работать над каким-то несоответствием с прототипом free, потому что не только не было такой вещи, как const и void * до C89, не было такой вещи, как прототип функции до C89. Сам stdlib.h был изобретением комитета. Если заголовки системы потрудились объявить free вообще, они сделали бы это следующим образом:

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

Теперь ключевым моментом здесь является то, что отсутствие прототипов функций означало, что компилятор не проводил проверку типа аргументов. Он применял предложения по умолчанию для аргументов по умолчанию (те же, которые все еще применяются к вызовам вариационной функции), и все. Ответственность за внесение аргументов в каждой линии call-line с ожидаемыми ответами целиком лежит на программисте.

Однако это все еще не означает, что нужно было отнести аргумент free к большинству компиляторов K & R. Функция типа

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

должен быть скомпилирован правильно. Поэтому я думаю, что у нас есть программа, написанная, чтобы справиться с багги-компилятором для необычной среды: например, среда, где sizeof(float *) > sizeof(int), и компилятор не будет использовать соответствующее соглашение о вызове для указателей, если вы их не набросаете в точке вызова.

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

Ответ 7

Приведение к char *, например, не только лишнее, но и неправильное.

Неправильное нажатие на float * или char *.

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

В Стандарт C, free() объявлен как:

7.22.3.3 Функция free

Сводка

1    #include <stdlib.h>
     void free(void *ptr);

И указатель всегда может быть преобразован в void * и без него:

6.3.2.3 Указатели

Указатель на void может быть преобразован в указатель или из указателя на любой тип объекта. Указатель на любой тип объекта может быть преобразован в указатель на void и обратно; результат должен сравните с исходным указателем.