C++中使用switch..case语句的易出错陷阱和规避方法
2016-10-10 23:25
579 查看
C++作为C语言的升级版,支持很多C语言不支持的语法。例如,函数中的局部变量不必在函数的最开始统一定义了,在函数内部随时定义新的局部变量成为可能。
比如下面的示例代码,在for循环的初始条件中定义了用于计数的整形变量i,这是不符合C语言语法规定的,故而无法通过C语言编译器的编译。
必须把代码修改为如下所示,在函数的开头定义整形变量i,才能通过编译。
不过,如果使用C++编译器,以上两段代码都是符合语法规定的,都可以通过编译。
回到主题,这里要说一个C++在语法方便的同时带来的隐患。来看这一段C++的swtich..case代码:
这段代码利用了刚才提到的C++新的语法支持,在switch..case的分支case 1中,定义了整形变量n,并且把它赋值为1。这段代码完全合法,编译通过(VS2012环境中),运行结果如下图所示,一切正常。
![](https://images2015.cnblogs.com/blog/956741/201610/956741-20161010232323774-1786822648.jpg)
问题出现在当我们尝试初始化整形变量n的时候。定义变量的同时初始化,是一个好习惯,然而,此时对n的初始化却会引发错误导致无法编译。修改示例代码中的case 1部分,尝试把整形变量n初始化为0:
编译报错如下图所示。大致意思是说n的初始化操作被跳过了。
![](https://images2015.cnblogs.com/blog/956741/201610/956741-20161010232342383-1852295645.jpg)
回想函数调用过程,在函数的参数、当前代码地址、栈地址入栈之后,紧接着系统会给函数内部的局部变量在栈里划分一片空间,这片划分出来的空间入栈之后,系统会给所有被初始化的局部变量赋予初始值。
如此一来,在示例代码中的情况下,C++编译器就不知所措了。整形变量n的作用域是swtich..case结构被花括号括起来的整个部分:虽然整形变量n的定义在case 1标签下面,但它对于case 2和case default都是可见的,可以把case 2和case default理解为整形变量n的“利益相关者”。站在编译器的角度,如果对整形变量n进行初始化操作,那么则相当于默认switch..case会跳转到case 1标签下,这显然是一种置case 2和case default于不顾的非法行为;如果不进行初始化操作,那么编译器就没有完整翻译程序源代码,没有完成自己的职责。在这种两难境地下,编译器只好选择报错了。
有没有一种解决方案,既能让我们充分利用C++灵活的语法规定(在switch..case结构内部也可以定义局部变量),又能够让我们保持定义局部变量后立即初始化的良好习惯,而且还不让编译器为难(报错)呢?
答案是有的!解决思路是把定义的局部变量隔绝起来,达到缩小局部变量作用域的效果,也就是让其他case分支看不到它。正所谓“眼不见心不烦”,其他的分支看不到这个局部变量,也就不会产生什么意见了。具体的解决方案就是在每一个case的标签下面都嵌入一对花括号。
修改后的代码如下所示:
经测试,编译通过,如下图所示:
![](https://images2015.cnblogs.com/blog/956741/201610/956741-20161010232403618-883290443.jpg)
当然,case 2和case defalut的下面不是必须要加一对花括号,因为它们下面并没有局部变量的定义和初始化操作。但是,在所有case label下都加上一对花括号是一个很好的习惯,因为随着代码量的增加,万一这个label下面发生了变量的定义和初始化操作而没有引起注意的话,调试起来可能会很麻烦。在那种情况下,编译器的提示信息可能不会像本文示例这样给出明确的错误描述,而是可能会因为上下文环境的原因而给出晦涩不清的错误描述,让人一下看不出问题到底出在了哪里。
总结:在享受C++语法便利的同时,不要忘记了这些潜在的小陷阱,灵活利用花括号来限制局部变量的作用域。
比如下面的示例代码,在for循环的初始条件中定义了用于计数的整形变量i,这是不符合C语言语法规定的,故而无法通过C语言编译器的编译。
int fun() { int n = 6; for (int i = 0; i < 3; i++) { n += i; } return n; }
必须把代码修改为如下所示,在函数的开头定义整形变量i,才能通过编译。
int fun() { int n = 6; int i = 0; for (i = 0; i < 3; i++) { n += i; } return n; }
不过,如果使用C++编译器,以上两段代码都是符合语法规定的,都可以通过编译。
回到主题,这里要说一个C++在语法方便的同时带来的隐患。来看这一段C++的swtich..case代码:
void fun(int nInput) { switch(nInput) { case 1: int n; n = 1; printf("case1"); break; case 2: printf("case2"); break; default: printf("case defalut"); break; } } int main(int argc, char* argv[]) { fun(8); return 0; }
这段代码利用了刚才提到的C++新的语法支持,在switch..case的分支case 1中,定义了整形变量n,并且把它赋值为1。这段代码完全合法,编译通过(VS2012环境中),运行结果如下图所示,一切正常。
![](https://images2015.cnblogs.com/blog/956741/201610/956741-20161010232323774-1786822648.jpg)
问题出现在当我们尝试初始化整形变量n的时候。定义变量的同时初始化,是一个好习惯,然而,此时对n的初始化却会引发错误导致无法编译。修改示例代码中的case 1部分,尝试把整形变量n初始化为0:
void fun(int nInput) { switch(nInput) { case 1: int n = 0; n = 1; printf("case1"); break; case 2: printf("case2"); break; default: printf("case defalut"); break; } } int main(int argc, char* argv[]) { fun(8); return 0; }
编译报错如下图所示。大致意思是说n的初始化操作被跳过了。
![](https://images2015.cnblogs.com/blog/956741/201610/956741-20161010232342383-1852295645.jpg)
回想函数调用过程,在函数的参数、当前代码地址、栈地址入栈之后,紧接着系统会给函数内部的局部变量在栈里划分一片空间,这片划分出来的空间入栈之后,系统会给所有被初始化的局部变量赋予初始值。
如此一来,在示例代码中的情况下,C++编译器就不知所措了。整形变量n的作用域是swtich..case结构被花括号括起来的整个部分:虽然整形变量n的定义在case 1标签下面,但它对于case 2和case default都是可见的,可以把case 2和case default理解为整形变量n的“利益相关者”。站在编译器的角度,如果对整形变量n进行初始化操作,那么则相当于默认switch..case会跳转到case 1标签下,这显然是一种置case 2和case default于不顾的非法行为;如果不进行初始化操作,那么编译器就没有完整翻译程序源代码,没有完成自己的职责。在这种两难境地下,编译器只好选择报错了。
有没有一种解决方案,既能让我们充分利用C++灵活的语法规定(在switch..case结构内部也可以定义局部变量),又能够让我们保持定义局部变量后立即初始化的良好习惯,而且还不让编译器为难(报错)呢?
答案是有的!解决思路是把定义的局部变量隔绝起来,达到缩小局部变量作用域的效果,也就是让其他case分支看不到它。正所谓“眼不见心不烦”,其他的分支看不到这个局部变量,也就不会产生什么意见了。具体的解决方案就是在每一个case的标签下面都嵌入一对花括号。
修改后的代码如下所示:
void fun(int nInput) { switch(nInput) { case 1: { int n = 0; n = 1; printf("case1"); break; } case 2: { printf("case2"); break; } default: { printf("case defalut"); break; } } } int main(int argc, char* argv[]) { fun(8); return 0; }
经测试,编译通过,如下图所示:
![](https://images2015.cnblogs.com/blog/956741/201610/956741-20161010232403618-883290443.jpg)
当然,case 2和case defalut的下面不是必须要加一对花括号,因为它们下面并没有局部变量的定义和初始化操作。但是,在所有case label下都加上一对花括号是一个很好的习惯,因为随着代码量的增加,万一这个label下面发生了变量的定义和初始化操作而没有引起注意的话,调试起来可能会很麻烦。在那种情况下,编译器的提示信息可能不会像本文示例这样给出明确的错误描述,而是可能会因为上下文环境的原因而给出晦涩不清的错误描述,让人一下看不出问题到底出在了哪里。
总结:在享受C++语法便利的同时,不要忘记了这些潜在的小陷阱,灵活利用花括号来限制局部变量的作用域。
相关文章推荐
- c++ switch/case 使用 string的方法
- C/C++学习(一)题目:求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
- android library工程中使用switch_case语句,调用资源文件的id出现错误的原因分析及解决方法
- (转载) case语句在SQL中的使用方法
- 每天学习一算法系列(12) (求1+2+…+n,不能使用乘除法,for、while、if 、else、switch、case 等关键字以及条件判断语句)
- c/c++_计算总运费_switch语句的使用
- 求1+2+…+n, 要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
- 在switch的case语句后,使用UIAlertView报错
- case语句在SQL中的使用方法大全
- 题目:求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
- 关于c++使用sql语句调用 mysql数据库中带参函数的方法
- switch 语句关于case的使用
- 在VC中使用switch语句时遇到“error C2360: initialization of 'k' is skipped by 'case' label”的编译错误。
- 使用Action表驱动代替switch…case语句
- 数据库里类switch语句和三元运算符的使用(case)
- case语句在SQL中的使用方法
- C#有效使用switch case语句
- Access数据库里使用case when语句的方法
- 用枚举方法 在case 语句中使用字符
- android 打library 库 不能使用switch/case语句