4

I recently came across the code snippet shown below, I was expecting it to be a syntax error but to my surprise, the code produces valid output.

#include <stdio.h>

int main(void) {
    int x = 2;

    switch(x) {
    case 1: printf("1"); break;

        do {
            case 2: printf("2 "); break;
            case 3: printf("3 "); break;
        } while(++x < 4);

        case 4: printf("4"); break;
    }
    return 0;
}

output: 2 4

Compiler: GCC 6.3

I found a similar problem but it is not justifying above condition completely, Mixed 'switch' and 'while' in C

Can anyone explain,

  1. What exactly happening here?
  2. Why isn't it a syntax error?
  3. Why case '3' is skipped?
tevemadar
  • 12,389
  • 3
  • 21
  • 49
TruthSeeker
  • 1,539
  • 11
  • 24
  • 2
    One of the comments in the Stack Overflow you question points to another question about [Duff’s Device](https://stackoverflow.com/questions/5569416/how-can-duffs-device-code-be-compiled). It is valid C because the grammar for case labels allows them to be prefixed to any statement. The compiler merely implements them as jump instructions (in its abstract machine). – Eric Postpischil Jul 29 '18 at 19:40

2 Answers2

5

case X: some_statement; is a labeled statement (6.8.1) just like goto_label: some_statement; with the only caveat that case/default labels may only appear inside the body of a switch (possibly in an arbitrarily nested compound statement). That makes case statements only very loosely coupled with switches, syntactically.

Semantically, switches are implementable as computed gotos and like regular gotos, they may jump pretty much anywhere (in C11, you can't jump past a VLA declaration) including inside of a loop (see https://en.wikipedia.org/wiki/Duff%27s_device#Mechanism for another description).

In your example, case 3: is skipped because of the break, but case 4: does follow because the break after case 3: is a loop-breaking break, not a switch-breaking break (break/continue always apply to the nearest construct they can apply to).

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • If I understand it correctly, break inside case '2' has caused termination of the loop and execution fall through in case '4' and switch is exited. – TruthSeeker Jul 30 '18 at 04:14
  • @prasadM Yup. If you want to easily see into code like that of the Duff's device, forget about switches tightly coupled with cases and fallthrough and simply think of case labels like a special kind of goto-labels that can only be used in switches. With that, fall-through's natural. `break`s inside `do{/*...*/}while(/*...*/);` jump right after the semicolon after `while(/*...*/);`. A `case 4:`-labeled statement follows. It is executed because it's just a labeled statement so there's no reason for the code to jump somewhere else. – Petr Skocik Jul 30 '18 at 07:25
0

The official C grammar for a switch statement is:

switch ( expression ) statement

Any statement can be a labeled-statement, for which the grammar includes:

case constant-expression : statement

This means you can put pretty much whatever you want inside the body of the switch: It can be a compound statement which includes multiple statements and a do-while statement that includes multiple statements, and case labels can be prefixed to any of those statements.

The compiler merely implements the switch using jump instructions (inside its abstract machine; they may end up as other instructions after optimization). Loops statements such as do-while are implement with code that tests the controlling expression and jumps conditionally. So, in spite of the nice structure you think of in structured languages, it boils down to jump instructions, and those can be interwoven as desired.

The jumps are not the most disconcerting part of this. Object initialization and lifetime is more of a concern. Switch statements can unintentionally skip the initialization of objects if care is not taken.

The printf of “3 ” is never executed because control jumps from the switch to case 2, then breaks, which exits the do-while. This leaves code at the case 4 statement, which prints “4 ” and then breaks out of the switch statement.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312