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

Как можно использовать переменную, когда ее определение обходит?

В моем сознании, всегда, определение означает распределение памяти.

В следующем коде int i выделяет 4-байтовое (обычно) хранилище в стеке программ и привязывает его к i, а i = 3 назначает 3 этому хранилищу. Но из-за goto определение обходит, что означает, что для i нет хранилища.

Я слышал, что локальные переменные выделяются либо при входе функции (f() в этом случае), где они находятся, либо в точке определения.

Но в любом случае, как i использовать, пока он еще не определен (вообще нет хранилища)? Где присваивается значение три при выполнении i = 3?

void f()
{
    goto label;
    int i;

label:
    i = 3;
    cout << i << endl; //prints 3 successfully
}
4b9b3361

Ответ 1

Короче говоря; goto приведет к скачку времени выполнения, определение/объявление переменной приведет к распределению памяти, времени компиляции.

Компилятор увидит и определит, сколько памяти будет выделяться для int, также будет сделано так, чтобы это распределенное хранилище было установлено на 3, когда "нажимать" i = 3;.

Это место памяти будет там, даже если в начале вашей функции есть goto, перед объявлением/определением, как и в вашем примере.


Очень глупый пример

Если я положу журнал на землю, и мой друг бежит (с закрытыми глазами) и прыгает через него, журнал все равно будет там, даже если он его не видел и не чувствовал.

Реально сказать, что он может развернуться (позже) и поджечь его, если захочет. Его прыжок не приводит к тому, что лог волшебным образом исчезает.

Ответ 2

Ваш код в порядке. Переменная живет там, где она будет жить, если бы goto не было.

Обратите внимание, что есть ситуации, когда вы не можете перескакивать через объявление:

С++ 11 6.7 Декларация о заявлении [stmt.dcl]

3 Можно передать в блок, но не таким образом, чтобы обходить объявления с инициализацией. программа, которая перескакивает с точки, где переменная с автоматической продолжительностью хранения не имеет точка, где она находится в области видимости, плохо сформирована, если переменная не имеет скалярного типа, тип класса с тривиальным значением по умолчанию конструктор и тривиальный деструктор, cv-квалифицированная версия одного из этих типов или массив одного из предшествующих типов и объявляется без инициализатора (8.5). [Пример:

void f()
{
    // ...
    goto lx;    // ill-formed: jump into scope of `a'
    // ...
ly:
    X a = 1;
    // ...
lx:
    goto ly;    // ok, jump implies destructor
                // call for `a' followed by construction
                // again immediately following label ly
}

-end пример]

Ответ 3

Определения не являются исполняемым кодом. Это всего лишь инструкции для компилятора, позволяющие ему знать размер и тип переменной. В этом смысле определение не обходит оператор goto.

Если вы используете класс с конструктором вместо int, вызов конструктора будет обходить goto, но хранилище будет выделено в любом случае. Однако экземпляр класса останется неинициализированным, поэтому, используя его до того, как его строка определения/инициализации получит элемент управления, будет ошибка.

Ответ 4

В моем сознании, всегда, определение означает распределение памяти.

Это неверно. Хранилище для переменной зарезервировано компилятором при создании стека-макета для этой функции. goto просто обходит инициализацию. Поскольку вы назначаете значение перед печатью, все в порядке.

Ответ 5

Управление потоком не имеет ничего общего с хранилищем переменных, которое зарезервировано компилятором во время компиляции.

Оператор goto влияет только на динамическую инициализацию объекта. Для встроенных типов и типов POD это не имеет значения, поскольку они могут оставаться неинициализированными. Однако для не-POD-типов это приведет к ошибке компиляции. Например, см. Это

struct A{ A(){} };  //it is a non-POD type

void f()
{
    goto label;

    A a;     //error - you cannot skip this!

label:
    return;
}

Ошибка:

prog.cpp: In function ‘void f()’:
prog.cpp:8: error: jump to label ‘label’
prog.cpp:5: error:   from here
prog.cpp:6: error:   crosses initialization of ‘A a’

Смотрите здесь: http://ideone.com/p6kau

В этом примере A является не-POD-типом поскольку он имеет определяемый пользователем конструктор, что означает, что объект должен быть динамически инициализирован, но поскольку Оператор goto пытается пропустить это, компилятор генерирует ошибку, как и должно быть.

Обратите внимание, что объекты только встроенных типов и типов POD могут оставаться неинициализированными.

Ответ 6

Чтобы сделать его коротким, объявление переменной лексично, то есть относится к лексическим {} -замкнутым блокам. Связывание действует с строки, объявленной в конце блока. Он не зависит от управления потоком (goto).

Переменные назначения переменных locol (stack), с другой стороны, являются операцией выполнения, выполняемой, когда поток управления поступает туда. Таким образом, goto влияет на это.

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

Ответ 7

Позиция объявления i не имеет отношения к компилятору. Вы можете доказать это сами, компилируя свой код с помощью int i до goto, а затем после и сравнивая сгенерированную сборку:

g++ -S test_with_i_before_goto.cpp -o test1.asm
g++ -S test_with_i_after_goto.cpp -o test2.asm
diff -u test1.asm test2.asm

Единственное различие в этом случае - это ссылка на исходное имя файла (.file).

Ответ 8

Определение переменной НЕ выделяет память для переменной. Он сообщает компилятору подготовить соответствующее пространство памяти для хранения переменной, хотя память не выделяется, когда управление передаёт определение.

Здесь действительно важна инициализация.