I\'ve always wondered this - why can\'t you declare variables after a case label in a switch statement? In C++ you can declare variables pretty much anywhere (and declaring
New variables can be decalared only at block scope. You need to write something like this:
case VAL:
// This will work
{
int newVal = 42;
}
break;
Of course, newVal only has scope within the braces...
Cheers, Ralph
This question is was originally tagged as [C] and [C++] at the same time. The original code is indeed invalid in both C and C++, but for completely different unrelated reasons.
In C++ this code is invalid because the case ANOTHER_VAL:
label jumps into the scope of variable newVal
bypassing its initialization. Jumps that bypass initialization of automatic objects are illegal in C++. This side of the issue is correctly addressed by most answers.
However, in C language bypassing variable initialization is not an error. Jumping into the scope of a variable over its initialization is legal in C. It simply means that the variable is left uninitialized. The original code does not compile in C for a completely different reason. Label case VAL:
in the original code is attached to the declaration of variable newVal
. In C language declarations are not statements. They cannot be labeled. And this is what causes the error when this code is interpreted as C code.
switch (val)
{
case VAL: /* <- C error is here */
int newVal = 42;
break;
case ANOTHER_VAL: /* <- C++ error is here */
...
break;
}
Adding an extra {}
block fixes both C++ and C problems, even though these problems happen to be very different. On the C++ side it restricts the scope of newVal
, making sure that case ANOTHER_VAL:
no longer jumps into that scope, which eliminates the C++ issue. On the C side that extra {}
introduces a compound statement, thus making the case VAL:
label to apply to a statement, which eliminates the C issue.
In C case the problem can be easily solved without the {}
. Just add an empty statement after the case VAL:
label and the code will become valid
switch (val)
{
case VAL:; /* Now it works in C! */
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Note that even though it is now valid from C point of view, it remains invalid from C++ point of view.
Symmetrically, in C++ case the the problem can be easily solved without the {}
. Just remove the initializer from variable declaration and the code will become valid
switch (val)
{
case VAL:
int newVal;
newVal = 42;
break;
case ANOTHER_VAL: /* Now it works in C++! */
...
break;
}
Note that even though it is now valid from C++ point of view, it remains invalid from C point of view.
A switch
block isn't the same as a succession of if/else if
blocks. I'm surprised no other answer explains it clearly.
Consider this switch
statement :
switch (value) {
case 1:
int a = 10;
break;
case 2:
int a = 20;
break;
}
It may be surprising, but the compiler will not see it as a simple if/else if
. It will produce the following code :
if (value == 1)
goto label_1;
else if (value == 2)
goto label_2;
else
goto label_end;
{
label_1:
int a = 10;
goto label_end;
label_2:
int a = 20; // Already declared !
goto label_end;
}
label_end:
// The code after the switch block
The case
statements are converted into labels and then called with goto
. The brackets create a new scope and it is easy to see now why you can't declare two variables with the same name within a switch
block.
It may look weird, but it is necessary to support fallthrough (that is, not using break
to let execution continue to the next case
).
C++ Standard has: 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).
The code to illustrate this rule:
#include <iostream>
using namespace std;
class X {
public:
X()
{
cout << "constructor" << endl;
}
~X()
{
cout << "destructor" << endl;
}
};
template <class type>
void ill_formed()
{
goto lx;
ly:
type a;
lx:
goto ly;
}
template <class type>
void ok()
{
ly:
type a;
lx:
goto ly;
}
void test_class()
{
ok<X>();
// compile error
ill_formed<X>();
}
void test_scalar()
{
ok<int>();
ill_formed<int>();
}
int main(int argc, const char *argv[])
{
return 0;
}
The code to show the initializer effect:
#include <iostream>
using namespace std;
int test1()
{
int i = 0;
// There jumps fo "case 1" and "case 2"
switch(i) {
case 1:
// Compile error because of the initializer
int r = 1;
break;
case 2:
break;
};
}
void test2()
{
int i = 2;
switch(i) {
case 1:
int r;
r= 1;
break;
case 2:
cout << "r: " << r << endl;
break;
};
}
int main(int argc, const char *argv[])
{
test1();
test2();
return 0;
}
After reading all answers and some more research I get a few things.
Case statements are only 'labels'
In C, according to the specification,
§6.8.1 Labeled Statements:
labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
In C there isn't any clause that allows for a "labeled declaration". It's just not part of the language.
So
case 1: int x=10;
printf(" x is %d",x);
break;
This will not compile, see http://codepad.org/YiyLQTYw. GCC is giving an error:
label can only be a part of statement and declaration is not a statement
Even
case 1: int x;
x=10;
printf(" x is %d",x);
break;
this is also not compiling, see http://codepad.org/BXnRD3bu. Here I am also getting the same error.
In C++, according to the specification,
labeled-declaration is allowed but labeled -initialization is not allowed.
See http://codepad.org/ZmQ0IyDG.
Solution to such condition is two
Either use new scope using {}
case 1:
{
int x=10;
printf(" x is %d", x);
}
break;
Or use dummy statement with label
case 1: ;
int x=10;
printf(" x is %d",x);
break;
Declare the variable before switch() and initialize it with different values in case statement if it fulfills your requirement
main()
{
int x; // Declare before
switch(a)
{
case 1: x=10;
break;
case 2: x=20;
break;
}
}
Some more things with switch statement
Never write any statements in the switch which are not part of any label, because they will never executed:
switch(a)
{
printf("This will never print"); // This will never executed
case 1:
printf(" 1");
break;
default:
break;
}
See http://codepad.org/PA1quYX3.
Interesting that this is fine:
switch (i)
{
case 0:
int j;
j = 7;
break;
case 1:
break;
}
... but this isn't:
switch (i)
{
case 0:
int j = 7;
break;
case 1:
break;
}
I get that a fix is simple enough, but I'm not understanding yet why the first example doesn't bother the compiler. As was mentioned earlier (2 years earlier hehe), declaration is not what causes the error, even despite the logic. Initialisation is the problem. If the variable is initialised and declared on the different lines, it compiles.