C: Позвонить бесплатно по автоматической переменной?

int main()
{
    int x = 0;
    free(x);
}

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

Спасибо!

Ответ 1

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

Во-первых, код не должен компилироваться, потому что он содержит нарушение ограничения. Выражение, которое вы передаете как операнд 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 с не-ICE x, равным 0, выдает значение нулевого указателя типа void *. Это значение нулевого указателя, переданное в free, приводит к отсутствию операции в соответствии с спецификацией free.

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

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

P.S. Заметьте, BTW, что многие ответы, уже отправленные здесь, совершают ту же серьезную ошибку. Они предполагают, что (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

Ответ 2

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

EDIT: Как отмечали другие, свободные (x) (с x = 0) и свободные (NULL) - это не одно и то же. (Хотя часто бывает 0, значение указателя NULL определяется реализацией и на него нельзя положиться.) Пожалуйста, см. Ответ AndreyT за очень хорошее разъяснение.

Ответ 3

Освобождение NULL (или 0) ничего не делает. Это не-op.

Ответ 4

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

tmp.c: 6: warning: передать аргумент 1 из 'free делает указатель из целого без литой

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

Ответ 5

На странице free man:

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

  

free (0 );

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

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

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

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

память отображает некоторые другие переменные.