C++ 在case语句中使用{}。为什么?

C++ 在case语句中使用{}。为什么?,c++,switch-statement,C++,Switch Statement,在case语句中使用{和}有什么意义?通常,无论一个case语句中有多少行,都会执行所有行。这只是关于旧/新编译器的一条规则,还是背后有什么原因 int a = 0; switch (a) { case 0:{ std::cout << "line1\n"; std::cout << "line2\n"; break; } } inta=0; 开关(a){ 案例0:{ std::cout这个{}表示一个新的范围块 考虑以下非常人为的例子:

case
语句中使用
{
}
有什么意义?通常,无论一个
case
语句中有多少行,都会执行所有行。这只是关于旧/新编译器的一条规则,还是背后有什么原因

int a = 0;
switch (a) {
  case 0:{
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
  }
}
inta=0;
开关(a){
案例0:{

std::cout这个
{}
表示一个新的范围块

考虑以下非常人为的例子:

switch (a)
{
    case 42:
        int x = GetSomeValue();
        return a * x;
    case 1337:
        int x = GetSomeOtherValue(); //ERROR
        return a * x;
}
您将得到一个编译器错误,因为范围中已定义了
x

将它们分离到各自的子范围将消除在switch语句之外声明
x
的需要

switch (a)
{
    case 42: {
        int x = GetSomeValue();
        return a * x; 
    }
    case 1337: {
        int x = GetSomeOtherValue(); //OK
        return a * x; 
    }
}

警告:

案例
中声明并初始化一个变量,如果没有
{}
包围,则该变量是错误的:

#include <iostream>
using namespace std;
int main() {
    int b = 3;
    switch (b) {
    case 3:
        int a = 3; //compilation error: "initialization of 'a' skipped by 'case' label"
        return a * b;
    case 1:
        return a * b;
    }
}
#包括
使用名称空间std;
int main(){
int b=3;
开关(b){
案例3:
int a=3;//编译错误:“由'case'标签跳过'a'的初始化”
返回a*b;
案例1:
返回a*b;
}
}

这是一种习惯,允许您将变量声明与结果析构函数(或范围冲突)一起注入进入
case
子句。另一种看待它的方式是,他们正在为他们希望拥有的语言编写,其中所有流控制都由块而不是语句序列组成。

检查这一基本编译器限制,您将开始想知道发生了什么:

int c;
c=1;

switch(c)
{
    case 1:
    //{
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    //}

    default : cout<<"def";
}
而这一条不会:

int c;
c=1;

switch(c)
{
    case 1:
    {
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    }

    default : cout<<"def";
}
intc;
c=1;
开关(c)
{
案例1:
{
int*i=新的int;
*i=1;

cout在switch中使用括号表示Rotem所说的一个新的范围块

但为了清楚起见,你阅读时也可以这样做。要知道案例在哪里停止,因为你可能会有条件中断

在case中使用initializer或一些非平凡对象声明变量的唯一方法是使用
{}
或其他具有自己作用域的控制结构(如循环或if语句)引入块作用域

switch (a)
{
    case 42: {
        int x = GetSomeValue();
        return a * x; 
    }
    case 1337: {
        int x = GetSomeOtherValue(); //OK
        return a * x; 
    }
}
血淋淋的细节

我们可以看到,案例只是带有标签的语句,就像goto语句使用的标签一样(这在第6.1节带标签的语句中有介绍),我们可以从第6.7节带标签的语句第3段中看到,在许多情况下不允许跳过声明,包括带有初始化的案例:

可以转移到块中,但不能以通过初始化绕过声明的方式。如果程序从具有自动存储持续时间的变量不在作用域内的点跳到其在作用域内的点,则该程序的格式不正确,除非该变量具有标量类型、具有普通默认构造函数的类类型和triv析构函数,这些类型之一的cv限定版本,或上述类型之一的数组,在没有初始值设定项的情况下声明(8.5)

并提供了以下示例:

void f() {
 // ...
 goto lx; // ill-formed: jump into scope of a

 ly:
  X a = 1;
 // ...
 lx:
  goto ly; // OK, jump implies destructor
          // call for a followed by construction
          // again immediately following label ly
}
注意,这里有一些微妙之处,您可以跳过没有初始化的标量声明,例如:

switch( n ) 
{
    int x ;
    //int x  = 10 ; 
    case 0:
      x = 0 ;
      break;
    case 1:
      x = 1 ;
      break;
    default:
      x = 100 ;
      break ;
}
是完全有效的()

至于不允许跳转通过初始化的理由,尽管涉及稍微不同的问题为自动变量提供了合理的理由:

[…]如果没有显式初始化,自动变量可以有不确定的(“垃圾”)值,包括陷阱表示[…]

更有趣的是,您可以在切换多个情况时扩展范围。最著名的例子可能是这样的:

void send( int *to, const int *from, int  count)
{
        int n = (count + 7) / 8;
        switch(count % 8) 
        {
            case 0: do {    *to = *from++;   // <- Scope start
            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);    // <- Scope end
        }
}
void发送(int*to,const int*from,int count)
{
int n=(计数+7)/8;
开关(计数%8)
{
案例0:do{*to=*from++;//0);//原因可能是:

  • 可读性,它在视觉上增强了每个案例作为一个范围部分
  • 为多个开关案例声明具有相同名称的不同变量
  • 微观优化—一个非常昂贵的资源分配变量的范围,您希望在离开案例范围后立即销毁该变量,甚至是“GOTO”命令使用的一个更通俗的场景

  • 一个用途是限制case语句中声明的变量的范围。缩进太多。case只是switch语句块中的标签:它们不会引入额外的嵌套,因此它们应该与
    switch
    关键字对齐,在第二个示例中,封闭的语句只缩进一次。请注意您在
    中断后有一个笨拙的四空格缩进;
    。请注意,已接受的答案仅部分正确,并遗漏了我在回答中提到的一些微妙之处。仅供参考:在C中(甚至是C11)不是C++,不能标记声明;它们不在语法类别<代码>语句<代码>中。C++中,(语法类别<代码>语句< /COD>的一个组件是<代码>声明语句< /代码>)。实际上,在我看来,即使跳过变量x的第二次声明,也会出现编译器错误。尽管过度使用这种样式并在switch语句中放入大的块会使遵循这些情况变得不可读。我更喜欢使语句保持小。@MatthieuM。我知道事实上,MS Visual Studio 2010将具有Abhishe的行为k表示:它不会在一个案例中编译任何变量声明(除非你用花括号表示该案例中的一个新范围)。我不知道这是否符合标准。@KRyan:不符合,但这是一个安全得多的替代方案,我很难责怪他们强制执行。标准第6.7(3)节(编号
    void send( int *to, const int *from, int  count)
    {
            int n = (count + 7) / 8;
            switch(count % 8) 
            {
                case 0: do {    *to = *from++;   // <- Scope start
                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);    // <- Scope end
            }
    }