23

I was playing with some syntax and found some strange compiler rules, was wondering what the reasoning is for this

C will not compile this but C++ will:

switch (argc) {
case 0:
    int foo;
    break;
default:
    break;
}

Both C and C++ will compile this:

switch (argc) {
case 0:
    ; int foo;
    break;
default:
    break;
}

C will compile this but not C++:

switch (argc) {
case 0:
    ; int foo = 0;
    break;
default:
    break;
}

gcc -v is gcc version 4.9.3 (MacPorts gcc49 4.9.3_0) if it matters. I realize the solution is to wrap the contents of case 0: with curly brackets, but I am more interested in the reasoning for compilation errors

asimes
  • 5,749
  • 5
  • 39
  • 76
  • 5
    [This answer](http://stackoverflow.com/a/92730/962089) is somewhat relevant. – chris Jul 22 '15 at 04:29
  • 2
    It looks like the second case still works for c++ if you set `foo` in an additional statement. gcc just complains about the unused variable in [this example](http://coliru.stacked-crooked.com/a/ba80bc1da97d8212), and clang is just fine with it. – jaggedSpire Jul 22 '15 at 04:34
  • 1
    @chris, Thanks, the third case kind of makes sense now (cannot jump over initialization in C++) – asimes Jul 22 '15 at 04:35
  • I can't see why the first isn't valid C. Looking at C99, a labeled statement is *identifier : statement* and there's no mention of a need for an expression there (I still get the error with a normal label and no switch). Edit: Never mind, GCC is more descriptive and explicitly says that a declaration is not a statement in C. – chris Jul 22 '15 at 04:38
  • 1
    @chris A declaration is not a statement in C (but it is in C++). – sepp2k Jul 22 '15 at 04:45
  • @sepp2k, Well then I guess the first example (kind of) makes sense then. If `int foo;` is not a statement in C but it follows `case 0:` then I guess it should be an error – asimes Jul 22 '15 at 04:50

1 Answers1

23
case 0:
    int foo;

In both C and C++ a labeled statement is a label followed by a statement. However in C++ the definition of a statement includes "block declarations" (that is declarations and definitions that may appear in a block) whereas in C it does not (in C a block is a sequence of "block items", which are either block declarations or statements - in C++ it's a sequence of statements, which include block declarations).

case 0:
    ; int foo;

This works because ; is a(n empty) statement in both C and C++, so here we indeed have a label followed by a statement.

case 0:
    ; int foo = 0;

As was already explained in the comments, this does not work in C++ because C++ makes it illegal to jump over an initialization.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • This is a nice answer but the third case is still the one that confuses me the most. How is it jumping over an initialization? Why does placing the definition inside of curly brackets avoid jumping over an initialization? – asimes Jul 22 '15 at 05:00
  • 1
    @asimes, When you put braces, `{; int foo = 0;}` is the statement for the *labeled-statement*. When you don't, `int foo = 0;` is part of the switch rather than one statement for a case, so it is in scope beyond the next label, but its initialization is skipped. – chris Jul 22 '15 at 05:02
  • @asimes It's potentially jumping over the initialization because it could jump to the default case, where `foo` would be in scope, but not initialized. If you add braces, `foo` is no longer in scope in the default block. Basically the "jumping over an initialization" rules prohibits you from jumping "in the middle" of the scope of a variable that has an initializer. That is, you're allowed to jump to the initialization or after the variable has gone out of scope, but not to anywhere between those points. – sepp2k Jul 22 '15 at 05:03
  • If I may: why does separating the declaration and initialization of `foo` placate gcc, when the initialization is still in the switch statement, and being skipped by at least one case? – jaggedSpire Jul 22 '15 at 05:04
  • 3
    @jaggedSpire, Assignment isn't initialization. You can just as easily do `int i; foo(i); i = 0;` without skipping over anything. – chris Jul 22 '15 at 05:05
  • Thanks guys, I think I put it all together now :) – asimes Jul 22 '15 at 05:05