在case语句中使用{}.为什么?
Using {} in a case statement. Why?
在case
语句中使用{
和}
有什么意义?通常,无论case
语句中有多少行,所有的行都会被执行。这只是一条关于较旧/较新编译器的规则,还是有其背后的原因?
int a = 0;
switch (a) {
case 0:{
std::cout << "line1n";
std::cout << "line2n";
break;
}
}
和
int a = 0;
switch (a) {
case 0:
std::cout << "line1n";
std::cout << "line2n";
break;
}
{}
表示作用域的新块。
考虑以下非常做作的例子:
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;
}
}
警告:
声明和初始化case
内部的变量而不包围{}
是错误的:
#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;
}
}
TL;DR
在事例中用初始化器或一些非平凡对象声明变量的唯一方法是使用{}
或其他具有自己作用域的控制结构(如循环或if语句)引入块作用域。
华丽细节
我们可以看到,用例只是标记的语句,就像goto语句中使用的标签一样(这在C++标准草案第6.1节"标记的语句"中有介绍),我们可以从6.7
节第3段中看到,在许多情况下都不允许跳过声明,包括那些具有初始化的情况:
可以转移到块中,但不能通过初始化绕过声明。从一个具有自动存储持续时间的变量不在作用域内的点跳到它在作用域中的点的87程序是格式错误的,除非该变量具有标量类型、具有平凡默认构造函数和平凡析构函数的类类型、这些类型之一的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 ;
}
是完全有效的(实例)。当然,如果你想在每个情况下声明相同的变量,那么它们都需要自己的作用域,但在开关语句之外,它的工作方式也是一样的,所以这应该不会太令人惊讶。
至于不允许跳过初始化的理由,缺陷报告467虽然涵盖了一个略有不同的问题,但为自动变量提供了合理的理由:
[…]如果未显式初始化,自动变量可能具有不确定("垃圾")值,包括陷阱表示,[…]
更有趣的可能是,您将开关中的范围扩展到多个情况。最著名的例子可能是Duff的设备,它看起来像这样:
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
}
}
这是一种习惯,允许您将带有结果析构函数(或范围冲突)的变量声明注入case
子句。另一种观点是,他们正在为他们希望拥有的语言写作,其中所有的流控制都由块而不是语句序列组成。
检查一下这是一个基本的编译器限制,你会开始想知道发生了什么:
int c;
c=1;
switch(c)
{
case 1:
//{
int *i = new int;
*i =1;
cout<<*i;
break;
//}
default : cout<<"def";
}
这会给你一个错误:
error: jump to case label [-fpermissive]
error: crosses initialization of ‘int* i’
而这个不会:
int c;
c=1;
switch(c)
{
case 1:
{
int *i = new int;
*i =1;
cout<<*i;
break;
}
default : cout<<"def";
}
在开关中使用括号表示Rotem所说的新范围块。
但当你阅读时,为了清晰起见,它也可以。要知道案例在哪里停止,因为它可能有条件中断。
原因可能是:
- 可读性,它在视觉上增强了每个案例的范围部分
- 为多个开关情况声明具有相同名称的不同变量
- 微观优化-一个非常昂贵的资源分配变量的范围,您希望在离开案例范围后立即销毁该变量,甚至是"GOTO"命令使用的更为复杂的场景
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- 为什么在全局范围内使用"extern int a"似乎不行?
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 为什么会发生堆损坏
- 为什么使用 "this" 指针调用派生成员函数?
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 为什么比较运算符如此快速
- 为什么 Serial.println(<char[]>);返回随机字符?
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 为什么会给出"multiple test case"错误?
- 为什么我总是"error: expected unqualified-id before 'case'"?
- 为什么您可以在 switch 语句中的"默认"下定义变量,而不能在"case"下定义变量
- 当"if else"已经存在时,我为什么要学习"switch case"
- 为什么case语句中的标签应该是常量
- 在case语句中使用{}.为什么?
- 为什么"case::LABEL:"用 g++ 编译?
- 在开关案例语句中,它说"duplicate case value"出现错误。有人知道为什么吗?
- 为什么在switch case语句中只允许有限的类型?