绕过定义时如何使用变量?

时间:2011-12-16 14:41:50

标签: c++ scope variable-declaration

在我看来,定义总是意味着存储分配。

在以下代码中,int i在程序堆栈上分配一个4字节(通常)存储,并将其绑定到ii = 3为该存储分配3。但由于goto,定义被绕过,这意味着没有为i分配存储空间。

我听说局部变量分配在它们所在的函数(本例中为f())的入口处,或者在定义点处。

但无论如何,i如何在尚未定义的情况下使用(根本没有存储空间)?执行i = 3时,值3分配给哪里?

void f()
{
    goto label;
    int i;

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

8 个答案:

答案 0 :(得分:44)

长话短说; goto将导致运行时跳转,变量定义/声明将导致存储分配,编译时间。

编译器将查看并决定为int分配多少存储空间,它还可以在“点击”3时将此分配的存储空间设置为i = 3;

即使在函数开头有一个goto,在声明/定义之前,内存位置也会存在,就像在你的例子中一样。


非常愚蠢的比喻

如果我在地上放置一根木头并且我的朋友跑了(闭着眼睛)并跳过它,那么日志仍会存在 - 即使他没有看到或感觉到它。

如果他愿意的话,他可以转身(在以后的某个时间)并将其置于火上是很现实的。他的跳跃并没有让日志神奇地消失。

答案 1 :(得分:16)

你的代码很好。如果goto没有出现,变量就会存在。

请注意,在某些情况下您无法跳过声明:

  

C ++ 11 6.7声明声明[stmt.dcl]

     

3可以转换为块,但不能以初始化方式绕过声明。一个   程序从具有自动存储持续时间的变量不在范围内的点跳转到a   除非变量具有标量类型,具有普通默认值的类类型,否则它在范围内的点是不正确的   构造函数和一个普通的析构函数,这些类型之一的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 example]

答案 2 :(得分:9)

定义不是可执行代码。它们只是编译器的指令,让它知道变量的大小和类型。从这个意义上说,goto语句没有绕过定义。

如果使用带有构造函数而不是int的类,则goto将绕过构造函数的调用,但无论如何都会分配存储。但是,类实例仍然是未初始化的,因此在定义/初始化行之前使用它来获取控件是错误的。

答案 3 :(得分:8)

  

在我看来,定义总是意味着存储分配。

这不正确。变量的存储由编译器在为函数创建堆栈布局时保留。 goto只是绕过了初始化。由于您在打印前指定了一个值,一切都很好。

答案 4 :(得分:2)

流的控制与编译器在编译时保留的变量存储无关。

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类型because it has user-defined constructor,这意味着对象需要动态初始化,但由于goto语句尝试为了省略这一点,编译器会产生错误。

请注意,只有内置类型和POD类型的对象才能保持未初始化状态。

答案 5 :(得分:1)

为简而言之,变量声明是词法,与词汇{} - 封闭的块有关。绑定从声明的行到块的末尾有效。它不受流量控制(goto)的影响。

另一方面,locol(堆栈)变量的变量赋值是在控制流到达时执行的运行时操作。所以goto会对此产生影响。

当涉及对象构建时,事情变得有点棘手,但这不是你的情况。

答案 6 :(得分:1)

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)引用。

答案 7 :(得分:0)

变量的定义不为变量分配内存。它确实告诉编译器准备适当的内存空间来存储变量,但是当控制传递定义时,内存不会被分配。

这里真正重要的是初始化。