1116

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 them close to first use is obviously a good thing) but the following still won't work:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

The above gives me the following error (MSC):

initialization of 'newVal' is skipped by 'case' label

This seems to be a limitation in other languages too. Why is this such a problem?

Rob
  • 76,700
  • 56
  • 158
  • 197
  • 10
    For an explanation based on the C BNF grammar, see http://stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/1181106#1181106%3E – johne Jan 12 '10 at 03:30
  • [Here is a really good read](http://complete-concrete-concise.com/programming/c/keyword-switch-case-default) about switch statements and labels (ABC:) in general. – Etherealone Aug 16 '12 at 22:33
  • 5
    I would say 'Why can't variables be initialized in a switch statement rather than declared'.Since just declaring the variable give me only a warning in MSVC. – ZoomIn Dec 09 '13 at 06:21
  • 4
    If you put everything inside the case label within curly braces { } then it will work. – E Purdy Aug 27 '20 at 13:33

23 Answers23

1331

Case statements are only labels. This means the compiler will interpret this as a jump directly to the label. In C++, the problem here is one of scope. Your curly brackets define the scope as everything inside the switch statement. This means that you are left with a scope where a jump will be performed further into the code skipping the initialization.

The correct way to handle this is to define a scope specific to that case statement and define your variable within it:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}
Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
TJ Seabrooks
  • 20,203
  • 6
  • 33
  • 30
  • 4
    @TallJef I don't know what 'old days' you're referring to. I have never encountered a compiler where all the stack space for a method *isn't* allocated when the method is entered, in 40 years. – user207421 Apr 10 '17 at 08:05
  • @EJP: Well, when `_alloca()` is used, the compiler can't know how much space is needed at entry, so it has to make piecemeal adjustments. – Ben Voigt Nov 29 '17 at 21:29
  • 2
    I encountered a peculiar situation with such statemenet in IAR compiler. There was an array inside case (with scope), but memory was allocated regardless of entering the case, just by entering function. Since other cases led to deeper stack than this one, it eventually resulted in a stack overflow. – Do-do-new Apr 30 '18 at 11:40
  • @MarquisofLorne I definitely have. Actually relied to it in some app where I had a recursive function with a temporary array that wasn't allocated through the whole function call, and not when a recursive call was made. – gnasher729 Jun 10 '20 at 04:38
  • It should be noted that on MSVC (probably similar to what @Do-do-new saw) the stack locations of the locals in each case blocks don't coalesce. So the total stack space used by the function becomes the sum of all local sizes in each case even if that case is never taken. To avoid this, rather than putting braces around each case like this, a function call can be made to introduce a new scope. The easiest way to do that is to declare a lambda inline, then force it to be _not_ outlined so that it's allocated in the .text section like any other free global function. – Stephen Eckels Oct 08 '22 at 02:38
  • #define SCOPE(code) [&]() DECLSPEC_NOINLINE { code }() – Stephen Eckels Oct 08 '22 at 02:39
  • 1
    @StephenEckels: Please no. The IIFE is not idiomatic C++ and hiding it in a macro is even worse. All programmer expectations of what `break;`, `return` etc do are violated. – Ben Voigt Jan 02 '23 at 15:35
  • It's a hack to work around a deficiency in the compilers allocation of local stack space when using the more typical workaround of braces in the cases. Obviously it has tradeoffs, if you don't care about that use the { } in cases. – Stephen Eckels Jan 25 '23 at 18:19
449

This question was originally tagged as and 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.

Starting from C23 all labels in C language will be interpreted as labelling implied null statements (N2508), i.e. the issue with being unable to place labels in front of declarations in C will no longer exist and the above ;-based fix will no longer be necessary.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 6
    @AnT: I understand why the one that fixes C++ isn't applicable for C; however, I'm unable to understand how it fixes the C++ issue of skipping the initialization in the first place? Wouldn't it still skip the declaration and assignment of `newVal` when it jumps to `ANOTHER_VAL`? – legends2k Jun 19 '15 at 09:08
  • 20
    @legends2k: Yes, it still skips it. However, when I say "it fixes the issue" , I mean that it fixes *the C++ compiler error*. In C++ it is illegal to skip a scalar declaration *with initializer*, but it is perfectly fine to skip a scalar declaration *without initializer*. At `case ANOTHER_VAL:` point variable `newVal` is visible, but with indeterminate value. – AnT stands with Russia Jun 19 '15 at 14:45
  • 3
    Fascinating. I found this question after reading `§A9.3: Compound Statement` from K&R C (second edition). The entry mentioned the technical definition of a *compound-statement* which is `{declaration-list[opt] statement-list[opt]}`. Confused, because I had thought a declaration WAS a statement, I looked it up and immediately found this question, an example where said disparity becomes apparent and actually **breaks** a program. I believe another solution (for C) would be to put another statement (possibly a null statement?) *before* the declaration so that the *labeled-statement* is satisfied. – Braden Best Feb 04 '17 at 10:04
  • 1
    Oops, I just noticed that the null-statement solution I suggested is already in your answer. Never mind, then. – Braden Best Feb 04 '17 at 10:19
  • The best solution is probably to just declare the variable outside of the switch statement. – SeanRamey Sep 07 '17 at 20:07
  • 6
    It's worth noting that the fix of adding an empty statement only works for C99 onwards. In C89, variables must be declared at the start of their enclosing block. – Arthur Tacca Dec 12 '19 at 16:40
  • @AnT It seems like a pretty bad design decision for a language that's supposed to be mostly backwards-compatible to allow `int x; x=42; label:;` but issue a hard error for `int x=42; label1;`. C's refusal to accept labeled declarations seems like similarly arbitrary a restriction for C in its current form (but at least C never advertised itself as mostly forward-compatible with C++). – Petr Skocik Jun 15 '20 at 11:16
  • As an aside, moving the declaration without initialization before all case-labels fixes it in both languages. – Deduplicator Sep 19 '20 at 19:41
  • @AnTstandswithRussia But there's no rule in C that one statement = one line... – justanotherguy May 03 '23 at 04:04
142

Ok. Just to clarify this strictly has nothing to do with the declaration. It relates only to "jumping over the initialization" (ISO C++ '03 6.7/3)

A lot of the posts here have mentioned that jumping over the declaration may result in the variable "not being declared". This is not true. An POD object can be declared without an initializer but it will have an indeterminate value. For example:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' set (not initialized) to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

Where the object is a non-POD or aggregate the compiler implicitly adds an initializer, and so it is not possible to jump over such a declaration:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

This limitation is not limited to the switch statement. It is also an error to use 'goto' to jump over an initialization:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

A bit of trivia is that this is a difference between C++ and C. In C, it is not an error to jump over the initialization.

As others have mentioned, the solution is to add a nested block so that the lifetime of the variable is limited to the individual case label.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Richard Corden
  • 21,389
  • 8
  • 58
  • 85
  • 2
    "Error jumping over initialization"??? Not with my GCC. It may give a "j may be used unitialized" warning when using j below label, but there is no error. However, in case of switch, there is an error (a hard error, not a weak warning). – Mecki Feb 20 '10 at 01:20
  • 9
    @Mecki: It is illegal in C++. ISO C++ '03 - 6.7/3: "...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)." – Richard Corden Feb 22 '10 at 18:21
  • 1
    Yes, but it is not illegal in C (at least gcc says it's not). j will be uninitialized (have some random number), but the compiler compiles it. However, in case of the switch statement, the compiler won't even compile it and I fail to see the difference between a goto/label case and a switch case. – Mecki Feb 22 '10 at 22:01
  • 8
    @Mecki: In general a single compiler behaviour does not necessarily reflect whtat is actually allowed by the language. I've checked both C'90 and C'99 and both standards include an example with a jump over initialization in a switch statement. – Richard Corden Feb 28 '10 at 22:23
43

The whole switch statement is in the same scope. To get around it, do this:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

Note the brackets.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mark Ingram
  • 71,849
  • 51
  • 176
  • 230
35

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

  1. Either use new scope using {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
    
  2. Or use dummy statement with label

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
    
  3. 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.

polfosol ఠ_ఠ
  • 1,840
  • 26
  • 41
Jeegar Patel
  • 26,264
  • 51
  • 149
  • 222
  • 2
    You correctly described the C issue. But the assertion that in C++ labelled initialization is not allowed is completely not true. There's nothing wrong with labeled initialization in C++. What C++ does not allow is *jumping over* initialization of variable `a` into the scope of variable `a`. So, from C point of view, the problems is with `case VAL:` label and you described it correctly. But from C++ point of view, the problem is with `case ANOTHER_VAL:` label. – AnT stands with Russia Nov 07 '13 at 08:14
  • In C++, unlike in C, declarations are a subset of statements. – Keith Thompson Aug 07 '16 at 00:01
21

You can't do this, because case labels are actually just entry points into the containing block.

This is most clearly illustrated by Duff's device. Here's some code from Wikipedia:

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

Notice how the case labels totally ignore the block boundaries. Yes, this is evil. But this is why your code example doesn't work. Jumping to a case label is the same as using goto, so you aren't allowed to jump over a local variable with a constructor.

As several other posters have indicated, you need to put in a block of your own:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }
emk
  • 60,150
  • 6
  • 45
  • 50
  • 1
    This Duff's device implementation has a bug that makes it extremely slow: count is type int so the % must perform a real division/modulo operation. Make count unsigned (or better yet, always use size_t for counts/indices) and the problem goes away. – R.. GitHub STOP HELPING ICE Jul 03 '10 at 20:58
  • 1
    @R..: What?! In a two's complement system, signedness doesn't affect modulos by powers of 2 (it's just an AND on the bottom bits), and doesn't affect divisions by powers of 2 as long as your processor architecture has an arithmetic right-shift operation (`SAR` in x86, versus `SHR` which is for unsigned shifts). – C. K. Young Sep 02 '10 at 14:00
  • @Chris: I believe he means when the compiler must allow for negative values where "just an AND on the bottom bits" doesn't hold; for example, -1 % 8 gives -1 on this two's complement system using g++ (the sign in this case is implementation defined per 5.6/4). –  Sep 02 '10 at 15:45
  • @Roger: Yes, the compiler must accommodate negative values, and it's extra cycles (so unsigned is still more ideal), but on x86 (which I tested with), gcc still doesn't generate a `DIV` instruction. It does the equivalent of: `num < 0 ? ((num + 7) & 7) - 7 : num & 7` (but without any branching, multiplication, or division whatsoever). So, it's not "extremely slow" by any means. – C. K. Young Sep 02 '10 at 16:40
  • Well it's still considerably slower than just a single bitwise and. On modern cpus, I wouldn't be surprised if that code were almost as slow as the division. My point stands: you should use unsigned types or write the bitwise and yourself. – R.. GitHub STOP HELPING ICE Sep 02 '10 at 20:33
  • @Roger: the sign is no longer implementation-defined. C99 defines division with negative numbers, and defines it the wrong way, so that this nasty behavior is now required by the standard. :-( – R.. GitHub STOP HELPING ICE Sep 02 '10 at 20:34
  • 3
    @Chris: I agree with you that R is exaggerating the impact; I only saw your comment and knew a simple AND didn't suffice. –  Sep 02 '10 at 23:21
  • @R..: The original code in this comment was from Wikipedia, as noted above. I've just fixed it to use size_t. Thank you for pointing out the problems with C99's % operator; that's one worth remembering. – emk Sep 05 '10 at 20:48
  • 1
    Also worth noting is the original Wikipedia code is for sending data to a memory mapped output, which looks odd here because it's not mentioned and every byte is copied to the same "to" location. Could get around that by either adding postfix ++ to the to, or mentioning the use case is for memory mapped IO. Totally peripheral to the original question :-). – Peter Mar 04 '11 at 23:47
16

Most of the replies so far are wrong in one respect: you can declare variables after the case statement, but you can't initialize them:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

As previously mentioned, a nice way around this is to use braces to create a scope for your case.

Zebra North
  • 11,412
  • 7
  • 37
  • 49
  • 1
    Mr. 32 you have misunderstood what your error is: yes that's not going to compile but not because you're declaring a variable inside a switch. The error is because you're trying to declare a variable after a statement, which is illegal in C. – Zebra North Dec 18 '11 at 15:26
  • 1
    Now a days that is legal in c90 and newer version of c – Jeegar Patel Dec 19 '11 at 04:39
13

My favorite evil switch trick is to use an if(0) to skip over an unwanted case label.

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

But very evil.

Jeremy
  • 2,321
  • 3
  • 21
  • 24
10

Try this:

switch (val)
{
    case VAL:
    {
        int newVal = 42;
    }
    break;
}
Dan Shield
  • 1,358
  • 7
  • 14
7

You can declare variables within a switch statement if you start a new block:

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

The reason is to do with allocating (and reclaiming) space on the stack for storage of the local variable(s).

Ether
  • 53,118
  • 13
  • 86
  • 159
Seb Rose
  • 3,628
  • 18
  • 29
  • 1
    The variable can be declared, but it cannot be initialized. Also, I'm pretty sure that the issue does not relate in anyway to the stack and local variables. – Richard Corden Sep 18 '08 at 14:12
6

Consider:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

In the absence of break statements, sometimes newVal gets declared twice, and you don't know whether it does until runtime. My guess is that the limitation is because of this kind of confusion. What would the scope of newVal be? Convention would dictate that it would be the whole of the switch block (between the braces).

I'm no C++ programmer, but in C:

switch(val) {
    int x;
    case VAL:
        x=1;
}

Works fine. Declaring a variable inside a switch block is fine. Declaring after a case guard is not.

slim
  • 40,215
  • 13
  • 94
  • 127
  • 3
    @Mr.32 : actually your example shows that a printf is not executed, but in this case, the int x is not a statement but a declaration, the x is declared, space for it is reserved every time the function environment gets stacked, see: http://codepad.org/4E9Zuz1e – Petruza Dec 18 '11 at 07:03
  • I was expecting to find this when reading the title of the question, because the question is not about declaring variables within "case:" labels, but in switch statements. And only you (and VictorH, emphasising your answer) actually talked about variables in switch statements. – cesss Jan 03 '18 at 22:47
4

The entire section of the switch is a single declaration context. You can't declare a variable in a case statement like that. Try this instead:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}
3

So far the answers have been for C++.

For C++, you can't jump over an initialization. You can in C. However, in C, a declaration is not a statement, and case labels have to be followed by statements.

So, valid (but ugly) C, invalid C++

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

Conversly, in C++, a declaration is a statement, so the following is valid C++, invalid C

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}
Peter
  • 971
  • 8
  • 15
  • 1
    The second example is NOT valid C++(test with vc2010 and gcc 4.6.1 C++ not allow skip the initialization part. gcc error message is : cross initialization of 'int i' – zhaorufei Dec 26 '11 at 09:50
3

I just wanted to emphasize slim's point. A switch construct creates a whole, first-class-citizen scope. So it is posible to declare (and initialize) a variable in a switch statement before the first case label, without an additional bracket pair:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}
Community
  • 1
  • 1
3

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.

thkala
  • 84,049
  • 23
  • 157
  • 201
Dan
  • 31
  • 3
  • 1
    First is not fine on gcc 4.2: "error: expected expression before 'int'". As Peter and Mr.32 say, "case 0: ; int j; ..." and "case 0: ; int j = 7; ..." do both work. The problem in C is just that "case – dubiousjim May 30 '12 at 21:21
3

I wrote this answer orginally for this question. However when I finished it I found that answer has been closed. So I posted it here, maybe someone who likes references to standard will find it helpful.

Original Code in question:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

There are actually 2 questions:

1. Why can I declare a variable after case label?

It's because in C++ label has to be in form:

N3337 6.1/1

labeled-statement:

...

  • attribute-specifier-seqopt case constant-expression : statement

...

And in C++ declaration statement is also considered as statement (as opposed to C):

N3337 6/1:

statement:

...

declaration-statement

...

2. Why can I jump over variable declaration and then use it?

Because: N3337 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 (The transfer from the condition of a switch statement to a case label is considered a jump in this respect.)

from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5).

Since k is of scalar type, and is not initialized at point of declaration jumping over it's declaration is possible. This is semantically equivalent:

goto label;

int x;

label:
cout << x << endl;

However that wouldn't be possible, if x was initialized at point of declaration:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;
Community
  • 1
  • 1
PcAF
  • 1,975
  • 12
  • 20
3

If your code says "int newVal=42" then you would reasonably expect that newVal is never uninitialised. But if you goto over this statement (which is what you're doing) then that's exactly what happens - newVal is in-scope but has not been assigned.

If that is what you really meant to happen then the language requires to make it explicit by saying "int newVal; newVal = 42;". Otherwise you can limit the scope of newVal to the single case, which is more likely what you wanted.

It may clarify things if you consider the same example but with "const int newVal = 42;"

2

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).

Dalmas
  • 26,409
  • 9
  • 67
  • 80
1

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

0

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;
}
Jingguo Yao
  • 7,320
  • 6
  • 50
  • 63
0

It appears that anonymous objects can be declared or created in a switch case statement for the reason that they cannot be referenced and as such cannot fall through to the next case. Consider this example compiles on GCC 4.5.3 and Visual Studio 2008 (might be a compliance issue tho' so experts please weigh in)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}
Olumide
  • 5,397
  • 10
  • 55
  • 104
  • If you're going to vote it down please explain why. I'm curious to know why creating an anonymous object appears to be an exemption. – Olumide May 31 '16 at 14:03
  • 1
    not a DV, but: The whole question is about declaring/scope of named variables. A temporary ("anonymous object" isn't a term) isn't a named variable, nor is it a declaration, nor is it subject to scope (unless bound to a `const` reference with scope of its own). It's an expression that lives and dies within its statement (wherever that may be). Therefore, it's wholly irrelevant. – underscore_d Jul 17 '16 at 17:55
  • 1
    `Foo();` is not a declaration; the question is about declarations. – M.M Jul 06 '17 at 04:02
0

I believe the issue at hand is that is the statement was skipped, and you tried to use the var elsewhere, it wouldn't be declared.

William Keller
  • 5,256
  • 1
  • 25
  • 22
0

newVal exists in the entire scope of the switch but is only initialised if the VAL limb is hit. If you create a block around the code in VAL it should be OK.

marijne
  • 2,992
  • 5
  • 22
  • 21