Подтвердить что ты не робот

Что происходит, когда переменная выходит за рамки?

В большинстве управляемых языков (то есть с GC) локальные переменные, выходящие за пределы области действия, недоступны и имеют более высокий приоритет GC (следовательно, сначала будут освобождены).

Теперь C не является управляемым языком, что происходит с переменными, которые выходят за рамки области?

Я создал небольшой тестовый файл в C:

#include <stdio.h>
int main(void){
    int *ptr;

    {
        // New scope
        int tmp = 17;
        ptr = &tmp; // Just to see if the memory is cleared
    }

    //printf("tmp = %d", tmp); // Compile-time error (as expected)
    printf("ptr = %d\n", *ptr);

    return 0;
}

Я использую GCC 4.7.3 для компиляции, а программа выше печатает 17, почему? И когда/при каких обстоятельствах будут освобождены локальные переменные?

4b9b3361

Ответ 1

Фактическое поведение вашего образца кода определяется двумя основными факторами: 1) поведение undefined по языку, 2) оптимизирующий компилятор будет генерировать машинный код, который физически не соответствует вашему C-коду.

Например, несмотря на то, что поведение undefined, GCC может (и будет) легко оптимизировать ваш код до простого

printf("ptr = %d\n", 17);

что означает, что вывод, который вы видите, имеет очень мало общего с тем, что происходит с любыми переменными в вашем коде.

Если вы хотите, чтобы поведение вашего кода лучше отражало то, что происходит физически, вы должны объявить свои указатели volatile. Поведение все равно будет undefined, но, по крайней мере, оно ограничит некоторые оптимизации.

Теперь, что происходит с локальными переменными, когда они выходят за рамки. Ничего физического не происходит. Типичная реализация будет выделять достаточно места в стеке программ для хранения всех переменных на самом глубоком уровне вложенности блоков в текущей функции. Это пространство обычно выделяется в стеке одним выстрелом при запуске функции и отбрасывается обратно на выходе функции.

Это означает, что память, ранее занятая tmp, по-прежнему сохраняется в стеке, пока функция не выйдет. Это также означает, что одно и то же пространство стека может (и будет) повторно использоваться разными переменными, имеющими примерно одинаковый уровень "глубина локальности" в блоках сиблинга. Пространство будет удерживать значение последней переменной до тех пор, пока какая-либо другая переменная, объявленная в какой-либо переменной блока sibling, не переопределит ее. В вашем примере никто не переопределяет пространство, ранее занятое tmp, поэтому вы обычно увидите, что значение 17 сохранилось без изменений в этой памяти.

Однако, если вы это сделаете

int main(void) {
  volatile int *ptr;
  volatile int *ptrd;

  { // Block
    int tmp = 17;
    ptr = &tmp; // Just to see if the memory is cleared
  }

  { // Sibling block
    int d = 5;
    ptrd = &d;
  }

  printf("ptr = %d %d\n", *ptr, *ptrd);
  printf("%p %p\n", ptr, ptrd);
}

вы увидите, что пространство, ранее занятое tmp, было повторно использовано для d, а его прежнее значение было переопределено. Второй printf обычно выводит одно и то же значение указателя для обоих указателей.

Ответ 2

Время жизни автоматического объекта заканчивается в конце блока, где он объявлен.

Доступ к объекту за пределами его жизни - это поведение undefined в C.

(C99, 6.2.4p2) "Если объект ссылается вне его времени жизни, поведение undefined. Значение указателя становится неопределенным, когда объект, на который он указывает, достигает конца своего срока службы".

Ответ 3

Локальные переменные выделяются в стеке. Они не "освобождаются" в том смысле, в каком вы думаете о языках GC, или о памяти, выделенной в куче. Они просто выходят за рамки, а для встроенных типов код ничего не сделает - и для объектов вызывается деструктор.

Доступ к ним за пределами их возможностей - Undefined Поведение. Вам просто повезло, поскольку ни один другой код не перезаписал эту область памяти... пока.