Почему переменные не могут быть объявлены в операторе switch? - PullRequest
873 голосов
/ 18 сентября 2008

Меня всегда удивляло это - почему вы не можете объявить переменные после метки регистра в операторе switch? В C ++ вы можете объявлять переменные практически везде (и объявление их близко к первому использованию, очевидно, хорошо), но следующее все равно не будет работать:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

Вышеприведенное дает мне следующую ошибку (MSC):

инициализация 'newVal' пропускается меткой 'case'

Это кажется ограничением и в других языках. Почему это такая проблема?

Ответы [ 23 ]

6 голосов
/ 18 сентября 2008

Рассмотрим:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

В отсутствие операторов прерывания иногда newVal объявляется дважды, и вы не знаете, так ли это до выполнения Я думаю, что ограничение связано с такой путаницей. Каков будет объем newVal? Конвенция будет предписывать, что это будет весь блок переключателей (между фигурными скобками).

Я не программист на C ++, но на C:

switch(val) {
    int x;
    case VAL:
        x=1;
}

Работает нормально. Объявление переменной внутри блока переключателей - это нормально. Декларации после дела охранника нет.

4 голосов
/ 18 сентября 2008

Весь раздел коммутатора является одним контекстом объявления. Вы не можете объявить переменную в таком случае. Попробуйте вместо этого:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}
3 голосов
/ 22 мая 2016

Я написал этот ответ первоначально для этого вопроса . Однако когда я закончил, я обнаружил, что ответ закрыт. Поэтому я разместил это здесь, может быть, кому-то, кому нравятся ссылки на стандартные, это будет полезно.

Оригинальный код вопроса:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

На самом деле есть 2 вопроса:

1. Почему я могу объявить переменную после case label?

Это потому, что в C ++ метка должна быть в форме:

N3337 6,1 / 1

помечены-заявление:

...

  • атрибут-спецификатор-seqopt case constant-expression: statement

...

И в C++ оператор декларации также рассматривается как оператор (в отличие от C):

N3337 6/1:

оператор

...

декларация-заявление

...

2. Почему я могу перепрыгнуть через объявление переменной и затем использовать его?

Потому что: N3337 6,7 / 3

Возможно передать в блок, , но не так, чтобы обойти объявления с инициализацией . программа, которая прыгает (Перевод из условия оператора switch в метку регистра в этом отношении считается скачком .)

от точки, в которой переменная с автоматическим хранением находится вне области действия, до точки, где она находится в области действия, имеет неправильную форму , если переменная не имеет скалярного типа , типа класса с тривиальным значением по умолчанию конструктор и тривиальный деструктор, cv-квалифицированная версия одного из этих типов или массив одного из предыдущие типы и объявляется без инициализатора (8.5).

Поскольку k имеет скалярный тип и не инициализируется в точке объявления, перепрыгивание через его объявление возможно. Это семантически эквивалентно:

goto label;

int x;

label:
cout << x << endl;

Однако это было бы невозможно, если бы x был инициализирован в точке объявления:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;
3 голосов
/ 07 сентября 2010

Интересно, что это нормально:

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

... но это не так:

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

Я понял, что исправить это достаточно просто, но я пока не понимаю, почему первый пример не беспокоит компилятор. Как упоминалось ранее (на 2 года раньше хе-хе), объявление не является причиной ошибки, даже несмотря на логику. Инициализация - это проблема. Если переменная инициализирована и объявлена ​​в разных строках, она компилируется.

3 голосов
/ 02 сентября 2009

Пока что ответы были на C ++.

Для C ++ вы не можете перепрыгнуть через инициализацию. Вы можете сделать это в C. Однако в C декларация не является оператором, и после меток case должны следовать операторы.

Итак, допустимый (но ужасный) C, недействительный C ++

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

И наоборот, в C ++ объявление является оператором, поэтому следующее допустимо C ++, недопустимо C

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}
3 голосов
/ 17 октября 2008

Я просто хотел подчеркнуть стройное х балл . Конструкция переключателя создает целую область первоклассного гражданина. Таким образом, можно объявить (и инициализировать) переменную в операторе switch перед меткой первого регистра, без дополнительной пары скобок:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}
3 голосов
/ 18 сентября 2008

Если в вашем коде написано "int newVal = 42", то разумно ожидать, что newVal никогда не инициализируется. Но если вы перейдете к этому утверждению (что вы и делаете), то именно это и произойдет - newVal находится в области действия, но ему не назначено.

Если это именно то, что вы действительно имели в виду, тогда язык должен сделать это явным, сказав "int newVal; newVal = 42;" В противном случае вы можете ограничить область действия newVal одним случаем, что более вероятно, чем вы хотели.

Это может прояснить ситуацию, если вы рассмотрите тот же пример, но с "const int newVal = 42;"

1 голос
/ 01 января 2015

A switch block - это не то же самое, что последовательность if/else if блоков. Я удивлен, что никакой другой ответ не объясняет это ясно.

Рассмотрим это switch утверждение:

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

Это может быть удивительно, но компилятор не увидит это как простой if/else if. Он выдаст следующий код:

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

Операторы case преобразуются в метки и затем вызываются с помощью goto. Скобки создают новую область видимости, и теперь легко понять, почему нельзя объявить две переменные с одинаковым именем в блоке switch.

Это может выглядеть странно, но необходимо поддерживать падение (то есть не использовать break, чтобы позволить выполнению продолжить до следующего case).

1 голос
/ 18 сентября 2008

Новые переменные могут быть декалифицированы только в области видимости блока. Вам нужно написать что-то вроде этого:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

Конечно, newVal имеет только область видимости в скобках ...

Ура, Ральф

0 голосов
/ 18 сентября 2008

Я считаю, что проблема в том, что это заявление было пропущено, и вы пытались использовать var в другом месте, оно не будет объявлено.

...