Является ли "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 и улучшить код до тех пор, пока не будут указаны предупреждения.