C++ 为什么可以';t变量是否可以在switch语句中声明?

C++ 为什么可以';t变量是否可以在switch语句中声明?,c++,switch-statement,C++,Switch Statement,我一直在想,为什么不能在switch语句中的case标签之后声明变量呢?在C++中,你可以在任何地方声明变量(并且声明它们接近第一次使用显然是一件好事),但下面的方法仍然无效: switch (val) { case VAL: // This won't work int newVal = 42; break; case ANOTHER_VAL: ... break; } 上面给出了以下错误(MSC): “case”标签跳过了“newVal”的初始化

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

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  
上面给出了以下错误(MSC):

“case”标签跳过了“newVal”的初始化


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

整个switch语句都在同一范围内。要绕过它,请执行以下操作:

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

    case ANOTHER_VAL:
      ...
    break;
}
注意括号。

试试以下方法:

switch (val)
{
    case VAL:
    {
        int newVal = 42;
    }
    break;
}

我认为当前的问题是该语句被跳过,并且您试图在其他地方使用var,它不会被声明。

如果您启动一个新块,您可以在switch语句中声明变量:

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

原因在于在堆栈上分配(和回收)空间以存储局部变量。

您不能这样做,因为
大小写
标签实际上只是包含块的入口点

这一点可以用下面的例子最清楚地说明。以下是来自维基百科的一些代码:

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}
请注意,
大小写
标签如何完全忽略块边界。是的,这是邪恶的。但这就是代码示例不起作用的原因。跳转到
case
标签与使用
goto
相同,因此不允许使用构造函数跳转到局部变量

正如其他几张海报所示,您需要在自己的街区中放置:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }

newVal存在于开关的整个范围内,但仅当VAL肢体被击中时才初始化。如果您在VAL中的代码周围创建一个块,则应该可以。

开关的整个部分都是一个声明上下文。不能在这样的case语句中声明变量。请尝试以下方法:

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

Case
语句只是标签。这意味着编译器会将其解释为直接跳转到标签。在C++中,问题是范围之一。您的花括号将范围定义为
switch
语句中的所有内容。这意味着您将有一个跳转范围,跳转将进一步执行到跳过初始化的代码中

正确的处理方法是定义特定于该
case
语句的范围,并在其中定义变量:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}
如果您的代码显示“int newVal=42”,那么您可以合理地预期newVal永远不会未初始化。但是,如果您对这个语句进行了检查(这就是您正在做的),那么这正是发生的情况-newVal在范围内,但尚未分配

如果这是您真正想要实现的,那么该语言需要通过说“int-newVal;newVal=42;”来明确它。否则,您可以将newVal的范围限制为单个案例,这更可能是您想要的

如果你考虑同一个例子,它可以澄清事情,但是用“const int NevVal= 42;”

考虑:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}
在没有break语句的情况下,有时newVal会声明两次,直到运行时才知道它是否声明。我的猜测是,限制是因为这种混乱。newVal的范围是什么?按照惯例,它将是整个开关块(在支架之间)

<>我不是C++程序员,但在C:

switch(val) {
    int x;
    case VAL:
        x=1;
}

很好。在开关块内声明变量是可以的。不允许在案例保护之后进行声明。

新变量只能在块范围内进行标记。你需要这样写:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;
当然,newVal只在大括号内有作用域


干杯,拉尔夫好的。只是要澄清这一点与声明无关。它只涉及“跳过初始化”(ISO C++ 03’6.7/3)< /P> 这里的很多帖子都提到跳过声明可能会导致变量“未声明”。事实并非如此。POD对象可以在没有初始值设定项的情况下声明,但其值不确定。例如:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' set (not 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;
}
 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;
此限制不限于switch语句。使用“goto”跳过初始化也是一个错误:

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

有一点琐事,这是C++中C++和C.的区别,跳过初始化不是错误。


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

到目前为止,大多数回复在一个方面是错误的:您可以在案例语句之后声明变量,但不能初始化它们:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...
switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

如前所述,解决此问题的一个好方法是使用大括号为您的案例创建范围。

我最喜欢的邪恶切换技巧是使用if(0)跳过不需要的案例标签

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

但是非常邪恶。

我只想强调斯利姆的。开关构造创建一个完整的第一类公民范围。因此,可以在switch语句中,在第一个case标签之前声明(并初始化)一个变量,而不需要额外的括号对:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}

目前为止,答案都是针对C++的。 对于C++,你不能跳过初始化。您可以在C中使用。但是,在C中,声明不是语句,大小写标签后面必须跟着语句

所以,有效的(但丑陋的)C,无效的C++

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}
<> Pr>,C++中,声明是一个语句,所以下面是有效的C++,无效的c

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}

有趣的是,这很好:

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

case 1:  
    break;
}
。。。但这不是:

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

case 1:  
    break;
}
我知道修复程序足够简单,但我还不明白为什么第一个示例不影响编译器。如前所述(2年前,呵呵),声明不是导致错误的原因,即使
case 1: int x=10;
        printf(" x is %d",x);
break;
label can only be a part of statement and declaration is not a statement
  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;
case 1:
       {
           int x=10;
           printf(" x is %d", x);
       }
break;
case 1: ;
           int x=10;
           printf(" x is %d",x);
break;
main()
{
    int x;   // Declare before
    switch(a)
    {
    case 1: x=10;
        break;

    case 2: x=20;
        break;
    }
}
switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}
#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}
#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}
#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}
switch (val)  
{  
case VAL:             /* <- C error is here */
  int newVal = 42;  
  break;
case ANOTHER_VAL:     /* <- C++ error is here */
  ...
  break;
}
switch (val)  
{  
case VAL:;            /* Now it works in C! */
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}
switch (val)  
{  
case VAL: 
  int newVal;
  newVal = 42;  
  break;
case ANOTHER_VAL:     /* Now it works in C++! */
  ...
  break;
}
switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}
if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block
int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}
goto label;

int x;

label:
cout << x << endl;
 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;