在switch语句内声明变量

120

我看到了一些关于这个问题的答案,我理解了——你不能在switch内部声明和赋值变量。但是我想知道下面的代码是否正确地抛出了一个错误:

error: expected expression before 'int'

代码:

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

为什么在它之前加入NSLog()调用会导致没有错误?

switch (i) {
    case 0:
        NSLog(@"wtf");
        int j = 1;
        break;
}

1
这个问题之前已经被问过(至少)两次:https://dev59.com/3HVD5IYBdhLWcg3wGHeu https://dev59.com/H3NA5IYBdhLWcg3wC5NR - Adam Rosenfield
2
是的,但这两个问题仍然没有解决。我认为让它保持不变没有问题——没有必要投票关闭它... - Quinn Taylor
1
Adam,虽然其他帖子回答了错误,但它并没有解决将表达式作为第一个调用时实际上会避免错误的事实。Quinn在这里回答了这个问题。 - dizy
3个回答

148
你实际上是可以在 switch 中声明变量的,只需要按照语言的语法进行操作。你之前得到错误是因为“case 0:”是一个标签,在 C 语言中,标签后第一条语句不能是声明语句——编译器需要的是一个表达式,如方法调用、普通赋值等等。(尽管这看起来很奇怪,但这就是规定。)
当你把 NSLog() 放在第一位时,你避免了这种限制。你可以将 case 的内容放在 { } 大括号中引入作用域,或者将变量声明移出 switch。选择哪种方式取决于个人喜好。只需注意,大括号中声明的变量仅在该作用域内有效,因此使用它的任何其他代码也必须出现在这些大括号中。

编辑:

顺便说一下,这种怪癖并不像你想象的那么罕见。在C和Java中,在forwhiledo循环中使用局部变量声明作为唯一语句(意思是“不被大括号包围”),甚至在ifelse子句中也是非法的。(实际上,在 "Java Puzzlers" 的谜题55中有涉及到这个问题,我强烈推荐阅读。)我认为我们通常不会写出这样的错误,因为在这些情况下仅声明变量没有太多意义。然而,在switch/case结构中,一些人省略了大括号,因为break语句是控制流程的关键语句。

要看编译器发生错误,请将此可怕且无意义的片段复制到您的(Objective-)C代码中:

if (1)
    int i;
else
    int i;
for (int answer = 1; answer <= 42; answer ++)
    int i;
while (1)
    int i;
do
    int i;
while (1);

始终使用 { } 大括号来界定这些结构的主体,这是另一个原因。 :-)

9
一般来说,如果变量只在单个case内使用,则将case部分放在{ }中会更好(这样可以避免它被意外重用)。 如果变量稍后再次使用,则在switch语句开始之前声明变量真的更有意义,否则会变得非常晦涩。 - Peter N Lewis
1
哇,连C#也有这个问题。对于简单的代码 if (true) int i = 0;,我在编译前在Visual Studio中就收到以下错误信息:嵌入语句不能是一个声明或标记语句。太惊人了。 - mkmurray
3
不足为奇。很高兴C#的错误信息比gcc的更加清晰明了。实际上,我不确定我是否应该将其归类为“问题”……更像是一种故意禁止的语法。我猜想大多数基于C的编程语言都有类似的情况发生。 - Quinn Taylor
1
是的,我道歉。我理解为什么这种语法被禁止,因为声明一个立即失去作用域的变量是没有意义的。然而,我想再说一件事,C#允许在Objective-C中被禁止的switch语法。这个错误信息只会出现在其他语句(if、else、for、while和do)中。 - mkmurray
1
我决定写一篇博客,甚至调查了C#语言规范的语法:http://murrayon.net/2009/09/variable-declaration-restrictions.html - mkmurray
显示剩余3条评论

50

我之前也遇到过这个问题,结论是需要将代码放在一个块中。

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

4

我使用的另一个简单的解决方法是在声明前添加一个空表达式(分号)。这样可以避免将变量范围限制为代码块(或在一些情况下有带有代码块和一些没有)。

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

这在我的default情况下给了我一个错误:Switch case is in protected scope. - zakdances

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接