36

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

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

我听说局部变量是f()在它们所在的函数(在这种情况下)的入口处分配的,或者在定义点处分配的。

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

void f()
{
    goto label;
    int i;

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

8 回答 8

44

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

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

即使goto在声明/定义之前的函数开头有 a ,该内存位置也将在那里,就像在您的示例中一样。


很傻的比喻

如果我把一根木头放在地上,我的朋友跑(闭着眼睛)跳过它,木头仍然在那里——即使他没有看到或感觉到它。

如果他愿意的话,可以说他可以转身(稍后)并将其点燃,这是现实的。他的跳跃并没有让原木神奇地消失。

于 2011-12-16T14:44:48.010 回答
16

你的代码很好。如果变量不存在,变量将存在于它会存在的任何地方goto

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

C++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
}

—结束示例]

于 2011-12-16T14:47:49.723 回答
9

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

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

于 2011-12-16T14:44:49.787 回答
8

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

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

于 2011-12-16T14:43:42.533 回答
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 类型,因为它有用户定义的构造函数,这意味着对象需要被动态初始化,但是由于goto语句试图跳过这个,编译器会产生错误,这是它应该的。

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

于 2011-12-16T14:51:17.627 回答
1

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

另一方面,locol(堆栈)变量的变量分配是当控制流到达那里时执行的运行时操作。所以goto对此有影响。

当涉及到对象构造时,事情会变得有些棘手,但这不是你的情况。

于 2011-12-16T14:44:53.173 回答
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) 引用。

于 2011-12-16T14:50:03.133 回答
0

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

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

于 2011-12-16T14:46:39.817 回答