Syntax: Single statement in function declaration - c

In the C programming language, it is possible to omit a code block in the case of a single statement, for example:
if(1) exit();
Now, does this only apply to conditionals ?
Why is this not valid in the case of functions:
void f(int a) exit();

It's a feature of C syntax. In BNF, a function definition is something like
FUNC_DEF ::= TYPE IDENTIFIER "(" PARAM_LIST ")" BLOCK
while a statement is
STATEMENT ::= (EXPRESSION | DECLARATION | CONTROL | ) ";" | BLOCK
BLOCK ::= "{" STATEMENT* "}"
(simplifying to allow intermixed declarations and statements, which C++ allows but C doesn't), and an if statement is
CONDITIONAL ::= "if" "(" EXPRESSION ")" STATEMENT
omitting the else part for now.
The reason for this is that otherwise, you could write the function
void no_op() {}
as
void no_op();
but the latter syntax is already in use to denote a declaration.

The syntax of a conditional statement is this:
if(expression) statement
A compound statement is a statement.
A compound statement is defined as
{ zero or more statements }
The syntax of a function definition is this
function_declaration compound_statement
So, by definition a function body must be a compound statement and have {}
QED :)

There is a very old dialect of C, the K&R C. In this dialect the function declaration may look like this:
fun_a(a,b)
char a;
float b;
{
fun_b(b,a);
}
I think it would be too hard to parse it without { and }.

Related

Error declaring a variable in a switch statement [duplicate]

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;
*
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
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.)
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)

Are a block and a compound statement the same concept?

In the C11 standard
6.8 Statements and blocks
A block allows a set of declarations and statements to be grouped
into one syntactic unit.
6.8.2 Compound statement Syntax
compound-statement:
{ block-item-listopt }
block-item-list:
block-item
block-item-list block-item
block-item:
declaration
statement
Semantics
A compound statement is a block.
6.9.1 Function definitions
Syntax
function-definition:
declaration-specifiers declarator declaration-listopt compound-statement
So a compound statement is a block.
Is a block a compound statement?
In a function definition, does the part "compound-statement" mean the same as a block?
A compound-statement is a block, but it's not the only thing which is a block. An iteration statement is also a block, for example (6.8.5/5), even if the loop body consists of a simple statement.
The syntax of a function definition, unlike an iteration statement, requires braces. A compound-statement is a syntactic category which is surrounded by braces.
A block is a semantic category, used to specify the scope of names and the lifetime of automatic objects.

Why can we not declare a variable after a switch case colon without using curly braces?

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;
*
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
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.)
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)

lone declaration in if statement?

I did not understand why this works:
if(1)
{
int i;
}
and this not:
if(1)
int i;
error: expected expression before int
If you could supply some standard reference.
In C, declarations can only occur as part of a compound-statement, not as part of any of the other kinds of statement (see C11 6.8 and 6.8.2).
This is due to C grammar. Specifically, the if statement is defined as:
if ( expression ) statement
According to C11 6.8 Statements and blocks, the statement is one of:
labeled-statementcompound-statementexpression-statementselection-statementiteration-statementjump-statement
The declaration can only directly appear in either compound-statement (i.e. delimeted by { ... }) or as a special case as first expression in for iteration-statement.
Declaration must be a part of a block-item1.
The block-item-list contains a block-item2.
And the block-item-list can only be inside brackets, as part of a compound-statement3.
This is different in C++, as a declaration-statement is included in statement (the former allows, via a block-statement, defining variables).
(Quoted from ISO/IEC 9899:201x 6.8.2 Compound statement 1)
1
block-item:
declaration
2
block-item-list:
block-item
block-item-list block-item
3
compound-statement:
{ block-item-list opt }
As you can see from section 6.8.2p1 which covers the { ... }-style example, a declaration is permitted within a compound-statement.
Section 6.8.4p1, however, which covers the syntax for selection statements (i.e. if (...) ...) doesn't explicitly permit any declarations. In addition, this notation requires an expression, as hinted by the error message, "expected expression ..."
The first code example declares an int visible only inside an otherwise empty scope ... hard to see the utility! The second apparently attempts to conditionally? declare an int visible in the enclosing (function?) scope. Hard to imagine the utility of such a conditional declaration ... would it require run-time re-compilation?

Can't have label on mid-stream declaration in C?

So this in C99:
label:
int ret = function(of, stuff);
gives a compile-time error, whereas this:
label:
;
int ret = function(of, stuff);
works just fine.
Is this a compiler bug? Or is this a bug in the definition of the C standard? Or if this is part of the C99 standard, perhaps someone would rise to the defense of the C standard to claim that this makes perfect sense?
Labels, which are defined in N1256 6.8.1 Labeled statements, can only contain statements.
Syntax
1 labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
int ret = function(of, stuff); is an declaration, which is defined in N1256 6.7 Declarations and isn't a statement.
Statements are defined below in N1256 6.8 Statements and blocks:
Syntax
1 statement:
labeled-statement
compound-statement
expression-statement
selection-statement
iteration-statement
jump-statement
compound-statement is so-called blocks, which is 0 or more declarations and statements surrounded by {}.
expression-statement is zero or one expression defined in N1256 6.5 Expressions, followed by a semicolon like i++;. The expression in the syntax is defined in N1256 6.5.17 Comma operator.
selection-statement is if and switch statement.
iteration-statement is while, do-while and for statement.
jump-statement is goto, continue, break and return statement.
As you see, declarations are not a statement, so you cannot put labels to declarations.
Arguably a bug in the spec -- when it was changed to allow statements and declarations to be mixed in a block (rather than requiring all declarations before statements), it should also have been changed to allow labels on a declaration, but it was not. An artifact of how the language evolved over time.
Its not a major problem as you discovered, as you can work around it trivially by putting the label on an empty statement before the declaration.

Resources