Storage allocation of local variables inside a block in c++
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 obj开发者_如何学Cects. 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’
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.)
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).
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.
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!
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.
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);
.
精彩评论