Obj-C中的奇怪切换错误

Alf*_*sol 17 iphone objective-c switch-statement

我的代码中有这个switch语句:

switch(buttonIndex){
      case 0:
         [actionSheet dismissWithClickedButtonIndex:buttonIndex animated:YES];
         break;
    case 1:
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.delegate = self;
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:[imagePicker autorelease] animated:YES];
        break;
    default:
        [self openEmailViewInViewController:self];
}
Run Code Online (Sandbox Code Playgroud)

在案例1中的UIImagePickerController实例化中,我收到一个错误:

error:expected expression before 'UIImagePickerController'
Run Code Online (Sandbox Code Playgroud)

我不知道我做错了什么.思考?

哦,buttonIndex是一个NSInteger

joh*_*hne 64

我遇到了这个问题,有一天我决定深入研究它.

简短的非答案但实用的解决方案:

解决这个"问题"的方法是在语句;的冒号后面使用分号case ...:.例如,使用您提供的示例,它可以"修复",因此它可以按照您的直观预期进行编译和操作:

    case 1:; // <- Note semi-colon.
            UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
            imagePicker.delegate = self;
Run Code Online (Sandbox Code Playgroud)

答案很长:

一些历史:以前,C只允许你在一个块的开头声明"阻塞局部"变量,然后是各种语句.C99改变了一些东西,所以你可以自由地混合变量声明和语句.

在C99 BNF语法的上下文中,变量声明是a declaration,而声明是a statement.A statement表示多个事物,其中一个被称为a compound-statement,这是熟悉的{ ... }块.该...部分被宽松地定义为zero or more block-items,松散地定义block-item为.either a declaration or a statement

问题在于labeled-statement(定义标签,案例标签,或default:基本上是...:语句)的方式,这是松散定义的...: zero or more statements.人们可能直觉地认为,这不是.在s 之后使用权利基本上终止了a 的一部分.这导致语法回退到定义,这允许下一个"语句"是或者.zero or more statements or declarations;labeled-statement:zero or more statementslabeled-statementcompound-statementstatementdeclaration

我没有调查这是否是对C99语言规范的无意忽略(实际上,C99标准中的一个错误),或者这是否是对编写语言语法复杂性的务实让步.如果你不熟悉编写语法,你会注意到上面的解释允许递归:A labeled-statement可以匹配case 1: case 2: case 3:.在过于简单化的术语中(1),某些类型的语法递归是简单且"明确的",而其他类型则是复杂且"含糊不清"的.为简单起见,大多数语言工具仅处理必须通过仅查看"下一个标记"来确定性地解决任何歧义的情况.我之所以提到这一点,只是因为虽然这可能在C99规范中看起来像是一个缺陷,但可能存在令人信服的,非显而易见的原因,为什么存在......而且我没有费心去做任何有关该主题的进一步研究以找出答案无论哪种方式.

(1) 这并不是技术上准确的描述,而是对那些不熟悉所涉问题的人的合理近似.

编辑:

我给出的解决方案在"大多数"情况下工作(案例是"用法",而不是switch cases),但在一种情况下失败:当声明声明C99时这不起作用variable length array,例如case 1:; void *ptrs[count]; 这是因为在C99中它是一个错误"跳过"C99 VLA的声明,该声明位于跳跃发生的相同词汇范围内.在这些情况下,您需要使用case 1: { void *ptrs[count]; }.在这种情况下,ptrsVLA 的范围在结束时结束}.这比最初出现的更复杂,因为以下是完全合法的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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这会编译,并在运行时打印case 3.

另请参阅:维基百科:Duffs设备

  • <开始慢速拍手> (5认同)

Chu*_*uck 24

您不能在案例标签后面声明变量.

  • +1但是,如果使用{...}来包装案例标签的语句,则可以创建嵌套范围. (22认同)

Seb*_*lis 5

在这种情况下,我喜欢做的一件事是将每个内容case放在大括号内.这是编译器允许的:

switch (buttonIndex)
{
    case 0:
    {
        [actionSheet dismissWithClickedButtonIndex:buttonIndex animated:YES];
        break;
    }
    case 1:
    {
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.delegate = self;
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:[imagePicker autorelease] animated:YES];
        break;
    }
    default:
    {
        [self openEmailViewInViewController:self];
    }
}
Run Code Online (Sandbox Code Playgroud)