Я столкнулся с этой проблемой, и однажды я решил в нее залезть.
Короткий без ответа, но прагматичное решение:
Способ обойти эту «проблему» заключается в использовании запятой, ;
, сразу после ободочной кишки в case ...:
заявлении. Например, используя пример вы предоставили, это может быть «фиксированной», поэтому он собирает и ведет себя, как вы бы интуитивно ожидать, что это:
case 1:; // <- Note semi-colon.
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
Длинный ответ:
Немного истории: Раньше только C позволил вам объявить «блокировать локальные» переменные в начале блока, за которым последовали различные операторы. C99 изменил ситуацию, чтобы вы могли свободно смешивать объявления переменных и утверждения.
В контексте грамматики C99 BNF объявление переменной является declaration
, а инструкция - statement
. A statement
означает несколько вещей, один из которых известен как compound-statement
, который является знакомым блоком ....
часть свободно определяется как zero or more
block-items
, с block-item
определяется, как свободно either a
declaration
or a
statement
.
Проблема заключается в том, как labeled-statement
(метка Гото, случай, этикетки или default:
, по существу те ...:
высказывания) определяется, которая свободно определяется ...: zero or more
statements
. Это не так, как можно было бы ожидать, интуитивно, zero or more
statements
or
declarations
. Использование ;
сразу после labeled-statement
с :
по существу заканчивает zero or more
statements
часть labeled-statement
. Это приводит к тому, что грамматика возвращается к определению compound-statement
, которое позволяет следующему «заявлению» быть либо statement
, либо declaration
.
Я не исследовал, является ли это непреднамеренным просмотром спецификации языка C99 (на практике, ошибка в стандарте C99), или если это прагматическая уступка сложностям написания языковых грамматик , Если вы не знакомы с написанием грамматик, вы заметите, что приведенное выше объяснение допускает рекурсию: A labeled-statement
может соответствовать case 1: case 2: case 3:
. В чрезмерно упрощенных терминах (1)
, некоторые типы рекурсии грамматики просты и «однозначны», а другие сложны и «неоднозначны». Для простоты большинство языковых инструментов будут обрабатывать только случай, когда любая двусмысленность должна быть детерминистически решена, рассматривая не что иное, как «следующий токен». Я упоминаю об этом только потому, что, хотя это может показаться интуитивно недостаточным в спецификации C99, могут быть убедительные, неочевидные причины, почему это существует ... и я не потрудился провести дальнейшие исследования по этому вопросу, чтобы узнать так или иначе.
(1)
Это не должно быть технически точным описанием, но разумным приближением для тех, кто не знаком с затронутыми проблемами.
EDIT:
Решение, которое я дал работы в «большинстве» случаев (дел, «обычаи», не switch
case
с), но это не обязательно в одном случае: Это не будет работать, если Объявление объявляет C99 variable length array
, например case 1:; void *ptrs[count];
Это связано с тем, что в C99 ошибка «проскользнуть» после объявления VLA C99, которая находится в той же лексической области, где произошел скачок. В этих случаях вам необходимо использовать case 1: { void *ptrs[count]; }
. В этом случае объем VLA ptrs
заканчивается на закрытии }
. Это гораздо сложнее, чем кажется на первый взгляд, так как следующий совершенно законна код C, хотя на первый взгляд один интуитивно кажется, что это не так:
switch(3){
case 0:
printf("case 0\n");
break;
case 1:;
int *ip = NULL;
printf("case 1\n");
break;
case 2:
{
int ia[6];
printf("case 2\n");
break;
case 3:
printf("case 3\n");
break;
default:
printf("default\n");
}
}
компилируется, и при запуске, отпечатки case 3
.
Смотрите также: Wikipedia: Duffs Device
+1 Тем не менее, вы можете создать вложенную область с помощью {...}, чтобы обернуть инструкции для ярлыка case. –