C++ 语句之switch中的细节

众所周知,C++的switch语句用于多分支选择,switch语句提供了一条便利的途径使我们能够在若干固定选项中做出选择。看似方便,用起来也确实方便,但若是没有经验,不注重细节问题,会踩很多坑,有时候还会因为看不懂编译器的报错备受折磨。

先举一个简单的例子:

#include <iostream>
using namespace std;
int main() {
	char ch = 'A';
	switch (ch)
	{
	case'A':
		cout << "It's A." << endl;
		break;
	case'B':
		cout << "It's B." << endl;
		break;
	case'C':
		cout << "It's C." << endl;
		break;
	case'D':
		cout << "It's D." << endl;
		break;
	default:
		break;
	}
	return 0;
}

程序很简单,给ch一个值,ch是什么就输出对应的语句,例如程序中,让ch=‘A’,那么运行后会输出"It's A."

break的作用

break的作用是中断这条switch语句,从break之后,从switch(){}整个区域之后的下一条语句开始,例如上文中的程序switch语句之后的下一条语句是return 0,因此在switch触发break之后,就执行return 0,(众所周知,整个switch是要看成一条语句的)

然后现在我们去掉case'A'里面的break来运行看看结果:

其余上下文与之前相同

......
	case'A':
		cout << "It's A." << endl;
		//break;
......

 看得出来,执行完case'A'分支内的语句之后没有停下,而是继续执行了case'B'内的分支,直到遇到case'B'分支内的break才结束。根据使用经验,这虽然是我们容易犯的一个错误(指的是:漏写break)但有时候也确实需要故意不写break。

接下来是另一个坑点:

switch内部的变量定义

简单来说就是在case分支内定义变量,这个错误非常的折磨人,我第一次遇到这个报错的时候,完全不能理解。下面来看一下问题出现的情景:

#include <iostream>
using namespace std;
int main() {
	char ch = 'B';
	switch (ch)
	{
	case'A':
		int x = 10;
		cout << "case A" << endl;
		break;
	case 'B':
		cout << "case B" << endl;
		break;
	}
	return 0;
}

就是上面这样一段简单的代码,会给你报一个错:

这是因为,整个switch内是同一个作用域,如果在switch的某个分支内部初始化一个变量,那么这个初始化就可能会被跳过,之后就有可能在未初始化的情况下使用这个变量,因此这种行为不合理。

简单来说就是有可能由于case分支跳过这个变量的初始化。

然而接下来又出现让人想不通的地方。

下面这段程序,不会报刚刚的错误,会输出正确的结果。总之就有了一个匪夷所思的地方,在case‘A’内部定义的变量,在case‘B’内仍然可见,因此可以得出一个结论,switch语句并不是直接跳过了case'A',而是执行了case'A'内部的定义x的语句,再转到case'B'分支

#include <iostream>
using namespace std;
int main() {
	char ch = 'B';
	switch (ch)
	{
	case'A':
		int x;//x只定义不初始化
		cout << "case A" << endl;
		break;
	case 'B':
		x = 10;//给x初始化
		cout << x << endl;
		break;
	}
	return 0;
}

为了验证,我们简单修改一下上面的程序,去掉了原本x的初始化操作,然后运行查看一下结果

    switch (ch)
	{
	case'A':
		int x;//x只定义不初始化
		cout << "case A" << endl;
		break;
	case 'B':
		cout << x << endl;
		break;
	}

不出意料报了一个没有初始化的错误。

 这里已经可以简单得出结论,switch语句会跳过初始化,但是不会跳过变量定义。

接下来我们再看一个例子:

#include <iostream>
#include<string>
using namespace std;
int main() {
	char ch = 'B';
	switch (ch)
	{
	case'A':
		string s;//定义了一个空字符串
		cout << "case A" << endl;
		break;
	case 'B':
		break;
	}
	return 0;
}

上面的语句,仍然会报同样的错误:

按照之前的逻辑,只是定义的话,似乎不会报错,但事实上,例如string等类型(具有默认构造函数)会进行默认的初始化,也就是说在定义的时候就已经被初始化了。因此仍然会报初始化被跳过的错误。

解决方案

如果想在case分支内部定义并且使用变量,可以使用一对{}框起来。

#include <iostream>
#include<string>
using namespace std;
int main() {
	char ch = 'A';
	switch (ch)
	{
	case'A':
		{
			string s = "hell world!";
			cout << s << endl;
		}
		break;
	case'B':
		break;
	}
	return 0;
}

在C++里面由{}包含起来的语句块,会被看做一条语句,这样就不会报变量初始化被跳过的错误,因为上面程序中s的作用域仅仅在{}内。同时这样会出现一个问题,就是无法在后面的分支使用这个变量。

接下来我们来看一种除了使用语句块,可以在分支内部定义并且初始化变量的情况:就是当定义所在的分支是switch语句中最后一条分支的时候,可以在其中初始化变量。

#include <iostream>
#include<string>
using namespace std;
int main() {
	char ch = 'A';
	switch (ch)
	{
	case 'A':
		string str = "hello";
		break;
	}
	return 0;
}

最后我们来看一个bug,我实在visual studio环境下运行的,首先看场景1:

#include <iostream>
#include<string>
using namespace std;
int main() {
	char ch = 'C';
	switch (ch)
	{
	case'A':
		int x;
	case'B':
		cout << x << endl;
	case'C':
		cout << x << endl;
	}
	return 0;
}

在A分支定义,然后不初始化,跳到C分支:发现报错

 然后我们稍作修改:

#include <iostream>
#include<string>
using namespace std;
int main() {
	char ch = 'C';
	switch (ch)
	{
	case'A':
		int x;
	case'B':
		x = 10;//在这里加上X的初始化,但是仍然选择C分支
		cout << x << endl;
	case'C':
		cout << x << endl;
	}
	return 0;
}

查看运行结果:

输出了一个未初始化的随机值

 这里我猜测,是在其他分支的初始化,骗过了visual studio的编译器,让它以为变量被初始化了。但实际上还是没有。其他有些编译器(例如Dev C++),在遇到未初始化的变量的时候,不会报错,最多给一个警告。

总结

switch语句虽然好用,但其实坑点非常多一个是break的问题,一个是变量定义相关,有关变量定义的问题。我再次简单总结一下:在switch语句任何地方定义变量(没有用{}包含起来),这个变量的作用域就是这条定义语句之后的所有区域,这个变量对这条语句接下来的区域可见的。所以不能再变量定义的时候初始化,这样会报一个初始化被跳过的错误,有一种掩耳盗铃的方法就是只定义不初始化,或者是在同一个分支内先定义再初始化欺骗编译器(对于会进行默认初始化的变量,不能做到只定义不初始化)。但最好还是不建议这样,建议不要再分支内定义或者使用变量,如果一定要定义变量,尽量限制在单个分支内,因此使用{}把变量定义限制在一个语句块之内。

  • 32
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值