2
case 1:
            { //question is about this curly brace
                int val;
                scanf("%d", &val);
                if(top1 == NULL){
                    enqueue(top1, val, bottom1);
                }
                else{
                    enqueue(top1, val);
                }
                break;
            }

Without the curly brace after case 1: it gave an error: *

a label can only be part of a statement and a declaration is not a statement: int val;

*

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
rohegde7
  • 633
  • 9
  • 15

3 Answers3

3

That is how the C grammar is defined. Variable declarations are not considered statements:

int x; //a declaration
int y = 3; //another declaration
x = y + 1; //a statement

A label is required to be followed by a statement. A label plus a declaration is not allowed.

foo: int x; //error
bar: x = y +1; //ok

That is, IMO, inconvenient:

while (...)
{
    //...
    goto end;
    end:   //error, no statement   
}

And remember that a case is just a special kind of label, so:

case 1: int x; //error
case 2: x = y +1; //ok

The issue with braces is that in C they are used to build a compound statement, that of course is a kind of statement. The lines inside the compound statements can be both declarations and statements, all mixed (old C versions only allowed declarations at the beginning, not in the middle). So:

case 1: int x; //error: label plus declaration
case 2: { int x; } //ok: label plus compound statement

As a footnote, since modern C allows to intermix declarations and statements you can also write:

case 1:; int x; //ok: label plus empty statement.

because an isolated ; is an empty statement, it can be used to satisfy the grammar whereever a no-op statement is needed.

Whether to use a ; or a { ... } is a matter of readability. In the end: example I'd use a ;, but in the case: I prefer the {...}.

while (...)
{
    //...
    goto end;
    end:;   //ok, empty statement   
}

switch (...)
{
    case 1: //ok, compound statement
    {
        int x;
    }
}

Of course more creative solutions can be written, such as:

case 1: {} int x; //ok, label plus empty compound statement
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • You could use `continue;` instead of `goto end;`, so it's not that inconvenient – ikegami Dec 25 '17 at 20:05
  • @rodrigo: Whether to use `;` or `{ … }` is more than readability. In the former, the scope of the identifier(s) declared extends to the end of the switch statement. This means the identifiers can be used in subsequent portions of the switch statement. If the switch statement is well structured, identifiers in one section are not used in others, and we would prefer `{ declaration; … }` to avoid accidents. If the switch statement requires cases to drop through to others, `; declaration;…` may be required for that particular use. – Eric Postpischil Dec 25 '17 at 20:05
  • @EricPostpischil: Well, about the drop-through, you could write `case 1: { declaration; stmt1; case 2: stmt2; }` but I agree that readability will suffer from that, better just to declare the variable before the full `switch`. – rodrigo Dec 25 '17 at 20:20
2

C Rules About Case Labels

The rules of the C standard that prevent a declaration from following a case label are:

  • A case label must be followed by a statement (C 2011 [N1570] 6.8.1).

  • The C standard defines a statement as one of labeled-statement, compound-statement, expression-statement, selection-statement, iteration-statement, or jump-statement (6.8). None of these is a declaration.

The C standard treats declarations and statements separately. The rule that allows declarations to be largely mingled with statements is that a compound-statement is a list of block-items in braces (that is, { block-item-listopt }) (6.8.2), and a block-item is defined as a declaration or a statement. So, inside braces, you can mix declarations and statements. But a case label must be part of a statement; it is not a separate thing you can insert anywhere.

Declarations can be included inside a switch using two alternatives. One is to use an empty statement after the case label, as in:

case 123:
    ;
    int foo;
    …

Another is to use a compound statement after the case label, as in:

case 123:
{
    int foo;
    …
}

Generally, the latter is preferable, because the scope of foo is limited to the compound statement, so it cannot be used accidentally in another section of the switch statement.

Reasons For the Rules

I do not see a reason for this other than history. Originally, declarations were even more restricted than they are now. Inside functions, declarations had to be the first statements inside braces. You could not put a declaration after any statement. That has been relaxed in modern C, but why is there still a restriction on what follows a case label?

There cannot be a semantic reason that a declaration cannot follow a case label in modern C, because the empty-statement example above would have the same semantics as:

case 123:
    int foo;

That is, the compiler would have to be prepared to create and initialize a new object at the same point in execution. Since it has to do that for the legal example code, it would be able to do it for this version too.

I also do not see a syntactic or grammatical barrier. The colon after the constant expression of a case label is pretty distinct. (The constant expression can have colons in it from ? : operators, but the first : not associated with a ? will be the end of the case label.) Once parsing reaches that colon, the current parsing state seems clean. I do not see why it could not recognize either a declaration or a statement there, just as it was prepared to do before the case.

(If somebody can find a problem in the grammar that would be caused by allowing a case label to be followed by a declaration, that would be interesting.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

First to say, the error you post is a syntax error related to the format of a case label statement. It allows you to use only an executable statement, and not a declaration. Put an empty statement before the declaration and you'll be ok. Try the following:

#include <stdio.h>
int main()
{
    switch(3) {
        int x = 3;  /* the initializer is ignored by the compiler
                     * you can include declarations here, but as
                     * this code is never executed, the initializer
                     * never takes place. */
    case 3:; /* <=== look this semicolon to allow the next declaration */
        int y = 5;
        printf("x = %d, y = %d\n", x, y);
        break;
    }
}

The first variable, x, will be declared properly, but the initializer will not be executed, as the case statement selected is the one corresponding to case label 3. The printf will print

x = 0, y = 5

(note: this happens on my machine, as the variable x is not initialized, Undefined Behaviour is expected)

In C, some evolution has been realized over the years, concerning the use of declarations in a block.

In ancient C, variables can be declared only at the beginning of a block (the piece of code between { and }, but this approach has been thrown for the new possibility of declaring a variable whenever you need it (even after some executable sentences after a block begin) But a case statement permits only to put an executable statement, and not a declaration, so that's the reason of your compiler error.

If you follow the ancient C way, you can only declare new local variables only after the opening { curly brace after the switch, as in:

switch(something) {
    int var1;
case BLA_BLA:
    /* use var1 here */

which, although counterintuitive, is valid since the old K&R code. The problem with this approach is that the variable is valid from the point of definition until the end of the switch statement, and so, it is global to all the case parts.

Another way, is the form you propose, in which you declare a new block by opening curly braces. This works also since the old K&R code, and makes it easier to control the scope of the variables defined. Personally, I prefer this second approach. A block is an executable statement, so there's no problem in using it as the labeled case statement of the switch (the declarations happen inside it).

Case labels don't delimit blocks of code, they label executable statements, so their syntax is specific to the case statement syntax (which finishes after the semicolon of the statement it is attached to, or the closing curly br)

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31