14

Assume:

switch ( test ) {

  // Set some variables, call some functions ?
  int x = 1 ;
  int y = function(x) ;
  //

  case 1 : 
    // Process for test = 1
    ...
    break;

  case 5 : 
    // Process for test = 5
    ...
    break;

  default : 
    // Process for all other cases.
    ...

}

Is it 'legal' to execute that extra bit of code I added before the first case? I've never seen this in examples.

Ulfalizer
  • 4,664
  • 1
  • 21
  • 30
Peter
  • 1,334
  • 12
  • 28
  • 5
    What happens if you try to compile it? – lurker Mar 13 '15 at 01:56
  • Yes, yet do not recommend. – chux - Reinstate Monica Mar 13 '15 at 02:07
  • Why don't you want to put your code right before the switch? – Jérôme Mar 13 '15 at 02:08
  • 1
    C++ does not allow such a jump. C forgive but it does not know whether initialization. – BLUEPIXY Mar 13 '15 at 02:13
  • @lurker, it compiles, using Borland C++ Builder 2009, which surprised me, hence my question. It's an easy fix (put the code before the switch()), but out of curiosity I tried it and the mere fact that it compiled raised the question. – Peter Mar 13 '15 at 02:51
  • @Peter: Putting declarations inside a switch block is, as everyone says, not a good idea. But there is a long history of putting code lines there, starting with [Duff's device](http://en.wikipedia.org/wiki/Duff%27s_device). And my little contribution to the insanity, still publicly available on [LtU](http://lambda-the-ultimate.org/node/1131#comment-12412) Of course, none of this stuff would pass code review these days. – rici Mar 13 '15 at 02:55
  • After I played with it a bit, using `gcc`, it compiled for me as well. I would think if you turned on the right warnings in C++ Builder you'd get a warning message about it. Based upon the behavior of the compilers, I decided it was a good question, so I upvoted it. – lurker Mar 13 '15 at 02:55
  • There are already great answers to your question. On the off-chance that you wanted to scope the variables that are related to work that needs to be done in the switch statement, then you can always just add an additional scope outside the switch may give you almost the same thing. E.g. `{ int x = 1; switch (test) {...} }`. This way x doesn't pollute the outer scope namespace, and if x is something complicated like a class, it gets destructed when the scope is exited. See this SO post for more: http://stackoverflow.com/a/5072864/601626 – Rob Apr 30 '15 at 14:02

5 Answers5

20

First some background on how switch (really) works:

A switch is usually thought of as a construct that selects a piece of code to execute depending on the value of some expression, as in

switch (x) {
case 1:
    foo();
    break;

case 2:
    bar();
    break;
}

However, it's more accurate to think of a switch as a form of computed goto statement. For example, the following is perfectly legal:

switch (x) {
    puts("I can't be reached");
case 1:
    if (cond) {
case 2:
        puts("Either x == 1 && cond, or x == 2");
    }
}

Depending on the value of x, the program will jump to either case 1 or case 2 (or past the switch if x is neither 1 nor 2).


Your program will compile as C (with junk values for x and y inside the switch, since the initializations are skipped), but not as C++. The reason is that C++ does not allow a jump to a case label to cross the initialization of a variable. For simple types like int, skipping over int x; is allowed (since no initialization is involved), but not skipping over int x = 1;.

The main motivation for this difference is probably that letting a jump to a case label cross an initialization in C++ would be unsafe when constructors are involved. For example, if C++ allowed a case label to occur after a definition My_class my_object within some scope, then jumping to that case label would skip my_object's constructor but still run its destructor when exiting the scope.

The same restrictions apply to goto in C++. You can't use it to jump into a block and past a variable initialization.


As a side note, switch follows the same general syntax as if and while. The syntax of if as given in the C11 standard (ISO/IEC 9899:2011, section 6.8.4) is

if ( expression ) statement

, while the syntax of switch is

switch ( expression ) statement

The only difference as far as statement is concerned (in C -- C++ adds some more limitations as mentioned above) is that it is allowed to contain case labels (and break) for a switch but not for an if (unless the if occurs within a switch).

Just as with an if, you can even leave off the braces and write code like the following. (Whether this is unnecessarily confusing is another discussion.)

switch (x) case 1: case 2: puts("x is 1 or 2");

Syntactically, case and default labels belong in the same category as goto labels. Section 6.8.1 of the C11 standard has the following definition:

labeled-statement:
        identifier : statement
        case constant-expression : statement
        default : statement

Ulfalizer
  • 4,664
  • 1
  • 21
  • 30
9

You can find out what happens with a simple test:

int w = 1;

switch (w)
{
    int i = 3;
    int y = foo(i);

case 1:
    printf("here %d\n", y);
    printf("here %d\n", i);
    break;

case 2:
    printf("not here\n");
    break;
}

This code will compile inside of a function using gcc. The C compiler sees i and y declared inside of a block delimited by the braces, so will accept it. However, the printf statements will print junk for i and y, because the assignments are never executed. This is because a switch statement forms a jump to the case which corresponds to the expression at the head of the switch. So the executable code between the opening brace and the first case cannot be reached. See Why can't variables be declared in a switch statement?, which doesn't explain exactly the same scenario, but does have some related discussion about the switch statement.

If you compile this with warnings turned on (gcc -Wall), you get:

foo.c: In function ‘main’:
foo.c:19:15: warning: ‘y’ may be used uninitialized in this function [-Wuninitialized]
foo.c:20:15: warning: ‘i’ may be used uninitialized in this function [-Wuninitialized]

Interestingly, the following code will compile without warnings and work:

int w = 1;

switch (w)
{
    int i;
    int y;

case 1:
    i = 2; y = 3 * i;
    printf("here %d\n", y);
    printf("here %d\n", i);
    break;

case 2:
    i = 1; y = 2;
    printf("here %d\n", y);
    printf("here %d\n", i);
    break;
}

The variables print as you would expect them to as they're declared within the scope of the switch block, and values set within the case sections where execution occurs. The fact that it works in this case is not to say that it is recommended practice. :)

Community
  • 1
  • 1
lurker
  • 56,987
  • 9
  • 69
  • 103
4

Control can reach statements in between the switch head and the first case expression -- but some other control structure has to send it there. For instance, there is nothing stopping you from interlacing a switch and a loop:

#include <stdio.h>

int main(void)
{
    int x = 1;
    switch (x) {
        do {
            printf("got here with x=%d\n", x);
    case 2:
            puts("case two or loop");
    case 1:
            puts("case one or loop");
        } while (++x < 3);
    }
    return 0;
}

compiles without any complaints from either gcc 4.9 or clang 3.5 in -std=c11 mode with the warnings cranked up as high as they will go, and when run, prints:

case one or loop
got here with x=2
case two or loop
case one or loop

Fun fact: with optimization enabled, both compilers will actually generate 100% straight-line code:

main:
    subq    $8, %rsp
    movl    $.LC0, %edi
    call    puts
    movl    $2, %esi
    movl    $.LC1, %edi
    xorl    %eax, %eax
    call    printf
    movl    $.LC2, %edi
    call    puts
    movl    $.LC0, %edi
    call    puts
    xorl    %eax, %eax
    addq    $8, %rsp
    ret
zwol
  • 135,547
  • 38
  • 252
  • 361
1

It is not legal, and when you compile you will get a bunch of "crosses initialization of ..." errors. There's a really good explanation of what's going on here: Getting a bunch of crosses initialization error

Essentially, a switch case works by jumping over any code in between the case statements, and it's illegal to jump past the initialization of a variable. It will technically be legal, and will compile, if you just put a function call in between the two, although the function will never actually be called.

int test = 1;
int x = 1 ; 
switch ( test ) { 

    //Technically legal, although will never be called.
    std::cout<<somefunc(x)<<std::endl;;  

    //jumps straight here, over any code above it
    case 1 : 
        std::cout<<"1"<<std::endl;
        break;

    case 5 : 
        std::cout<<"5"<<std::endl;
        break;

    default : 
        std::cout<<"Default."<<std::endl;
}  
Community
  • 1
  • 1
Schiem
  • 589
  • 3
  • 12
  • 2
    It's legal (but a really bad idea) in C. It's illegal in C++. – Keith Thompson Mar 13 '15 at 02:45
  • It compiled, using Borland C++ Builder 2009, but it was indeed only a function call (I did not check if it got called), I extended the question with more possibilities without testing that either. I basically thought it was illegal so I was surprised that it compiled, and since I did not trust it, I could not trust whatever happened either. A question to the experts was in order. – Peter Mar 13 '15 at 02:55
0

I tried to compile a code similar to what you ask, it comes out at least one error:

error C2360: initialization of 'x' is skipped by 'case' label

Your extra bit of code has never been executed because after switch check the statement 'test', then it will go to corresponding label, your extra bit of code is not under any label, therefore it is forever been skipped.

Why you can't put your extra bit of code before the switch statement? any reason?

V-SHY
  • 3,925
  • 4
  • 31
  • 47