Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's compiler thinking about the switch-statement?

Inspired from a -5 question again!

  • Is empty case of switch in C# combined with the next non-empty one?

I read [this comment] of @Quartermeister and been astonished!

So why this compiles

switch(1) {
    case 2:
}

but this doesn't.

int i;

switch(i=1) {
    case 2: // Control cannot fall through from one case label ('case 2:') to another
}

neither this

switch(2) {
    case 2: // Control cannot fall through from one case label ('case 2:') to another
}

update:

The -5 question became -3.

like image 597
Ken Kin Avatar asked Mar 07 '13 20:03

Ken Kin


People also ask

Why do programmers use switch?

The main reasons for using a switch include improving clarity, by reducing otherwise repetitive coding, and (if the heuristics permit) also offering the potential for faster execution through easier compiler optimization in many cases.

What happens in a switch statement?

A switch statement first evaluates its expression. It then looks for the first case clause whose expression evaluates to the same value as the result of the input expression (using the strict comparison, === ) and transfers control to that clause, executing all statements following that clause.

Why are switch statements better?

A switch statement is significantly faster than an if-else ladder if there are many nested if-else's involved. This is due to the creation of a jump table for switch during compilation. As a result, instead of checking which case is satisfied throughout execution, it just decides which case must be completed.

What is switch in C programming?

The switch statement in C is an alternate to if-else-if ladder statement which allows us to execute multiple operations for the different possibles values of a single variable called switch variable. Here, We can define various statements in the multiple cases for the different values of a single variable.


1 Answers

None of them should compile. The C# specification requires that a switch section have at least one statement. The parser should disallow it.

Let's ignore the fact that the parser allows an empty statement list; that's not what's relevant. The specification says that the end of the switch section must not have a reachable end point; that's the relevant bit.

In your last example, the switch section has a reachable end point:

void M(int x) { switch(2) { case 2: ; } }

so it must be an error.

If you had:

void M(int x) { switch(x) { case 2: ; } }

then the compiler does not know if x will ever be 2. It assumes conservatively that it could, and says that the section has a reachable end point, because the switch case label is reachable.

If you had

void M(int x) { switch(1) { case 2: ; } }

Then the compiler can reason that the endpoint is not reachable because the case label is not reachable. The compiler knows that the constant 1 is never equal to the constant 2.

If you had:

void M(int x) { switch(x = 1) { case 2: ; } }

or

void M(int x) { x = 1; switch(x) { case 2: ; } }

Then you know and I know that the end point is not reachable, but the compiler does not know that. The rule in the specification is that reachability is only determined by analyzing constant expressions. Any expression which contains a variable, even if you know its value by some other means, is not a constant expression.

In the past the C# compiler had bugs where this was not the case. You could say things like:

void M(int x) { switch(x * 0) { case 2: ; } }

and the compiler would reason that x * 0 had to be 0, therefore the case label is not reachable. That was a bug, which I fixed in C# 3.0. The specification says that only constants are used for that analysis, and x is a variable, not a constant.

Now, if the program is legal then the compiler can use advanced techniques like this to influence what code is generated. If you say something like:

void M(int x) { if (x * 0 == 0) Y(); }

Then the compiler can generate the code as though you'd written

void M(int x) { Y(); }

if it wants. But it cannot use the fact that x * 0 == 0 is true for the purposes of determining statement reachability.

Finally, if you have

void M(int x) { if (false) switch(x) { case 2: ; } }

then we know that the switch is not reachable, therefore the block does not have a reachable end point, so this is, surprisingly, legal. But given the discussion above, you now know that

void M(int x) { if (x * 0 != 0) switch(x) { case 2: ; } }

does not treat x * 0 != 0 as false, so the end point is considered reachable.

like image 196
Eric Lippert Avatar answered Sep 30 '22 05:09

Eric Lippert