Explanation of switch statement constraints on var

2020-04-02 06:40发布

问题:

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?

回答1:

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.



回答2:

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


回答3:

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.