19

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?

autistic
  • 1
  • 3
  • 35
  • 80
  • 5
    @molbdnilo If your line of reasoning is "`goto` can be dangerous, thus it shouldn't be used" then think about the line of reasoning "Any feature in C can be dangerous, thus it shouldn't be used"... or "Driving can be dangerous, ...". Does this make sense, any more? The quote that started the whole "goto is bad" cargo cult crud, originally written by Edsger Dijkstra, has been used out of context by people such as yourself for decades so many times that he long ago wrote comments on it showing regret. – autistic Jun 29 '15 at 11:04
  • 2
    @molbdnilo FYI, the original context was more along the lines of "If there are more suitable control structures (e.g. `if/else`, `while/do-while`, `switch` and functions) then those should be used"... However, there are still a small number of usecases in C where `goto` is the most appropriate control structure, the simplest being [jumping into cascading resource clean-ups near the end of a function for error handling](http://stackoverflow.com/a/46638/1989425) or [jumping out of multiple nested loop/switch control structures](http://stackoverflow.com/a/47472/1989425), without extra logic. – autistic Jun 29 '15 at 11:11
  • @molbdnilo ... and as a more complex example of a decent use of `goto`, see the example in [David Tribbles conclusion entitled "The tao of goto"](http://david.tribble.com/text/goto.html#conclusion). – autistic Jun 29 '15 at 11:29
  • yes - goto considered useful for cascading error labels to ensure ( in a simple non-repetitive way ) that all local allocations/resource requests are unrolled on errors.. – amdixon Jun 29 '15 at 11:42

4 Answers4

6

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

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 9
    In C# you can use `goto case x;` to jump to a label explicitly because case fallthrough is forbidden – phuclv Jun 29 '15 at 10:56
  • 1
    This answer is nice, and was my guess too, though it doesn't make implementing `goto` *much* more difficult. What I'd really love (and would get my accepted answer) is a quote from the rationale, or something, if there is one... – autistic Jun 29 '15 at 10:56
  • It's not just scope; case and default labels are just not legal targets for goto statements. And you can always write case somecase: somecase: ... goto somecase; – gnasher729 Apr 12 '16 at 07:51
  • @gnasher729 Do you see how the question refers to the C standard? Do you think you can find something official to support your assertion? – autistic Jul 19 '16 at 07:00
  • In response to an answer below, I wrote this code: `{ int x = 42; { int x = -1; } }`. At any given point beyond the first declaration within this nest of blocks, you should be able to declare another `int` and assign it to `x`... which `x`? Well, there are rules in C which tell us about shadowing. – autistic Apr 29 '18 at 15:02
  • @gnasher729 Well, yes, manually defining a label corresponding to each branch of a `switch` is always an option. Mind you, that label can only exist once in the function, so if you want to add the same switch (with different logic within) again later on, you're boned. Additionally, since there's no synonym for *default* and `case default: default: ... goto default;` is illegal, it becomes quite difficult to write maintainable code which pertains to an end-user concept referred to as a "default" action; it becomes a game of "what's the least confusing synonym?". Have you looked them up? I have. – autistic Apr 29 '18 at 15:09
6

counter example

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

  goto x; // which one?
}
Peter Miehle
  • 5,984
  • 2
  • 38
  • 55
  • 11
    Both of them are out of scope. Did you read the entire question? – autistic Jun 29 '15 at 10:56
  • To elaborate, I'm referring more to examples like this, where `case x` are *still in scope*: `switch (b) { case x: doSomething(); default: goto case x; /* which one? */ } switch (b2) { case x: doMore(); default: goto case x; /* which one? */ }` ... and the answer to the questions in the comments should be obvious. – autistic May 06 '17 at 04:49
  • 1
    `{ int x = 42; { int x = -1; } } int y = x; // which one?` – autistic Oct 15 '17 at 15:46
  • 1
    To be clear, I'm not advocating for what you seem to think. Which one is irrelevant there, because the scope of both is non-existent outside of those switches, and thus that would be still an error (the same category of errors as the example rebuttal I gave). `case` and `default` would still be limited in scope to their respective `switch` statements, so you would only be able to `goto default` from **within** the `switch`. Does this make sense? The accepted answer at least addresses this. – autistic Apr 29 '18 at 14:52
  • I also want to point out that I'm well aware there's an easy "solution": Define your own label name, and there are times when *default* makes sense as a label name, right? Can you think of a synonym for *default*? Because I kinda wanted to use it, and struggled for a while... good luck ;) – autistic Apr 29 '18 at 14:55
2

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.

John Doe
  • 373
  • 3
  • 4
0

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.

madmurphy
  • 1,451
  • 11
  • 20
  • 1
    You may want to avoid asking rhetorical questions in your answers, as the answer is obvious in this case. `x: y: // which one can you goto? x, y, or both?` The answer is that *neither* are the boss. Most significantly, please note that `case` labels are actually labels, as defined by the C standard, and that the condition in a `switch` statement may only be evaluated once where-as it would be evaluated numerous times in a chain of `if`-`else`-`else if`. Have you attempted to write a C compiler before? – autistic May 16 '18 at 00:40
  • I notice you are implicitly converting the `int` returned by `getchar` straight to a `char` without first checking whether it's `EOF`... tsssk tsssk, [that is a common error](http://c-faq.com/stdio/getcharc.html). One major pitfall of C is: sometimes the compiler and your perception of what the compiler does do not agree with each other. Sometimes you invoke *undefined behaviour*, if you're not careful, and unlike other languages where you get error messages, in C you get bugs like *heartbleed*... Reality check: if you're new to C (as in six months or less), why are you studying it? – autistic May 16 '18 at 00:46
  • @Sebivor My example does not need to use the `int` of `getchar()` (do you see a `case EOF:`?), so a direct cast will do just fine. And my example was not about `getchar()`, but about the `case` labels, about which we agree to disagree (and that's fine). I see that you like to review things by the way. I [recently wrote a library](https://madmurphy.github.io/libconfini/html/index.html) that would need some third opinion. If you have time… :) – madmurphy May 17 '18 at 23:54
  • Yes, it does. `EOF` is *not* a character value; it's a value outside of the character values that `getchar` returns (which are all `unsigned char`, if you read the manual, hence distinct from the negative value `EOF`, hence the negative value `EOF` is not a character value). When you convert the return value straight to a character value, you're discarding the fact that `EOF` is not a character value, and treating it as though it is one... You should do *less guessing and more reading*. – autistic Jun 09 '18 at 14:14
  • You seem to have an obsession with magic numbers... that sucks for you, because it makes your code suck for others. – autistic Jun 09 '18 at 14:27
  • They are not so magic, they are called bitmasks. I'm sure you must have read about them :) – madmurphy Jun 10 '18 at 02:22
  • I tend not to trust software that lacks internal documentation in such a serious way... You may trust it because you wrote it, but that is not transitive for everyone else. Just sayin'. – autistic Jun 11 '18 at 01:13