Why can't I "goto default;" or "goto case x;" within a switch selection structure?

10.8k views Asked by At

Section 6.8.1 of C11 or C99, or section 3.6.1 of C89 all seem to indicate that default and case x (where x is some constant-expression) are examples of labeled statements, along-side identifier:-style labels that are suitable for use with goto.

I'm aware that I could simply place an identifier:-style label directly following the default: or case x: labels. That's not what this question is about. I'm more curious as to whether there is any actual rationale behind prohibiting this kind of behaviour.

If it were possible to declare default: labels outside of a switch selection structure, then I would understand, as there would be some conflict between where the goto inside of the switch selection structure is intended to aim. However, section 6.4.1 of C11 or C99 or 3.1.1 of C89 prohibits the use of default as anything other than a keyword, and 6.8.1 restricts its use further to switch structures only (or generic structures in C11, which are irrelevant here).

I would also understand if multiple (possibly nested) switch structures, each with default: (or case x:) labels introduced ambiguity, however the scope of those labels seems to be restricted to within their inner-most surrounding switch structures, and referring to any identifier outside of its scope is clearly an error requiring a diagnostic at compile-time.

Has this been discussed in any standard documents (e.g. the rationale)? Is there any kind of explanation for this behaviour other than "it is because it is" or "because the spec says so"? If so, what is that explanation?

4

There are 4 answers

0
John Doe On

ISO/IEC 9899:2011 Information technology -- Programming languages -- C

6.8.1 Labeled statements

  1. Syntax

labeled-statement:

identifier : statement

case constant-expression : statement

default : statement

  1. A case or default label shall appear only in a switch statement. Further constraints on such labels are discussed under the switch statement.
  2. Label names shall be unique within a function.
  3. Any statement may be preceded by a prefix that declares an identifier as a label name. Labels in themselves do not alter the flow of control, which continues unimpeded across them.

6.8.6 Jump statements

  1. Syntax

jump-statement:

goto identifier ;

6.8.6.1 The goto statement

  1. The identifier in a goto statement shall name a label located somewhere in the enclosing function.
  2. A goto statement causes an unconditional jump to the statement prefixed by the named label in the enclosing function.

6.2.1 Scopes of identifiers

  1. A label name is the only kind of identifier that has function scope. It can be used (in a goto statement) anywhere in the function in which it appears, and is declared implicitly by its syntactic appearance (followed by a : and a statement).

case, default labels are not identifiers, named labels.

"Because the spec says so." Do not overthink it.

7
madmurphy On

I'm aware that I could simply place an identifier:-style label directly following the default: or case x: labels. That's not what this question is about. I'm more curious as to whether there is any actual rationale behind prohibiting this kind of behaviour.

At cost of sounding tautologic, the rationale is that each case is intended to be uniquely jumped into from one single goto (the one created by the switch statement). Which is also why we need to group together more case labels if we want a block of code to be executed in case of more than one condition.

The following code might give you a visual answer. If goto [case label] were allowed, what would you write in order to go to the first case, goto case 'q' or goto case 'e'? Which one is the boss of that block of code?

#include <stdio.h>

int yes_or_no (char input, char additional_yes) {

    int retval;

    switch (input) {

        case 'q':
        case 'e':

            printf("Bye bye! :-)\n");
            retval = 0;
            break;

        case 'n':

            printf("I am very sad that you said no :-(\n");
            retval = 0;
            break;

        case 'y':
        other_yes:

            printf("Thank you for saying yes! :-)\n");
            retval = 0;
            break;

        case '\n':
        case '\r':
        case ' ':
        case '\t':
        case '\v':
        case '\f':

            printf("Mmmmm... maybe you need time to think...\n");
            retval = 1;
            break;

        default:

            if (input == additional_yes) {

                goto other_yes;

            }

            printf("\"%c\" is not an answer!\n", input);
            retval = 1;


    }

    return retval;

}

int main (int argc, char *argv[]) {

    printf("Please, answer (yes/ja or no/nein) -- e or q to quit\n");

    while (yes_or_no(getchar(), 'j'));

    return 0;

}

Consider also that, while optimizing, the compiler might decide not to use jump tables at all if it thinks that a simple repeated if would be faster than a switch (this might happen especially when the case labels are not many). In this case, if goto [case label] were allowed, you would force the compiler to create a label where normally there wouldn't be one.

This is one of the things I like most of C: if you want something you have to say it.

6
Jens Gustedt On

(I don't see how goto to a case would work syntactically.)

As you say, case and default labels only have the scope of the corresponding switch statement and can only be jumped to from outside. On the other hand labels in C have function scope and can be jumped to from anywhere in the function.

So we are talking of labels with a quite different properties, they are probably treated quite different, internally. It seems relatively complicated to reconcile these properties and would make implementing this more complicated. All of a sudden you would have to have to decide when implementing goto which case or default is the correct one, whereas now you just have to put the address of the file scope identifier, there.

All of this said, this is just a guess about the original intentions for this distinction. But I am sure that argumentation along these lines would quickly kill any attempt to introduce such a feature now.

5
Peter Miehle On

counter example

fun() {
  switch (b) {
     case x:
       doSomething();
       break;
  }
  switch(b2) {
     case x:
       doMore();
       break;
  }

  goto x; // which one?
}