Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

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?

share|improve this question
34  
GCC has a special flag for this. It's -Wswitch-unreachable – Eli Sadoff Jan 18 at 19:08
39  
"Does this serve any purpose at all?" Well, you can goto in and out of the otherwise unreachable part, which may be useful for various hacks. – HolyBlackCat Jan 18 at 19:09
11  
@HolyBlackCat Wouldn't that be such for all unreachable code? – Eli Sadoff Jan 18 at 19:10
24  
@EliSadoff Indeed. I guess it doesn't serve any special purpose. I bet it is allowed just because there is no reason to forbid it. After all, switch is just a conditional goto with multiple labels. There are more or less same restrictions on it's body as you would have on a regular block of code filled with goto labels. – HolyBlackCat Jan 18 at 19:14
14  
Worth pointing out that @MooingDuck s example is a variant on Duff's device (en.wikipedia.org/wiki/Duff's_device) – Michael Anderson Jan 19 at 2:14
up vote 135 down vote accepted

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++.

share|improve this answer
2  
"... but is never initialized ..." Looks like i is initialized to 4, what am I missing? – yano Jan 18 at 19:24
6  
Note that if the variable is static, it will be initialized to zero, so there's a safe usage for this as well. – Leushenko Jan 18 at 19:29
20  
@yano We always jump over the i = 4;initialization, so it never takes place. – AlexD Jan 18 at 19:32
9  
Hah of course! ... whole point of the question ... geez. The desire is strong to delete this stupidity – yano Jan 18 at 19:35
1  
Nice! Sometimes I needed a temp variable inside a case, and always had to use different names in each case or define it outside the switch. – SJuan76 Jan 19 at 13:08

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.

share|improve this answer
5  
shoot, yeah, I've seen that loop construct before, but totally forgot about it. Scaaary. – Marcus Müller Jan 18 at 19:21
6  
@MarcusMüller: Duff's Device, scary yeah... – Matthieu M. Jan 19 at 13:13
    
@MatthieuM Duff's Device does have case labels inside a loop, but starts with a case label before the loop. – hvd Jan 19 at 15:38
    
I'm not sure if I should upvote for the interesting example or downvote for the utter madness of writing this in a real program :). Congrats for diving into the abyss and returning back in one piece. – Liviu T. Jan 19 at 23:44
    
@ChemicalEngineer: If the code is part of a loop, as it is in Duff's Device, { /*code*/ switch(x) { } } may look cleaner but it is also wrong. – Ben Voigt 2 days ago

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.

share|improve this answer
2  
Of course, this would still be possible without allowing statements above the first case, as the order of do { and case 0: don't matter, both serve to place a jump target on the first *to = *from++;. – Ben Voigt 2 days ago
1  
@BenVoigt I'd argue that putting the do { is more readable. Yes, arguing about readability for Duff's Device is stupid and pointless and likely a simple way to go mad. – QPaysTaxes 2 days ago
    
@QPaysTaxes You should check out Simon Tatham’s Coroutines in C. Or maybe not. – Jonas Wielicki 36 mins ago

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

-Wunreachable-code option was removed in gcc 4.4 onward. https://gcc.gnu.org/ml/gcc-help/2011-05/msg00360.html

share|improve this answer
1  
When you know why, you know why. Interesting. Thanks. – Jonathan Leffler Jan 18 at 19:09
    
Having experienced the issue first hand always helps! – 16tons Jan 18 at 19:13
    
@JonathanLeffler: The general issue of gcc warnings being susceptible to the particular set of optimization passes selected is still true, unfortunately, and makes for a poor user experience. It's really annoying to have a clean Debug build followed by a failing Release build :/ – Matthieu M. Jan 19 at 19:38
1  
@MatthieuM.: If significant semantic analysis would be required to discover that a certain variable will always be false at some spot in the code, code which is conditional upon that variable being true would be found to be unreachable only if such analysis were performed. On the other hand, I would consider a notice that such code was unreachable rather differently from a warning about syntactically-unreachable code, since it should be entirely normal to have various conditions be possible with some project configurations but not others. It may at times be helpful for programmers... – supercat 2 days ago
1  
...to know why some configurations generate larger code than others [e.g. because a compiler could regard some condition as impossible in one configuration but not another] but that doesn't mean that there's anything "wrong" with code which could be optimized in that fashion with some configurations. – supercat 2 days ago

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.

share|improve this answer
16  
If I were working with you, I would no longer be working with you. – njzk2 Jan 20 at 3:41
    
And what is difference between nocase: and default:? – i486 2 days ago
    
@i486 Wheni=4 it does not trigger nocase. – Sanchke Dellowar 2 days ago
1  
@njzk2 I'm a man who likes his gotos. Deal with it B-\ – Sanchke Dellowar 2 days ago
    
@SanchkeDellowar that's what I mean. – njzk2 2 days ago

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.

share|improve this answer

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));
share|improve this answer
5  
"might not"? ;-) – Richard II Jan 19 at 13:55
    
@RichardII Is it a pun or what? Please explain. – Dancia 2 days ago
1  
@Dancia He's saying that this is pretty clearly not the best way to do something like this, and "might not" is something of an understatement. – QPaysTaxes 2 days ago

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.

share|improve this answer
    
The addition of variable-length arrays led to the imposition of structural restrictions related to them. – supercat Jan 20 at 0:09
    
@supercat What kind of restrictions? – cmaster 2 days ago
    
Neither a goto nor switch/case/default label may appear within the scope of any variably-declared object or type. This effectively means that if a block contains any declarations of variable-length-array objects or types, any labels must precede those declarations. There's a confusing bit of verbiage in the Standard which would suggest that in some cases the scope of a VLA declaration extends to the entirety of a switch statement; see stackoverflow.com/questions/41752072/… for my question on that. – supercat 2 days ago
    
@supercat Thanks. Learned something today :-) – cmaster 2 days ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.