Объяснение ограничений оператора switch на переменно измененные типы в стандарте C

Я пишу компилятор C, и когда я пришел к реализации оператора switch, одно ограничение меня сильно смутило. Раздел 6.8.4.2p2 стандарта гласит:

Если у оператора switch есть связанный регистр или метка по умолчанию внутри область идентификатора с изменяемым типом, весь оператор switch должен находиться в пределах этого идентификатора.

Сноска:

То есть объявление либо предшествует оператору switch, либо оно следует за последним регистром или меткой по умолчанию, связанной с коммутатором, который находится в блоке, содержащем объявление.

Я не могу понять, что означает это ограничение. Кто-нибудь может дать мне пример?

Ответы

Ответ 1

Это говорит о том, что если один случай может видеть переменно измененный массив, то весь оператор 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 избежит этой проблемы.

Ответ 2

Я думаю, что эта цитата из Стандарта C относительно оператора goto поможет понять цитату относительно оператора switch.

6.8.6.1 Оператор goto

1 Идентификатор в операторе goto должен называть метку, расположенную где-то во включающей функции. Оператор goto не должен прыгать за пределами области действия идентификатора, имеющего переменно измененный введите внутри области этого идентификатора.

Фактически оператор 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

Ответ 3

  Если у оператора switch есть связанный регистр или метка по умолчанию в области видимости идентификатора с изменяемым типом, весь оператор switch должен находиться в пределах области действия этого идентификатора.

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 вы можете перейти в любое место кода, поэтому инструкции по выделению или очистке памяти для массива переменной длины могут быть опущены.