Является ли "goto" умным для правильной работы с переменными стека в C (не С++)
(Извините за плохой английский.)
Вопрос 1.
void foo(void)
{
goto inside;
for (;;) {
int stack_var = 42;
inside:
...
}
}
Будет ли место в стеке выделено для stack_var
, когда я перейду в метку inside
? То есть могу ли я правильно использовать переменную stack_var
внутри ...
?
Вопрос 2.
void foo(void)
{
for (;;) {
int stack_var = 42;
...
goto outside;
}
outside:
...
}
Будет ли место в стеке stack_var
освобождено, когда я перейду на метку outside
? Например. правильно ли сделать return
внутри ...
?
Другими словами, goto
умный для правильной работы со стековыми переменными (автоматическое (деление) при прохождении через блоки), или это просто глупый прыжок?
Ответы
Ответ 1
Вопрос 1:
Можно ли правильно использовать переменную stack_var внутри...?
Код в... может писать на stack_var
. Однако эта переменная неинициализирована, потому что поток выполнения перепрыгнул через инициализацию, поэтому код не должен считываться из него, не записывая его сначала.
Из стандарта C99, 6.8: 3
Инициализаторы объектов, которые имеют продолжительность автоматического хранения [...], оцениваются и значения хранятся в объектах (включая сохранение неопределенного значения в объектах без инициализатора) каждый раз, когда объявление достигается в порядке выполнения
Мой компилятор компилирует приведенную ниже функцию к части сборки, которая иногда возвращает неинициализированное содержимое x
:
int f(int c){
if (c) goto L;
int x = 42;
L:
return x;
}
cmpl $0, %eax
jne LBB1_2
movl $42, -16(%rbp)
LBB1_2:
movl -16(%rbp), %eax
...
popq %rbp
ret
Вопрос 2:
Будет ли место в стеке stack_var deallocated, когда я получаю внешнюю метку?
Да, вы можете ожидать, что память, зарезервированная для stack_var
, будет исправлена, как только переменная выходит за пределы области видимости.
Ответ 2
Существуют две разные проблемы:
-
лексическое определение переменных внутри кода C. Переменная C имеет смысл только внутри блока, в котором она объявлена. Вы можете себе представить, что компилятор переименовывает переменные в уникальные имена, которые имеют смысл только внутри блока области.
-
вызывать фреймы в сгенерированном коде. Хороший оптимизирующий компилятор обычно выделяет кадр вызова текущей функции в стеке класса машины в начале функции. Данное местоположение в этом кадре вызова, называемое слотом, может (и обычно) повторно использоваться компилятором для нескольких локальных переменных (или других целей).
И локальная переменная может храниться только в регистре (без какого-либо слота в кадре вызова), и этот регистр, очевидно, будет повторно использоваться для различных целей.
Вероятно, вы столкнулись с undefined поведением для вашего первого случая. После goto inside
stack_var
не инициализируется.
Я предлагаю вам скомпилировать с gcc -Wall
и улучшить код до тех пор, пока не будут указаны предупреждения.