Valid, but worthless syntax in switch-case?

CSwitch StatementLanguage Lawyer

C Problem Overview


Through a little typo, I accidentally found this construct:

int main(void) {
	char foo = 'c';
 
	switch(foo)
	{
		printf("Cant Touch This\n");   // This line is Unreachable

		case 'a': printf("A\n"); break;
		case 'b': printf("B\n"); break;
		case 'c': printf("C\n"); break;
		case 'd': printf("D\n"); break;
	}
 
	return 0;
}

It seems that the printf at the top of the switch statement is valid, but also completely unreachable.

I got a clean compile, without even a warning about unreachable code, but this seems pointless.

Should a compiler flag this as unreachable code?
Does this serve any purpose at all?

C Solutions


Solution 1 - C

Perhaps not the most useful, but not completely worthless. You may use it to declare a local variable available within switch scope.

switch (foo)
{
    int i;
case 0:
    i = 0;
    //....
case 1:
    i = 1;
    //....
}

The standard (N1579 6.8.4.2/7) has the following sample:

> EXAMPLE    In the artificial program fragment

> switch (expr) { int i = 4; f(i); case 0: i = 17; /* falls through into default code */ default: printf("%d\n", i); }

> the object whose identifier is i exists with automatic storage duration (within the block) but is never initialized, and thus if the controlling expression has a nonzero value, the call to the printf function will access an indeterminate value. Similarly, the call to the function f cannot be reached.

P.S. BTW, the sample is not valid C++ code. In that case (N4140 6.7/3, emphasis mine):

> A program that jumps90 from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5).

> ----

> 90) The transfer from the condition of a switch statement to a case label is considered a jump in this respect.

So replacing int i = 4; with int i; makes it a valid C++.

Solution 2 - C

> Does this serve any purpose at all?

Yes. If instead of a statement, you put a declaration before the first label, this can make perfect sense:

switch (a) {
  int i;
case 0:
  i = f(); g(); h(i);
  break;
case 1:
  i = g(); f(); h(i);
  break;
}

The rules for declarations and statements are shared for blocks in general, so it's the same rule that allows that that also allows statements there.


Worth mentioning as well is also that if the first statement is a loop construct, case labels may appear in the loop body:

switch (i) {
  for (;;) {
    f();
  case 1:
    g();
  case 2:
    if (h()) break;
  }
}

Please don't write code like this if there is a more readable way of writing it, but it's perfectly valid, and the f() call is reachable.

Solution 3 - C

There is a famous use of this called Duff's Device.

int n = (count+3)/4;
switch (count % 4) {
  do {
    case 0: *to = *from++;
    case 3: *to = *from++;
    case 2: *to = *from++;
    case 1: *to = *from++;
  } while (--n > 0);
}

Here we copy a buffer pointed to by from to a buffer pointed to by to. We copy count instances of data.

The do{}while() statement starts before the first case label, and the case labels are embedded within the do{}while().

This reduces the number of conditional branches at the end of the do{}while() loop encountered by roughly a factor of 4 (in this example; the constant can be tweaked to whatever value you want).

Now, optimizers can sometimes do this for you (especially if they are optimizing streaming/vectorized instructions), but without profile guided optimization they cannot know if you expect the loop to be large or not.

In general, variable declarations can occur there and be used in every case, but be out of scope after the switch ends. (note any initialization will be skipped)

In addition, control flow that isn't switch-specific can get you into that section of the switch block, as illustrated above, or with a goto.

Solution 4 - C

Assuming you are using gcc on Linux, it would have given you a warning if you're using 4.4 or earlier version.

The -Wunreachable-code option was removed in gcc 4.4 onward.

Solution 5 - C

Not only for variable declaration but advanced jumping as well. You can utilize it well if and only if you're not prone to spaghetti code.

int main()
{
    int i = 1;
    switch(i)
    {
        nocase:
        printf("no case\n");

        case 0: printf("0\n"); break;
        case 1: printf("1\n"); goto nocase;
    }
    return 0;
}

Prints

1
no case
0 /* Notice how "0" prints even though i = 1 */

It should be noted that switch-case is one of the fastest control flow clauses. So it must be very flexible to the programmer, which sometimes involves cases like this.

Solution 6 - C

It should be noted, that there are virtually no structural restrictions on the code within the switch statement, or on where the case *: labels are placed within this code*. This makes programming tricks like duff's device possible, one possible implementation of which looks like this:

int n = ...;
int iterations = n/8;
switch(n%8) {
    while(iterations--) {
        sum += *ptr++;
        case 7: sum += *ptr++;
        case 6: sum += *ptr++;
        case 5: sum += *ptr++;
        case 4: sum += *ptr++;
        case 3: sum += *ptr++;
        case 2: sum += *ptr++;
        case 1: sum += *ptr++;
        case 0: ;
    }
}

You see, the code between the switch(n%8) { and the case 7: label is definitely reachable...


* As supercat thankfully pointed out in a comment: Since C99, neither a goto nor a label (be it a case *: label or not) may appear within the scope of a declaration that contains a VLA declaration. So it's not correct to say that there are no structural restrictions on the placement of the case *: labels. However, duff's device predates the C99 standard, and it does not depend on VLA's anyway. Nevertheless, I felt compelled to insert a "virtually" into my first sentence due to this.

Solution 7 - C

You got your answer related to the required gcc option -Wswitch-unreachable to generate the warning, this answer is to elaborate on the usability / worthyness part.

Quoting straight out of C11, chapter §6.8.4.2, (emphasis mine)

> switch (expr) > { > int i = 4; > f(i); > case 0: > i = 17; > /* falls through into default code */ > default: > printf("%d\n", i); > } > > the object whose identifier is i exists with automatic storage > duration (within the block) but is never initialized, and thus if the > controlling expression has a nonzero value, the call to the printf > function will access an indeterminate value. Similarly, the call to > the function f cannot be reached.

Which is very self-explanatory. You can use this to define a locally scoped variable which is available only within the switch statement scope.

Solution 8 - C

It is possible to implement a "loop and a half" with it, although it might not be the best way to do it:

char password[100];
switch(0) do
{
  printf("Invalid password, try again.\n");
default:
  read_password(password, sizeof(password));
} while (!is_valid_password(password));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionabelenkyView Question on Stackoverflow
Solution 1 - CAlexDView Answer on Stackoverflow
Solution 2 - Cuser743382View Answer on Stackoverflow
Solution 3 - CYakk - Adam NevraumontView Answer on Stackoverflow
Solution 4 - C16tonsView Answer on Stackoverflow
Solution 5 - CDellowarView Answer on Stackoverflow
Solution 6 - Ccmaster - reinstate monicaView Answer on Stackoverflow
Solution 7 - CSourav GhoshView Answer on Stackoverflow
Solution 8 - CceltschkView Answer on Stackoverflow