C: Вызов бесплатный по автоматической переменной? - PullRequest
11 голосов
/ 26 февраля 2010
int main()
{
    int x = 0;
    free(x);
}

Это компилируется и кажется неактивным. Что на самом деле происходит? это поведение определено?

Спасибо! * * 1004

Ответы [ 8 ]

14 голосов
/ 26 февраля 2010

Нет, поведение не определено. Более того, код не должен компилироваться.

Во-первых, код не должен компилироваться, поскольку он содержит нарушение ограничения. Выражение, которое вы передаете в качестве операнда free, имеет тип int. Параметр free имеет тип void *. Единственный случай, когда значение int может быть неявно преобразовано в тип void *, это когда значение int является интегральным выражением константы (ICE) со значением 0. В вашем случае x не является ICE, а это означает, что он не может быть неявно преобразован в void *. Единственная причина, по которой ваш код компилируется, состоит в том, что по историческим причинам (для поддержки унаследованного кода) ваш компилятор незаметно игнорирует нарушение ограничения, присутствующее в вызове free(x). Я уверен, что если вы повысите уровень предупреждений в компиляторе, он будет жаловаться (по крайней мере, с предупреждением). Педантичный компилятор немедленно выдаст ошибку для вызова free(x). Попробуйте Comeau Online, например, в режиме C89 / 90:

"ComeauTest.c", line 6: error: argument of type "int" is incompatible with parameter
          of type "void *"
      free(x); 
           ^

(Кроме того, вы не забыли включить stdlib.h перед вызовом free?)

Во-вторых, давайте предположим, что код компилируется, т.е. он интерпретируется компилятором как free((void *) x). В этом случае непостоянное целое значение x преобразуется в тип указателя void *. Результат этого преобразования определяется реализацией. Обратите внимание, что язык гарантирует, что при преобразовании ICE со значением 0 в тип указателя результатом будет нулевой указатель. Но в вашем случае x не является ICE, поэтому результат преобразования определяется реализацией. В C нет гарантии, что вы получите нулевой указатель путем преобразования целого числа не-ICE со значением 0 в тип указателя. В вашей реализации, вероятно, просто случилось так, что (void *) x с non-ICE x, равным 0, создает нулевое значение указателя типа void *. Это значение нулевого указателя при передаче free приводит к неработоспособности в соответствии со спецификацией free.

В общем случае передача такого указателя на free приведет к неопределенному поведению. Указатели, которые вы можете легально передать free, это указатели, полученные при предыдущих вызовах malloc / calloc / realloc, и нулевые указатели. Ваш указатель нарушает это ограничение в общем случае, поэтому поведение не определено.

Это то, что происходит в вашем случае. Но, опять же, ваш код содержит нарушение ограничения. И даже если вы переопределите нарушение, поведение не определено.

P.S. Обратите внимание, что многие ответы, уже размещенные здесь, допускают ту же серьезную ошибку. Они предполагают, что (void *) x с нулем x должен давать нулевой указатель. Это абсолютно неверно. Опять же, язык не дает абсолютно никаких гарантий относительно результата (void *) x, когда x не является ICE. (void *) 0 гарантированно будет нулевым указателем, но (void *) x с нулем x равно не гарантированно будет нулевым указателем.

Это описано в C FAQ http://c -faq.com / null / runtime0.html . Для тех, кто хочет лучше понять, почему это так, было бы неплохо прочитать весь раздел о нулевых указателях http://c -faq.com / null / index.html

12 голосов
/ 26 февраля 2010

В вашем случае это работает, потому что вызов free (NULL) (т.е. free (0)) является NOOP. Однако, если вы вызываете его с любым значением, отличным от 0 (NULL), поведение будет неопределенным - возможны сбои и / или повреждение памяти.

РЕДАКТИРОВАТЬ: Как другие отметили позже, free (x) (с x = 0) и free (NULL) не одно и то же. (Хотя это часто 0, значение указателя NULL определяется реализацией и на него нельзя положиться.) Пожалуйста, смотрите ответ AndreyT для очень хорошего разъяснения.

4 голосов
/ 26 февраля 2010

Очистка NULL (или 0) ничего не делает. Это неоперация.

3 голосов
/ 26 февраля 2010

Вы пытались повысить уровень предупреждения вашего компилятора? Например gcc -ansi -pedantic -W -Wall отчеты:

tmp.c: 6: предупреждение: передача аргумента 1 из «free» делает указатель из целого числа без преобразования

Поведение не определено. Не делай этого.

2 голосов
/ 26 февраля 2010

Со страницы руководства free:

free () освобождает пространство памяти, на которое указывает ptr, которое должно быть возвращено предыдущим вызовом malloc (), calloc () илиперераспределить ().В противном случае или, если free (ptr) уже был вызван ранее, происходит неопределенное поведение.Если ptr равен NULL, никакие операции не выполняются.

В вашем примере вы фактически вызываете free(0), поскольку free принимает указатель в качестве аргумента.По сути, вы говорите среде выполнения освободить память по адресу 0, для которого не был ранее выделен malloc.

Поскольку '0' равно NULL, ничего не произойдет.(Спасибо за комментарии за указание на мою глупую ошибку).

0 голосов
/ 26 февраля 2010
  

free (0 );

"Calling free function with NULL value , will have no effect."

На самом деле это будет иметь значение, когда мы освободим используемую память.

Здесь значение x равно нулю, так что это не создает никаких проблем. Если х имеет значение, отличное от

ноль, в этом случае может возникнуть ошибка сегментации. Поскольку значение x может использоваться как

представление памяти некоторых других переменных.

0 голосов
/ 26 февраля 2010

free () работает с динамически выделяемой памятью, то есть с памятью, выделенной с помощью malloc, calloc или realloc. Кроме того, он принимает указатель, и вы передаете значение х. Нет причин вызывать его для переменных, выделенных стеком, которые будут освобождены, когда стек раскручивается и такое поведение не определено. Всегда хорошо читать документацию.

http://www.cplusplus.com/reference/clibrary/cstdlib/free/

0 голосов
/ 26 февраля 2010

Он должен выдавать нарушение прав доступа на большинстве архитектур. ОК, это 0, поэтому он работает, но если бы он не был 0, он потерпит неудачу. Это не определено. Вы не можете free() переменную, выделенную из стека.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...