Я всегда задавался вопросом: почему вы не можете объявлять переменные после метки регистра в операторе switch? В C ++ вы можете объявлять переменные практически везде (и объявление их близко к первому использованию, очевидно, хорошо), но следующее по-прежнему не будет работать:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Вышесказанное дает мне следующую ошибку (MSC):
инициализация 'newVal' пропускается по метке 'case'
Это кажется ограничением и в других языках. Почему это такая проблема?
c++
switch-statement
обкрадывать
источник
источник
Ответы:
Case
заявления являются только метками . Это означает, что компилятор интерпретирует это как переход непосредственно к метке. В C ++ проблема здесь одна из области видимости. Ваши фигурные скобки определяют объем как все внутриswitch
утверждения. Это означает, что вы остаетесь с областью, в которой будет выполняться переход к коду, пропускающему инициализацию.Правильный способ справиться с этим - определить область действия, специфичную для этого
case
оператора, и определить в нем переменную:источник
Этот вопрос
будетпервоначально был помечен как [C] и [C ++] в то же время. Исходный код действительно недействителен как на C, так и на C ++, но по совершенно другим не связанным причинам.В C ++ этот код недопустим, потому что
case ANOTHER_VAL:
метка попадает в область действия переменной,newVal
минуя ее инициализацию. Переходы, которые обходят инициализацию автоматических объектов, недопустимы в C ++. Эта сторона вопроса правильно решается большинством ответов.Однако в языке C обход инициализации переменной не является ошибкой. Прыжок в область действия переменной при ее инициализации допустим в C. Это просто означает, что переменная остается неинициализированной. Оригинальный код не компилируется в C по совершенно другой причине. Метка
case VAL:
в исходном коде прилагается к объявлению переменнойnewVal
. В языке C декларации не являются заявлениями. Они не могут быть помечены. И это то, что вызывает ошибку, когда этот код интерпретируется как C-код.Добавление дополнительного
{}
блока устраняет проблемы как C ++, так и C, даже если эти проблемы очень разные. На стороне C ++ это ограничивает область действияnewVal
, обеспечивая, чтобыcase ANOTHER_VAL:
больше не переходил в эту область, что устраняет проблему C ++. На стороне C, что дополнительно{}
вводит составной оператор, тем самым делаяcase VAL:
метку для применения к оператору, что устраняет проблему C.В случае C проблема может быть легко решена без
{}
. Просто добавьте пустое утверждение послеcase VAL:
метки, и код станет действительнымОбратите внимание, что, хотя теперь он действителен с точки зрения C, он остается недействительным с точки зрения C ++.
Симметрично, в случае C ++ проблема может быть легко решена без
{}
. Просто удалите инициализатор из объявления переменной, и код станет действительнымОбратите внимание, что даже если он теперь действителен с точки зрения C ++, он остается недействительным с точки зрения C.
источник
newVal
когда он переходит кANOTHER_VAL
?case ANOTHER_VAL:
точке переменнаяnewVal
видна, но с неопределенным значением.§A9.3: Compound Statement
из K & R C (второе издание). В записи упоминается техническое определение составного заявления, которое есть{declaration-list[opt] statement-list[opt]}
. Смущенный, потому что я думал, что заявление было заявлением, я посмотрел его и сразу нашел этот вопрос, пример, где указанное несоответствие становится очевидным и фактически нарушает программу. Я полагаю, что другое решение (для C) было бы поместить другое заявление (возможно, нулевое утверждение?) Перед объявлением так, чтобы маркированное утверждение было выполнено.Хорошо. Просто уточнить это строго не имеет ничего общего с декларацией. Это относится только к «перепрыгиванию через инициализацию» (ISO C ++ '03 6.7 / 3)
Во многих постах здесь упоминается, что перепрыгивание через объявление может привести к тому, что переменная «не будет объявлена». Это неправда. Объект POD может быть объявлен без инициализатора, но он будет иметь неопределенное значение. Например:
Если объект не POD или агрегат, компилятор неявно добавляет инициализатор, и поэтому невозможно перепрыгнуть через такое объявление:
Это ограничение не ограничивается оператором switch. Также является ошибкой использовать 'goto', чтобы перепрыгнуть через инициализацию:
Немного мелочей в том, что это разница между C ++ и C. В C не является ошибкой перепрыгивать через инициализацию.
Как уже упоминалось, решение состоит в том, чтобы добавить вложенный блок, чтобы время жизни переменной ограничивалось индивидуальной меткой регистра.
источник
Весь оператор switch находится в той же области. Чтобы обойти это, сделайте это:
Обратите внимание на скобки.
источник
После прочтения всех ответов и дополнительных исследований я получаю несколько вещей.
В С, согласно спецификации,
§6.8.1 Помеченные заявления:
В C нет ни одного предложения, которое допускает «помеченное объявление». Это просто не часть языка.
Так
Это не скомпилируется , см. Http://codepad.org/YiyLQTYw . GCC выдает ошибку:
Даже
это также не компилируется , см. http://codepad.org/BXnRD3bu . Здесь я также получаю ту же ошибку.
В C ++, согласно спецификации,
помечено-объявление разрешено, но помечено -инициализация не допускается.
Смотрите http://codepad.org/ZmQ0IyDG .
Решение такого условия два
Либо используйте новую область видимости, используя {}
Или используйте пустышку с меткой
Объявите переменную перед switch () и инициализируйте ее различными значениями в выражении case, если она удовлетворяет вашему требованию
Еще несколько вещей с оператором switch
Никогда не записывайте в коммутатор какие-либо операторы, которые не являются частью какой-либо метки, потому что они никогда не будут выполнены
Смотрите http://codepad.org/PA1quYX3 .
источник
a
в область видимости переменнойa
. Итак, с точки зрения C, проблема сcase VAL:
меткой, и вы правильно ее описали. Но с точки зрения C ++ проблема заключается вcase ANOTHER_VAL:
метке.Вы не можете сделать это, потому что
case
метки на самом деле являются просто точками входа в содержащий блок.Это наиболее ясно иллюстрируется устройством Даффа . Вот некоторый код из Википедии:
Обратите внимание, как
case
метки полностью игнорируют границы блоков. Да, это зло. Но именно поэтому ваш пример кода не работает. Переход кcase
метке аналогичен использованиюgoto
, поэтому вам не разрешено перепрыгивать через локальную переменную с помощью конструктора.Как указали несколько других постеров, вы должны поместить в свой блок:
источник
SAR
в x86, по сравнению сSHR
которым для беззнаковых смен).Большинство ответов пока неверны в одном отношении: вы можете объявлять переменные после оператора case, но не можете их инициализировать:
Как упоминалось ранее, хорошим способом решения этой проблемы является использование фигурных скобок для создания области для вашего случая.
источник
Мой любимый трюк со злым переключателем - использовать if (0) для пропуска нежелательной метки регистра.
Но очень злой.
источник
goto
без запутывания кодаПопробуй это:
источник
Вы можете объявить переменные в операторе switch, если вы начинаете новый блок:
Причина в том, чтобы выделить (и восстановить) пространство в стеке для хранения локальной переменной (переменных).
источник
Рассматривать:
В отсутствие операторов прерывания иногда newVal объявляется дважды, и вы не знаете, будет ли он до времени выполнения. Я думаю, что ограничение связано с такой путаницей. Каков будет объем newVal? Конвенция будет предписывать, что это будет весь блок переключателей (между скобками).
Я не программист C ++, но в C:
Работает отлично. Объявление переменной внутри блока переключателей - это нормально. Декларировать после дела охранника нет.
источник
Весь раздел коммутатора представляет собой единый контекст объявления. Вы не можете объявить переменную в таком случае. Попробуйте это вместо:
источник
Если в вашем коде написано «int newVal = 42», то разумно ожидать, что newVal никогда не инициализируется. Но если вы перейдете к этому утверждению (что вы и делаете), то именно это и произойдет - newVal находится в области видимости, но не был назначен.
Если это именно то, что вы действительно хотели сделать, тогда язык должен сделать это явным, сказав "int newVal; newVal = 42;". В противном случае вы можете ограничить область действия newVal одним случаем, что более вероятно, чем вы хотели.
Это может прояснить ситуацию, если вы рассмотрите тот же пример, но с "const int newVal = 42;"
источник
Я просто хотел бы подчеркнуть тонкий «s точку . Конструкция переключателя создает целую область первоклассного гражданина. Таким образом, можно объявить (и инициализировать) переменную в операторе switch перед меткой первого регистра, без дополнительной пары скобок:
источник
int newVal
будет выполнена, но не= 42
присвоение.До сих пор ответы были для C ++.
Для C ++ вы не можете перепрыгнуть через инициализацию. Вы можете сделать это в C. Однако в C декларация не является оператором, и после меток должны следовать операторы.
Итак, допустимый (но некрасивый) C, неверный C ++
И наоборот, в C ++ объявление является оператором, поэтому следующее допустимо C ++, недопустимо C
источник
Интересно, что это нормально
... но это не так:
Я понял, что исправить это достаточно просто, но я пока не понимаю, почему первый пример не беспокоит компилятор. Как было упомянуто ранее (на 2 года раньше хе-хе), декларация не является причиной ошибки, даже несмотря на логику. Инициализация - это проблема. Если переменная инициализирована и объявлена в разных строках, она компилируется.
источник
Я написал этот ответ изначально для этого вопроса . Однако когда я закончил, я обнаружил, что ответ закрыт. Так что я разместил это здесь, может быть, кто-то, кто любит ссылки на стандартные, найдет это полезным.
Оригинальный код в вопросе:
На самом деле есть 2 вопроса:
1. Почему я могу объявить переменную после
case
метки?Это потому, что в C ++ метка должна быть в форме:
N3337 6.1 / 1
И в
C++
заявлении заявление также рассматривается как утверждение (в отличие отC
):N3337 6/1:
2. Почему я могу перепрыгнуть через объявление переменной и затем использовать его?
Потому что: N3337 6,7 / 3
Поскольку он
k
имеет скалярный тип и не инициализируется в точке объявления, перепрыгивание через него возможно. Это семантически эквивалентно:Однако это было бы невозможно, если бы оно
x
было инициализировано в точке объявления:источник
Новые переменные могут быть декалифицированы только в области видимости блока. Вам нужно написать что-то вроде этого:
Конечно, newVal имеет только область в фигурных скобках ...
Ура, Ральф
источник
switch
Блок не является такой же , как последовательностьif/else if
блоков.Я удивлен, что никакой другой ответ не объясняет это ясно.Рассмотрим это
switch
утверждение:Это может быть удивительно, но компилятор не увидит в этом ничего простого
if/else if
. Он выдаст следующий код:Операторы
case
преобразуются в метки, а затем вызываются с помощьюgoto
. Скобки создают новую область видимости, и теперь легко понять, почему вы не можете объявить две переменные с одинаковым именем в пределахswitch
блоке.Это может выглядеть странно, но необходимо поддерживать падение (то есть не использовать,
break
чтобы выполнение продолжалось до следующегоcase
).источник
Я считаю, что проблема заключается в том, что это заявление было пропущено, и вы пытались использовать var в другом месте, оно не будет объявлено.
источник
newVal существует во всей области действия коммутатора, но инициализируется только в случае удара по лимбу VAL. Если вы создаете блок вокруг кода в VAL, все должно быть в порядке.
источник
Стандарт C ++ имеет: Возможна передача в блок, но не таким образом, чтобы обойти объявления с инициализацией. Программа, которая переходит от точки, в которой локальная переменная с автоматическим хранением находится вне области действия, до точки, в которой она находится в области действия, имеет неправильную форму, если переменная не имеет тип POD (3.9) и не объявлена без инициализатора (8.5).
Код для иллюстрации этого правила:
Код для отображения эффекта инициализатора:
источник
Похоже, что анонимные объекты могут быть объявлены или созданы в операторе switch switch по той причине, что на них нельзя ссылаться и как таковые они не могут перейти к следующему случаю. Рассмотрим этот пример, который компилируется в GCC 4.5.3 и Visual Studio 2008 (может быть проблема с соответствием, так что эксперты, пожалуйста, оцените)
источник
const
ссылкой с собственной областью действия). Это выражение, которое живет и умирает в своем утверждении (где бы оно ни было). Поэтому это совершенно не имеет значения.Foo();
не является декларацией; вопрос о декларациях.