Error switch jumps into scope of identifier with variably modified type

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

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

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

Со сноской:

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

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

3 ответа

Лучший ответ

Это говорит о том, что если в одном случае можно увидеть переменно измененный массив, то весь switch оператор ДОЛЖЕН также его увидеть.

Это означает, что следующий код является допустимым:

void switch_test(int size)
{
    int array[size];
    ...
    // code to populate array
    ...
    switch (expr) {
    case 1:
        printf("%dn", 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("%dn", array[0]);
    }
}

Причина, по которой последнее является недопустимым, заключается в том, что если бы код перешел к случаю 1, то array, возможно, не был бы создан должным образом, поскольку размер VLA определяется во время выполнения. Обеспечение видимости VLA до оператора switch позволяет избежать этой проблемы.


3

dbush
6 Сен 2019 в 17:09

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


2

KamilCuk
6 Сен 2019 в 12:55

Я думаю, что эта цитата из стандарта 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 = %dn", sum );
    }

    return 0;
}

Вывод программы

sum = 1


4

Vlad from Moscow
6 Сен 2019 в 13:49

  • Печать

Страницы: [1]   Вниз

Тема: Непонятка с массивом (С)  (Прочитано 1234 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн
[DarkNet]Alpha

Здравствуйте.
Решил я на досуге спрограммировать олимпиадную задачу по информатике на С (на самой олимпиаде писал на паскале). Суть её такова: имеется решётка заданных размеров (задаём сами). Потом эту сетку заселяют бактериями (в каждую ячейку заданной строки или столбца добавляют по столько-то бактерий). После n заселений надо вывести кол-во бактерий в самой заселённой ячейке и её координаты.
Вот код.

//#include <iostream.h>
#include <stdio.h>

int main(void)
{
int x; //размер решётки по x
int y; //размер решётки по y
int w; w=0; //расчётная переменная
int w1; w1=0; //расчётная переменная 2
int maxv; maxv=0; //служит для записи макс. значения решётки
int maxvx; maxvx=0; //служит для записи x-координаты макс. значения
int maxvy; maxvy=0; //служит для записи y-координаты макс. значения
scanf("%d", &x, "%d", &y);
if (((x>=1) && (x<=10000)) && ((y>=1) && (y<=10000)))
{
int grid [ x ] [y]; //создание решётки (здесь поставил пробелы для форума, а то он квадратик рисовал)
}
else goto a; //останов, если нарушает допустимые границы
int qop; //кол-во операций заселения
scanf("%dn", &qop);
if ((qop<0) || (qop>100000))
{
goto a; //останов, если нарушает допустимые границы
}
int qb; //кол-во заселённых бактерий
int cell; //строка/столбец, в которую будут заселены бактерии
int ori; //направление заселения: 1=строка, 2=столбец
w=0; w1=0;
while (w!=qop)
{
scanf("%d", &ori, "%d", &cell, "%d", &qb, "n");
if ((ori<1) || (ori>2) || (qb<0) || (qb>10000))
{
goto a; //останов, если нарушает допустимые границы
}

if (ori==1)
{
while (w1!=x)
{
grid[cell][w1]=grid[cell][w1]+qb;
w1=w1+1;
}
}
if (ori==2)
{
while (w1!=y)
{
&grid[w1][cell]=&grid[w1][cell]+&qb;
w1=w1+1;
}
}
}
w=0; w1=0;
while (w!=x)
{
while (w1!=y)
{
if (grid[w][w1] > maxv)
{
maxv=grid[w][w1];
maxvx=w;
maxvy=w1;
}
}
}
printf(&maxv, " ", &maxvx, " ", &maxvy, " ");
a:
getch();
return 0;
}

И вот вывод.

ЧЯДНТ?

« Последнее редактирование: 23 Декабря 2009, 14:49:33 от DarknetAlpha »


Оффлайн
alexander.pronin

Для начала.
У Вас grid описан внутри if.


Оффлайн
[DarkNet]Alpha

Это плохо?
Попробую вывернуть…


Пользователь решил продолжить мысль 23 Декабря 2009, 13:20:28:


Поправил соответствующий кусок.

if (((x<1) || (x>10000)) || ((y<1) || (y>10000)))
{
goto a; //останов, если нарушает допустимые границы
}
int grid[x][y]; //создание решётки
Старые ашыпки исчезли. Появилась незамеченная.

main.c:16: error: jump into scope of identifier with variably modified type
Кстати, подскажите, пожалуйста, команду неизврашённой остановки программы.
Моей фантазии хватило на halt, stop, exit. Ни одна не работает.

« Последнее редактирование: 23 Декабря 2009, 15:22:09 от DarknetAlpha »


Оффлайн
alexander.pronin

Это не басик, goto забудьте (это для Вас источник ошибок).
Остановкой будет окончание (выполнение программы). Есть еще варианты, exit, return,break, но Вам достаточно {} правильно использовать.
Стиль пока ужасный, т.е. у Вас большие перспективы. :coolsmiley:
Изучите.
if() {

}
else {

}
и другие варианты.

« Последнее редактирование: 23 Декабря 2009, 15:42:31 от alexander.pronin »


Оффлайн
wl

int x; //размер решётки по x
int y; //размер решётки по y
int grid[x][y];
   
Это принципиально неправильно.
Welcome to hell, hehe!  :knuppel2: Это вам не паскаль и не додиез (C#).

Размер массива должен задаваться константами и не может задаваться переменными.

Если Вам нужен массив переменного размера, объявляете указатель, потом выделяете память, а в конце освобождаете.
А двумерный массив — это массив указателей.

Как-то так:

int **grid;
int i,x,y;

grid=(int **)malloc(x*sizeof(int *));
for(i=0;i<x;i++){
  grid[i]=(int *)malloc(y*sizeof(int)); //к указателю можно обращаться как к массиву
}

// работаем

//в конце
for(i=0;i<x;i++){
  free(grid[i]);
}
free(grid);


Далее, стоит выяснить, что такое область действия переменных.

if (((x>=1) && (x<=10000)) && ((y>=1) && (y<=10000)))
{
int grid [ x ] [y]; //создание решётки (здесь поставил пробелы для форума, а то он квадратик рисовал)
}
Здесь переменная grid появляется внутри оператора if, а при выходе из него исчезает, и больше не видна.

А неизвращенная остановка программы — оператором return из функции main(), либо функцией exit(), но чтобы последняя заработала, надо подключить заголовок stdlib.h
Что же касается оператора break, упомянутого предыдущим оратором, то он прерывает выполнение цикла или оператора switch, и к завершению программы не приводит. Если компилятор увидит его вне цикла или свитча, выдаст ошибку.

« Последнее редактирование: 23 Декабря 2009, 18:16:24 от wl »

На свете феньки есть такие, брат Горацио, которых лохи просто не секут. (Шекспир, «Гамлет», вольный перевод)


Оффлайн
Упс

Дочитал до scanf(«%d», &x, «%d», &y);
Дальше не стал.
int scanf(const char *__format, …..)


Оффлайн
wl

Ничего-ничего, все в порядке! :)

«Если компилятор не обнаружил ошибок в вашей первой программе, обратитесь к разработчикам, пусть исправляют ошибки в компиляторе»

На свете феньки есть такие, брат Горацио, которых лохи просто не секут. (Шекспир, «Гамлет», вольный перевод)


Оффлайн
Yurror

Мой совет: купите книжку по Си и начните с простенького.
Только вот не надо брать в библиотеке книжки 80х годов. и там где есть слово Windows иначе перекорежите и без того кривые знания.
А программулину в Юмор рекомендую определить.

И ты еще не добрался до самого сложного вопроса «на миллион»: где взять getch()?

« Последнее редактирование: 26 Декабря 2009, 08:23:41 от Yurror »


Оффлайн
wl

А как же классика — Керниган и Ричи «Язык программирования Си»?

На свете феньки есть такие, брат Горацио, которых лохи просто не секут. (Шекспир, «Гамлет», вольный перевод)


Оффлайн
Yurror

А как же классика — Керниган и Ричи «Язык программирования Си»?

Уже вышел новый стандарт и мне не очень нравится их стиль оформления исходников ;)


  • Печать

Страницы: [1]   Вверх

Bug 21113
Jumps into VLA or VM scope not rejected for C++

Summary:

Jumps into VLA or VM scope not rejected for C++

Status: RESOLVED
FIXED

Alias:

None

Product:

gcc

Classification:

Unclassified

Component:

c++

(show other bugs)

Version:

4.1.0

Importance:

P2
normal

Target Milestone:

Assignee:

Not yet assigned to anyone

URL:


Keywords:

accepts-invalid

Depends on:


Blocks:



C++VLA
  Show dependency tree

/ graph

Reported: 2005-04-19 21:20 UTC by Joseph S. Myers
Modified: 2016-03-07 17:28 UTC
(History)

CC List:

4
users

(show)

See Also:

Host:

Target:

Build:

Known to work:

4.9.3, 5.3.0, 6.0

Known to fail:

4.1.2, 4.5.3

Last reconfirmed:

2016-03-07 00:00:00


Attachments
Add an attachment
(proposed patch, testcase, etc.)

Note
You need to
log in
before you can comment on or make changes to this bug.


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 = %dn", sum );
    }

    return 0;
}

The program output is

sum = 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("%dn", 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("%dn", 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.

Понравилась статья? Поделить с друзьями:
  • Error swap was not declared in this scope
  • Error swap papyrus
  • Error svg image
  • Error suppressible vsim 12110
  • Error supported eeprom not found