switch内部的变量定义

转载自:switch case语句内部变量定义

switch case语句是非常常用的语句,入门的码农也知道是做什么的。

但关于switch case内定义变量的问题,网上的很多博文都有谬误,在这里我写一下对这个语句的了解。


先看合法的定义方式:

  1. int main(int argc, const char * argv[]) {  
  2.     int idx = 2;  
  3.     switch (idx) {  
  4.             int k;  
  5.         case 1:  
  6.             int j;  
  7.             break;  
  8.         case 2:  
  9.             k = 1;  
  10.             j = 2;  
  11.             std::cout<<"K:"<<k<<std::endl;  
  12.             std::cout<<"J:"<<j<<std::endl;  
  13.             break;  
  14.         default:  
  15.             break;  
  16.     }  
  17.     return 0;  
  18. }  


在C++11 std Dialect下 打印出的结果是:
  1. K:1  
  2. J:2  


然后看看不合法的写法:

  1. int main(int argc, const char * argv[]) {  
  2.     int idx = 2;  
  3.     switch (idx) {  
  4.             int k = 1;  
  5.         case 1:  
  6.             int j = 1;  
  7.             break;  
  8.         case 2:  
  9.             k = 1;  
  10.             j = 2;  
  11.             std::cout<<"K:"<<k<<std::endl;  
  12.             std::cout<<"J:"<<j<<std::endl;  
  13.             break;  
  14.         default:  
  15.             break;  
  16.     }  
  17.     return 0;  
  18. }  

编译无法通过。


解释下原因:

一为什么合法?

首先一个变量有没有被定义是在编译时就检查过的,因此这里可以编译通过。

其次在处理switch case语句中,C++11标准的编译器都会在执行case跳转前为变量分配空间,因此执行也没有问题。

(当然我并不建议在case语句外定义变量,因为何时为变量分配空间是编译器特性,而非语言特性,虽然这个问题中遵守C++11标准的编译器没有问题)

二为什么不合法?

C++11标准禁止这种写法。在编译时,编译器就会报错。C++11禁止这种写法的原因来自于C++的一个规定:

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer. (The transfer from the condition of a switch statement to a case label is considered a jump in this respect.)

众所周知,C++不允许使用未初始化的变量,而初始化操作和定义变量对于编译器来说是两码事,初始化操作是一个确确实实的在运行时才会被调用的语句,是可以被case跳转屏蔽掉的语句,而定义则是在编译器就完成检查的。

如果二的写法合法的话,那么会发生这样的问题:

编译时定义变量被执行,初始化和赋值语句也用各自的方式编译完成,并且在运行时,程序也的确为此变量分配了空间。但是在运行时,初始化语句却在某些情况下被跳转掉了。

虽然对于int等类型的变量,赋值和初始化在我们看来都是"=",但编译后的二进制是完全不同的,编译器会在编译时把"int j = 1;"编译成初始化,而把"j = 1;"编译成赋值。因此在整个运行过程中,如果初始化语句被case跳转掉了,我们在其他case语句中的"="不可能对此变量进行初始化。

这时,如果在整个switch(){}的定义域中,有对此变量的调用,那就是『试图在初始化一个变量前使用它』,因此C++11禁止了这种写法。


额外谈一些:

如果不在case中加上{},整个switch(){}都是使用同一个作用域,这一点通过上文符合语法例子中对J、K的定义和使用就能看出来。最好的方法就是在编码时,将整个switch语句用到的变量在switch外声明,并且针对某个case需要单独使用变量的情况,用{}明确此case语句的作用域

case的实现与goto非常相似,case的本质是一种标签,在switch case语句中变量的定义问题可以推广到goto语句中。

下面的代码也是不合法的,因为string是一个类,类有自己的隐式初始化方法,实际上这依然是个可能被跳转掉的初始化语句。

  1. case 1:  
  2.     std::string tempStr;  
  3.     break;  

猜你喜欢

转载自blog.csdn.net/li1914309758/article/details/79231411