#c #gcc
#c #управление памятью
Вопрос:
В большинстве управляемых языков (то есть тех, в которых есть GC) локальные переменные, которые выходят за пределы области видимости, недоступны и имеют более высокий GC-приоритет (следовательно, они будут освобождены первыми).
Итак, C не является управляемым языком, что происходит с переменными, которые выходят за пределы области видимости здесь?
Я создал небольшой тестовый пример на C:
#include <stdio.h>
int main(void){
int *ptr;
{
// New scope
int tmp = 17;
ptr = amp;tmp; // Just to see if the memory is cleared
}
//printf("tmp = %d", tmp); // Compile-time error (as expected)
printf("ptr = %dn", *ptr);
return 0;
}
Я использую GCC 4.7.3 для компиляции, и вышеприведенная программа печатает 17
, почему? И когда / при каких обстоятельствах локальные переменные будут освобождены?
Комментарии:
1.
gcc
4.7.3
на сегодняшний день не выпущена. Должно быть, это4.7.3
предварительный выпуск.
Ответ №1:
Фактическое поведение вашего примера кода определяется двумя основными факторами: 1) поведение не определено языком, 2) оптимизирующий компилятор сгенерирует машинный код, который физически не соответствует вашему C-коду.
Например, несмотря на то, что поведение не определено, GCC может (и будет) легко оптимизировать ваш код до простого
printf("ptr = %dn", 17);
это означает, что вывод, который вы видите, имеет очень мало общего с тем, что происходит с любыми переменными в вашем коде.
Если вы хотите, чтобы поведение вашего кода лучше отражало то, что происходит физически, вам следует объявить свои указатели volatile
. Поведение по-прежнему будет неопределенным, но, по крайней мере, это ограничит некоторые оптимизации.
Теперь, что происходит с локальными переменными, когда они выходят за пределы области видимости. Ничего физического не происходит. Типичная реализация выделит достаточно места в программном стеке для хранения всех переменных на самом глубоком уровне вложенности блоков в текущей функции. Обычно это пространство выделяется в стеке одним выстрелом при запуске функции и освобождается обратно при выходе из функции.
Это означает, что память, ранее занятая tmp
, продолжает оставаться зарезервированной в стеке до завершения работы функции. Это также означает, что одно и то же пространство стека может (и будет) повторно использоваться разными переменными, имеющими примерно одинаковый уровень «глубины локализации» в родственных блоках. Пробел будет содержать значение последней переменной до тех пор, пока какая-либо другая переменная, объявленная в некоторой переменной родственного блока, не переопределит ее. В вашем примере никто не переопределяет пространство, ранее занимаемое tmp
, поэтому вы обычно увидите, что значение 17
сохраняется в этой памяти без изменений.
Однако, если вы сделаете это
int main(void) {
volatile int *ptr;
volatile int *ptrd;
{ // Block
int tmp = 17;
ptr = amp;tmp; // Just to see if the memory is cleared
}
{ // Sibling block
int d = 5;
ptrd = amp;d;
}
printf("ptr = %d %dn", *ptr, *ptrd);
printf("%p %pn", ptr, ptrd);
}
вы увидите, что пространство, ранее занимаемое tmp
, было повторно использовано для d
, а его прежнее значение было переопределено. Второй printf
обычно выводит одно и то же значение указателя для обоих указателей.
Ответ №2:
Время жизни автоматического объекта заканчивается в конце блока, где он объявлен.
Доступ к объекту за пределами его жизненного цикла является неопределенным поведением в C.
(C99, 6.2.4p2) «Если на объект ссылаются за пределами его жизненного цикла, поведение не определено. Значение указателя становится неопределенным, когда объект, на который он указывает, достигает конца своего срока службы.»
Ответ №3:
Локальные переменные размещаются в стеке. Они не «освобождаются» в том смысле, в каком вы думаете о языках GC или памяти, выделенной в куче. Они просто выходят за пределы области видимости, и для встроенных типов код ничего не будет делать, а для объектов вызывается деструктор.
Доступ к ним за пределами их области видимости является неопределенным поведением. Вам просто повезло, поскольку никакой другой код не перезаписал эту область памяти … пока.