在 switch case 语句中初始化数组

Initializing arrays in switch case statements

异想天开,我在 switch 语句中初始化了以下数组 c 完全期望我的编译器说 不,你不能那样做 但令我惊讶的是在 MSVC、GCC 和 Clang 中编译。在线example

我假设标准允许,在这种情况下我的问题是为什么? ...考虑到在 case 语句中不允许声明和初始化非数组。

int main()
{
    char ch;

    switch( ch )
    {
    case 'x':
        //int a = 42; // NOT OKAY
        break;

    case 'y':
        int b;
        b = 42;    // OKAY

    case 'z':
        int c[2] = { 0 , 1 };  // OKAY (Huh???)
        break;
    };
}

你的阵列在原处完全没问题。您在 switch(ch) 的词法范围内声明(并初始化)它,允许它存在、被初始化并被其他操作引用(在声明之后)。

switch 之外引用它是行不通的,例如:

switch(ch) {
  ...
  case 'z':
    int c[2] = { 0 , 1 };  // OKAY (Huh???)
    break;
};

some_operation(c);

唯一会被禁止的情况是,如果您有 另一个 case 语句使用相同的变量。由于 case 语句只是标签(正如 here 所指出的),您可以跳过稍后使用的变量的初始化。

因此,只要您仅在一个标签中使用该变量,而不是在多个标签中使用,编译器就会让您这样做。

如果跳转到 case 标签绕过初始化数组,编译器会发出错误。例如

    switch( ch )
    {
        int c[2] = { 0 , 1 };  // OKAY (Huh???)
    case 'x':
        //int a = 42; // NOT OKAY
        break;

    case 'y':
        int b;
        b = 42;    // OKAY

    case 'z':
//        int c[2] = { 0 , 1 };  // OKAY (Huh???)
        break;
    } 

    switch( ch )
    {
    case 'x':
        //int a = 42; // NOT OKAY
        break;

    case 'y':
        int b;
        b = 42;    // OKAY

    case 'z':
        int c[2] = { 0 , 1 };  // OKAY (Huh???)
        break;
    default:
        break;
    } 

但是原程序中跳转都没有绕过初始化数组。

这是一个更简化的演示程序。 这将编译成功

int main() 
{
    goto L1;

    {
        L1:;
        int c[2] = { 0 , 1 };
    }       
}    

虽然这会报错

int main() 
{
    goto L1;

    {
        int c[2] = { 0 , 1 };
        L1:;
    }       
}    

如果您将示例更改为

int main()
{
    char ch;

    switch( ch )
    {
    case 'x':
        int c[2] = { 0 , 1 };
        break;

    case 'z':
        int a = 42;
        break;
    }
}

您会注意到数组现在出现了错误,但 int 没有。

最后一种情况实际上允许初始化。

规则不是"you're not allowed to initialise a variable in a case"而是"you're not allowed to jump across a variable initialisation."

并且在最后一种情况下不可能跳过初始化。

该规则的原因是在case中声明的变量在后续情况的范围内,并且跳转到后续情况将绕过初始化。

如果你重写为 goto-sequence,这会变得(稍微)更清楚,因为关于范围和初始化的相同规则适用:

if (ch == 'x') goto x;
if (ch == 'y') goto y;
if (ch == 'z') goto z;
goto end;
{
  x:
    int a = 42;  // goto y or z breaks this
    goto end;
  y:
    int b;      // uninitialised, so OK
    b = 42;
    goto end;
  z:
    int c[2] = {0, 1};  // No label after this, so can't jump across, so OK
    goto end;
}
end: