Can I put code outside of cases on the switch?

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 this extra bit of code, which I added before the first case ? I have never seen this in examples.

+7
c ++ c
source share
5 answers

First, how switch works (really):

A switch usually considered 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 is more accurate to think of switch as a form of the computed goto . For example, it is completely 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 program will go to case 1 or case 2 (or by switch if x is neither 1 nor 2).


Your program will compile as C (with junk values ​​for x and y inside the switch , as initialization is skipped), but not as C ++. The reason is that C ++ does not allow going to the case label in order to cross initialize the variable. For simple types such as int , int x; skipped int x; allowed (since initialization is not involved), but not passed through int x = 1; .

The main motivation for this difference is probably that switching to the case label of the initialization crossroads in C ++ would be unsafe if constructors are involved. For example, if C ++ allows the case label to occur after My_class my_object defined within a certain scope, then passing to this case label skips the my_object constructor, but still my_object its destructor when leaving the scope.

The same restrictions apply to goto in C ++. You cannot use it to go into a block even after the variable is initialized.


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

if (expression)

whereas switch syntax is

switch (expression)

The only difference with respect to the statement (in C - C ++, a few more restrictions are added, as mentioned above) is that for switch allowed to contain case labels (and break ), but not for if (unless if not found inside the switch ) .

As with if , you can even leave curly braces and write code, as shown below. (Regardless of whether this is unnecessarily confusing, this is another discussion.)

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

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

labeled operator:
identifier :

case expression constant expression :
by default :

+12
source share

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 be compiled inside the function using gcc . The C compiler sees i and y declared inside a block limited by curly braces, so it will accept it. However, printf statements will print garbage for i and y because assignments are never executed. This is due to the fact that the switch forms a jump in case , which corresponds to the expression at the head of switch . Thus, the executable code between the opening bracket and the first case cannot be reached. See Why variables cannot be declared in a switch statement? , which does not explain the exact same scenario, but has some related discussions regarding the switch .

If you compile this with warnings enabled ( gcc -Wall ), you will 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 warning or 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; } 

Variables are printed as you would expect them, since they are declared within the scope of the switch , and the values ​​set in the case sections where execution is performed. The fact that it works in this case does not mean that it is recommended to practice. :)

+7
source share

A control can reach statements between the switch header and the first case , but some other control structure should send it there. For example, there is nothing to stop you from alternating between 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 gcc 4.9 or clang 3.5 in -std=c11 with warnings raised higher than they will go and prints at startup:

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

Interesting fact: when optimization is enabled, both compilers will generate 100% straightforward 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 
+2
source share

This is not legal, and when you compile, you get a bunch of "cross-initialization ... errors". There's a really good explanation of what's happening here: Getting a bunch of cross initialization errors

In fact, the switch case works by jumping over any code between case statements, and it illegally jumps after the variable is initialized. This will be technically legal and will compile if you simply place a function call between them, although the function will never 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; } 
+1
source share

I tried to compile a code similar to what you are asking, it throws at least one error:

error C2360: initialization "x" is skipped by the label "case"

Your extra bit of code has never been executed, because after the switch checks the β€œtest” instruction, it will go to the corresponding label, your extra bit of code is not under any label, so it is skipped forever.

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

0
source share

All Articles