协慌网

登录 贡献 社区

为什么不能在 switch 语句中声明变量?

我一直想知道这一点 - 为什么你不能在 switch 语句中的 case 标签之后声明变量?在 C ++ 中,您可以在任何地方声明变量(并且声明它们接近第一次使用显然是一件好事)但是以下仍然不起作用:

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

以上给出了以下错误(MSC):

'case' 标签跳过'newVal' 的初始化

这似乎也是其他语言的限制。为什么会出现这样的问题?

答案

Case陈述只是标签 。这意味着编译器会将其解释为直接跳转到标签。在 C ++ 中,这里的问题是范围之一。您的花括号将范围定义为switch语句中的所有内容。这意味着您将留下一个范围,在该范围内将跳过初始化的代码进一步执行跳转。处理此问题的正确方法是定义特定于该case语句的范围并在其中定义变量。

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

这个问题最初标记为 [C] 和 [C ++] 在同一时间。原始代码在 C 和 C ++ 中确实无效,但是出于完全不同的无关原因。我相信这个重要的细节被现有答案遗漏(或混淆)。

  • 在 C ++ 中,此代码无效,因为case ANOTHER_VAL: label 跳转到变量newVal的范围,绕过其初始化。在 C ++ 中,绕过本地对象初始化的跳转是非法的。大多数答案都正确地解决了这个问题的这一方面。

  • 但是,在 C 语言中绕过变量初始化不是错误。在 C 初始化中跳转到变量的范围是合法的。它只是意味着变量未初始化。由于完全不同的原因,原始代码无法在 C 中编译。标签case VAL:在原始代码中附加变量newVal的声明。在 C 语言中,声明不是语句。它们无法贴上标签。当这段代码被解释为 C 代码时,这就是导致错误的原因。

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }

添加额外的{}块可以修复 C ++ 和 C 问题,即使这些问题恰好存在很大差异。在 C ++ 方面,它限制了newVal的范围,确保case ANOTHER_VAL:不再跳转到该范围,这消除了 C ++ 问题。在 C 方面,额外的{}引入了复合语句,从而使case VAL:标签应用于语句,从而消除了 C 问题。

  • 在 C 情况下,问题可以在没有{}情况下轻松解决。只需在case VAL:标签后添加一个空语句,代码就会生效

    switch (val)  
    {  
    case VAL:;            /* Now it works in C! */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:  
      ...
      break;
    }

    请注意,即使它现在从 C 的角度来看是有效的,但从 C ++ 的角度来看它仍然是无效的。

  • 对称地,在 C ++ 案例中,问题可以在没有{}情况下轻松解决。只需从变量声明中删除初始化程序,代码就会生效

    switch (val)  
    {  
    case VAL: 
      int newVal;
      newVal = 42;  
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }

    请注意,即使它现在从 C ++ 的角度来看是有效的,但从 C 的角度来看它仍然是无效的。

好。只是为了澄清这一点,严格与宣言无关。它只涉及 “跳过初始化”(ISO C ++ '03 6.7 / 3)

这里的很多帖子都提到跳过声明可能会导致变量 “未被声明”。这不是真的。可以在没有初始化程序的情况下声明 POD 对象,但它将具有不确定的值。例如:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

如果对象是非 POD 或聚合,则编译器会隐式添加初始化程序,因此无法跳过此类声明:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

此限制不限于 switch 语句。跳过初始化时使用'goto' 也是错误的:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

有点琐事是,这是 C ++ 和 C 之间的区别。在 C 中,跳过初始化并不是错误。

正如其他人所提到的,解决方案是添加嵌套块,以便变量的生命周期仅限于单个案例标签。