c++ 中块内局部变量的存储分配

发布于 2024-11-17 19:59:17 字数 898 浏览 0 评论 0原文

我想知道编译器在什么时候为块内的局部变量分配存储空间。 goto 和 switch 如何跳过构造函数? :

class Tree {/*...*/}
...
void foo (int i){
if (i < 10) goto label; //illegal: cannot jump past a ctor
 Tree t (45);
 label: 
   switch (i){
      case 1:
            Tree t2 (45);
            break;
      case 2: //illegal: cannot jump past ctor 
            Tree t3 (45);
            break;
   }
}

虽然上面的代码不适用于用户定义的对象,但如果我用内置对象替换它们,它就可以工作。这是为什么?

编辑: 内置对象,如 int、char 等。 我得到的错误(ubuntu 上的 g++ 4.5):

jumpPastConstructor.c++: In function ‘void foo(int)’:
jumpPastConstructor.c++:26:3: error: jump to label ‘label’
jumpPastConstructor.c++:24:20: error:   from here
jumpPastConstructor.c++:25:10: error:   crosses initialization of ‘Tree t’
jumpPastConstructor.c++:31:16: error: jump to case label
jumpPastConstructor.c++:29:25: error:   crosses initialization of ‘Tree t2’

I want to know at which point the compiler allocates storage for local variables inside a block. How does goto and switch jump past a constructor? :

class Tree {/*...*/}
...
void foo (int i){
if (i < 10) goto label; //illegal: cannot jump past a ctor
 Tree t (45);
 label: 
   switch (i){
      case 1:
            Tree t2 (45);
            break;
      case 2: //illegal: cannot jump past ctor 
            Tree t3 (45);
            break;
   }
}

While the above code does not work for user-defined objects it works if i replace them with built-in objects. Why is that?

Edit:
Built in objects like int, char, etc.
The errors i get (g++ 4.5 on ubuntu):

jumpPastConstructor.c++: In function ‘void foo(int)’:
jumpPastConstructor.c++:26:3: error: jump to label ‘label’
jumpPastConstructor.c++:24:20: error:   from here
jumpPastConstructor.c++:25:10: error:   crosses initialization of ‘Tree t’
jumpPastConstructor.c++:31:16: error: jump to case label
jumpPastConstructor.c++:29:25: error:   crosses initialization of ‘Tree t2’

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

蓝礼 2024-11-24 19:59:17

6.7/3:

可以转入
阻止,但不是以绕过的方式
带初始化的声明。一个
程序从某个点跳转
具有自动功能的局部变量
存储持续时间不在范围内
它在范围内的点是
除非变量具有 POD,否则格式错误
类型 (3.9) 并且声明时没有
初始化程序 (8.5)。

重要的不是何时分配存储,而是何时调用构造函数。跳过构造函数的 goto 将是一个问题,这就是它被禁止的原因。 (没有初始化器的 POD 类型不需要任何构造,因此是允许的。)

6.7/3:

It is possible to transfer into a
block, but not in a way that bypasses
declarations with initialization. A
program that jumps from a point where
a local variable with automatic
storage duration is not in scope to a
point where it is in scope is
ill-formed unless the variable has POD
type (3.9) and is declared without an
initializer (8.5).

What matters is not when the storage is allocated, but when the constructor is called. A goto that jumped past a constructor would be a problem, which is why it's banned. (POD types with no initialiser don't need any construction, so they're allowed.)

随遇而安 2024-11-24 19:59:17

问题的第一部分很简单:大多数编译器将所有本地分配整理成单个堆栈分配,然后对该分配进行分区。仅当它们进入范围或显式初始化时才会发生初始化。

从编码的角度来看,您的示例非常糟糕,因为您跳过了 x 进入范围的点,因此构造函数永远不会被调用(这就是 goto)以及为什么你的编译器告诉你停止尝试滥用它。但是,某些类型可以保持未初始化,例如 intfloat 等内置类型。您会收到警告,这就是为什么并非所有类型都会抛出异常如果跳过其初始化(构造函数),则会出现错误。

the first part of the question is easy: most compilers collate all local allocations into a single stack allocation and then partition off that allocation. the initialization happens only when they come into scope or they are explicitly initialized.

Your example is pretty bad from a coding point of view, as you jump over the point at which x comes into scope, thus the constructor will never be called (this is one of the reasons why goto is bad) and why your compiler is telling you to stop trying to abuse it. However, certain types can be left uninitialized, such as the built-in types of int, float etc. you'll instead get a warning, which is why not everything throws an error if you jump over its initialization (constructor).

森罗 2024-11-24 19:59:17

转换为 xx.cpp 中的可编译代码:

class C
{
    int i;
public:
    C(int i_val = 0) : i(i_val) { }
};

int main()
{
    int someval = 2;
    goto label; //error
    C x;
label:
    switch (someval)
    {
        case 1:
            C x2;
            break;
        case 2: //error
            C x3;
            break;
    }
}

并按 MacOS X 10.6.8 上的 G++ 4.6.0 所示进行编译,会产生所示错误:

$ g++ -Wall -Wextra -c xx.cpp
xx.cpp: In function ‘int main()’:
xx.cpp:13:1: error: jump to label ‘label’ [-fpermissive]
xx.cpp:11:10: error:   from here [-fpermissive]
xx.cpp:12:7: error:   crosses initialization of ‘C x’
xx.cpp:19:14: error: jump to case label [-fpermissive]
xx.cpp:17:15: error:   crosses initialization of ‘C x2’
$

每个变量 x有一个默认构造函数>x2x3

C++ 标准只是简单地规定,不允许您跳入变量构造之后的块。有效的方法是:

class C
{
    int i;
public:
    C(int i_val = 0) : i(i_val) { }
};

int main()
{
    int someval = 2;
    goto label; //error
    {
    C x;
    }
label:
    switch (someval)
    {
        case 1:
            {
            C x2;
            }
            break;
        case 2: //error
            {
            C x3;
            }
            break;
    }
}

使用三对额外的大括号,您不再跳到声明和初始化变量的块,因此代码是合法的,并且可以在前面显示的命令行下干净地编译。

Converted into compilable code in xx.cpp:

class C
{
    int i;
public:
    C(int i_val = 0) : i(i_val) { }
};

int main()
{
    int someval = 2;
    goto label; //error
    C x;
label:
    switch (someval)
    {
        case 1:
            C x2;
            break;
        case 2: //error
            C x3;
            break;
    }
}

and compiled as shown with G++ 4.6.0 on MacOS X 10.6.8 yields the errors shown:

$ g++ -Wall -Wextra -c xx.cpp
xx.cpp: In function ‘int main()’:
xx.cpp:13:1: error: jump to label ‘label’ [-fpermissive]
xx.cpp:11:10: error:   from here [-fpermissive]
xx.cpp:12:7: error:   crosses initialization of ‘C x’
xx.cpp:19:14: error: jump to case label [-fpermissive]
xx.cpp:17:15: error:   crosses initialization of ‘C x2’
$

There is a default constructor for each of the variables x, x2, and x3.

And the C++ standard simply says you are not allowed to jump into a block past variable construction. What would work is:

class C
{
    int i;
public:
    C(int i_val = 0) : i(i_val) { }
};

int main()
{
    int someval = 2;
    goto label; //error
    {
    C x;
    }
label:
    switch (someval)
    {
        case 1:
            {
            C x2;
            }
            break;
        case 2: //error
            {
            C x3;
            }
            break;
    }
}

With the three extra pairs of braces, you are no longer jumping into the blocks where the variables are declared and initialized, so the code is legitimate and compiles cleanly under the command line shown before.

〃温暖了心ぐ 2024-11-24 19:59:17

这里的解决方案是:为每个CASE添加括号

switch (i){
  case 1:{
        Tree t2 (45);
        break;
  }
  case 2: {//illegal: cannot jump past ctor 
        Tree t3 (45);
        break;
  }

}

我不知道这有什么疯狂的!!!!但是添加 { 和 } 可以解决这个问题!

Solution here is: Add bracket to each CASE

switch (i){
  case 1:{
        Tree t2 (45);
        break;
  }
  case 2: {//illegal: cannot jump past ctor 
        Tree t3 (45);
        break;
  }

}

I don't know what crazy is this!!!! But Add { and } could solve this issue!

江心雾 2024-11-24 19:59:17

您不能跨构造函数 gotocase。内置函数没有构造函数。

编译器知道当它进入函数时其局部变量的总空间需求是多少,并且它将移动堆栈指针以容纳它们。该内存未初始化。

它在函数流程中根据需要调用构造函数和析构函数。这就是为什么你不能像这样使用 gotocase —— 它破坏了不变量。诸如 break 之类的语句根据需要在 for 循环中调用析构函数,并且一切正常。

You can't goto or case across constructors. Builtins don't have constructors.

The compiler knows when it enters the function what the total space requirement of its local variables is, and it will move the stack pointer to accommodate them. This memory is uninitialised.

It calls the constructors and destructors during the function flow as it needs to. This is why you can't use goto or case like that -- it breaks the invariants. Statements such as break call destructors as necessary in, say, a for-loop, and everything works out OK.

煮茶煮酒煮时光 2024-11-24 19:59:17

我来到这里是因为我遇到了同样的问题。
对我有帮助的是把“树”移到case外面。
并且不是 Tree t2 (45); 而是将 Tree t2; 放在 switch 之前。
以及 case 1 内的 t2 (45);

然后您需要对 Tree t3 (45); 执行相同的操作。

I came here because I had the same problem.
What helped for me was to decare the 'Tree' outside the case.
And to not Tree t2 (45); but to place Tree t2; before switch.
And t2 (45); inside case 1.

And then you need to do the same for Tree t3 (45);.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文