I'm writing a C compiler, and when I come to the implementation of the switch
statement one constraint confused me a lot. Section 6.8.4.2p2 of the standard states:
If a switch statement has an associated case or default label within
the scope of an identifier with a variably modified type, the entire
switch statement shall be within the scope of that identifier.
With a footnote:
That is, the declaration either precedes the switch statement, or it
follows the last case or default label associated with the switch that
is in the block containing the declaration.
I can't really understand what this constraint means. Can some one give me an example?
What this is saying is that if one case is able to see a variably modified array, then the entire switch
statement MUST be able to see it as well.
This means that the following code is legal:
void switch_test(int size)
{
int array[size];
...
// code to populate array
...
switch (expr) {
case 1:
printf("%d\n", array[0]);
break;
case 2:
// do something else
}
}
But this code is not:
void switch_test(int size)
{
switch (expr) {
case 2:
// do something else
int array[size]; // ILLEGAL, VLA in scope of one label but not all
case 1:
...
// code to populate array
...
printf("%d\n", array[0]);
}
}
The reason the latter is illegal is because if the code were to jump to case 1 then array
might not have been created properly since the size of a VLA is determined at run time. Ensuring that the VLA is visible before the switch
statement avoids this issue.
I think that this quote from the C Standard relative to the goto
statement will help to understand the quote relative to the switch statement.
6.8.6.1 The goto statement
1 The identifier in a goto statement shall name a label located
somewhere in the enclosing function. A goto statement shall not jump
from outside the scope of an identifier having a variably modified
type to inside the scope of that identifier.
In fact the swutch statement uses goto statements to pass the control to the selected label. So any such passing the control to a case label shall not skip a declaration of an object of a variable modified type. That is such a declaration either should be placed before a swict statement or inside the switch statement after all its labels.
And there is an example
goto lab3; // invalid: going INTO scope of VLA.
{
double a[n];
a[j] = 4.4;
lab3:
a[j] = 3.3;
goto lab4; // valid: going WITHIN scope of VLA.
a[j] = 5.5;
lab4:
a[j] = 6.6;
}
goto lab4; // invalid: going INTO scope of VLA.
that is the statements goto lab3;
and goto lab4;
are bypassing the declaraion double a[n];
.
Here is an example of a valid switch statement according to the footnote.
#include <stdio.h>
int main(void)
{
int n = 2;
switch ( n )
{
case 0:
break;
case 1:
break;
default: ;
int a[n];
for ( int i = 0; i < n; i++ ) a[i] = i;
int sum = 0;
for ( int i = 0; i < n; i++ ) sum += a[i];
printf( "sum = %d\n", sum );
}
return 0;
}
The program output is
sum = 1
If a switch statement has an associated case or default label within the scope of an identifier with a variably modified type, the entire switch statement shall be within the scope of that identifier.
void func(int a) {
switch(a) {
{ // this is the scope of variable b
// b is an identifier with variably modified type
int b[a];
// the following is the "associated case within a scope of an identifier"
case 1: // this is invalid
break;
}
// the scope of switch statement has to be within the scope of that identifier
}
}
It is so because compiler may need to emit cleanup instructions after allocating memory for VLA variable. In switch you can jump anywhere in the code, so instructions for allocating or cleaning the memory for a variable length array could be omitted.