4

I recently discovered that switch (...) while (0) {} is legal in C (here), but I cannot find an explanation about this thing.
The only other time I saw this in the Internet is in an obfuscation GitHub repository, without any explanation.

All my researches obviously gave me results about while or switch loops, without even mentioning this syntax, so I guess it's more something legal but very rare and likely useless, an abuse of the standard. Can anyone help me understand this ?

EDIT : As explained in @phuclv answer, a switch statement expects a selection statement right after, which can be brackets with some code inside (in this case... likely case statements) or a loop with its own brackets etc, it means this is legal in C :

switch (...) while (...) switch (...) {}

switch doesn't care at all about what statement follows, it only seems to look for case(s) and / or default.

switch (1) while (0) {
    puts("Not executed");
}

The puts statement isn't executed because there's no case / default, so the switch is basically useless here. You can see it on Compiler Explorer, GCC gives a warning and removed the switch.

However, beware :

#include <stdio.h>

int main(void) {
    switch (1) while (1) switch (0) {
        case 1:
        puts("hello");
    }
}

Nothing is displayed and the program is exited instantly, because the switch (1) has no case 1 or default statement. If we add one :

switch (1) case 1: while (1) switch (0)

The program loops indefinitely because the most nested loop is switch (0) with no case 0 or default.

Conclusion : The while (0) is only an abuse and has no utility except obfuscation, but that's still an interesting thing to know.

Chi_Iroh
  • 1,061
  • 5
  • 14
  • 1
    Are you asking what it does (the link already explains), or why it is allowed? – HolyBlackCat Jun 11 '23 at 08:34
  • 1
    There is no real point to this, it just leverages / demonstrates that C's switch is little more than local jumps and doesn't really care about anything else, so you can interleave it with other C syntax. An other demonstration which is actually useful is [Duff's Device](https://en.wikipedia.org/wiki/Duff%27s_device), but it is more complicated as it actually does thing, so the core insight is less clear. – Masklinn Jun 11 '23 at 08:35
  • Ok I see, I have already seen Duff's device interesting abuse of switch, but here I was wondering if there was any interest other than allow continue in the switch. Also @HolyBlackCat if you know why such thing is legal, I'm listening to you. Does this act lake a while loop inside a switch or is it different ? Or does it act like a switch taking an one-line statement (as while whithout {}) so the while loop is counted as one instruction belonging to the switch ? – Chi_Iroh Jun 11 '23 at 08:50
  • 1
    @Masklinn It's highly questionable if Duff's Device is actually useful in modern programming, it has _not_ aged well. – Lundin Jun 12 '23 at 10:00
  • 1
    Being syntactically valid does not mean that the syntax has a purpose. That is kind of the point of the answer you referenced in your question. It is simply "emergent behaviour" of the switch construct syntax definition/implementation. It happens because it is not explicitly prevented, not because it was intended. To prevent it would require a more complex language, definition and more complex compiler for no particular benefit. – Clifford Jun 25 '23 at 18:25

2 Answers2

8

In the C standard a statement is defined as one of these

A.2.3 Statements

(6.8) statement:

labeled-statement
compound-statement
expression-statement
selection-statement
iteration-statement
jump-statement

switch is a selection-statement which is defined to have syntax like this

(6.8.4) selection-statement:

if ( expression ) statement
if ( expression ) statement else statement
switch ( expression ) statement

So switch receives an expression and do the statement, which is normally the compound-statement {} but it can be any statement including the iteration-statement

(6.8.2) compound-statement:

{ block-item-listopt }

(6.8.5) iteration-statement:

while ( expression ) statement
do statement while ( expression ) ;
for ( expressionopt ; expressionopt ; expressionopt ) statement
for ( declaration expressionopt ; expressionopt ) statement

switch statement jumps to the labeled-statement (which is also a normal statement) and it doesn't care about the content inside the sub statement, the compiler will just generate a computed jump into the case matching expression

(6.8.1) labeled-statement:

identifier : statement
case constant-expression : statement
default : statement
phuclv
  • 37,963
  • 15
  • 156
  • 475
0

basically, the construction

switch(expr) while (cond) {
}

should be almost equivalent to:

while(cond) {
    switch(expr) {
    }
}

except for the order in which cond and expr are evaluated.

Just check the following code:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    switch(random() % 6) while(1) {
        case 0: printf("You got a one(1)\n");
        case 1: printf("You got a two(2)\n");
        case 2: printf("You got a three(3)\n");
        case 3: printf("You got a four(4)\n");
        case 4: printf("You got a five(5)\n");
        case 5: printf("You got a six(6)\n");
    }
}

when run, it gives you something like:

You got a four(4)
You got a five(5)
You got a six(6)
You got a one(1)
You got a two(2)
You got a three(3)
You got a four(4)
You got a five(5)
You got a six(6)
You got a one(1)
...

Edit

I'm afraid the output of my post is not actually very random :) So, I'm afraid the equivalence is far from true.

After checking it against the while(cond) switch(expr) i got a more or less random sequence, so for some reason the expr is not completely evaluated when used the former construct.

After checking it against the switch(expr) while(cond) I got no randomness, as the random() evaluates only once, and produces the output shown (the first random number happens to be a 0, selecting the first case, and all the others in sequence, looping back to the while loop. In this case, using break; in the switch is of no use, as it will break out of the loop making you seen behaviour (the loop executes until the break; statement, getting out of the loop)

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • I just tried adding break at the end of each case but I don't understand why it runs only once. – Chi_Iroh Jun 24 '23 at 09:07
  • Do you actually need that construction? – Luis Colorado Jun 25 '23 at 17:54
  • 1
    See my edit to the answer, you gave me a hint to answer and to see why the first approach generates some randomness, and the second doesn't. Change the `break;` statement into a `goto end;` (and put an `end` label after the last `case` label), and you will get your desired output. – Luis Colorado Jun 25 '23 at 18:04
  • I understand better more, that's an interesting case. I'll use a debugger with some codes containing tricky switch to follow their control flow – Chi_Iroh Jun 26 '23 at 05:48
  • 1
    probably the best thing is to check what the compiler assembles the code into. IMHO there's no fair application of putting the `switch` statemente outer to the `while` loop. The switch seems to evaluate only once... and the next passes select the first case statement. A bit weird. – Luis Colorado Jun 26 '23 at 13:59
  • We can see in compiler explorer (https://godbolt.org/z/3dqG5MKrj) that random is in fact called once at the beginning, as you said. – Chi_Iroh Jun 26 '23 at 17:03