C标准中对可变修改类型的switch语句约束的解释

Explanation of switch statement constraints on variably modified types in C standard

我正在编写一个 C 编译器,当我开始执行 switch 语句时,一个约束让我很困惑。 standard 的第 6.8.4.2p2 节指出:

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.

带脚注:

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.

我不太明白这个约束是什么意思。有人可以给我举个例子吗?

这就是说,如果一个案例能够看到一个可变修改的数组,那么整个 switch 语句 必须 也能够看到它.

这意味着下面的代码是合法的:

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
    }
}

但是这段代码不是:

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]);
    }
}

后者非法的原因是,如果代码跳转到情况 1,则可能无法正确创建 array,因为 VLA 的大小是在 运行 时确定的。确保 VLA 在 switch 语句之前可见可避免此问题。

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
    }
}

之所以如此,是因为编译器可能需要在为 VLA 变量分配内存后发出清理指令。在 switch 中,您可以跳转到代码中的任何位置,因此可以省略为可变长度数组分配或清理内存的指令。

我认为 C 标准中与 goto 语句相关的引用将有助于理解与 switch 语句相关的引用。

6.8.6.1 goto语句

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.

实际上swutch语句使用goto语句将控制权传递给所选标签。因此,任何将控制传递给 case 标签的此类操作都不应跳过变量修饰类型对象的声明。这样的声明要么放在 swict 语句之前,要么放在 switch 语句中所有标签之后。

还有一个例子

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.

即语句 goto lab3;goto lab4; 绕过了声明 double a[n];

根据脚注,这是一个有效的 switch 语句示例。

#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;
}

程序输出为

sum = 1