C存在fall through的switch语句
switch语句的一般形式如下:
1 | switch(表达式){ |
一个遵循标准的C编译器至少允许一条switch语句中有257个case标签(ANSI C标准),这是为了允许switch满足一个8bit字符的所有情况(256个可能的值加上EOF)。
每个case结构由3部分组成:关键字case;紧随其后的常量值或常量表达式;再紧接一个冒号。看一个例子:
1 | int main(){ |
编译出错:
case a: j=2;
error: case label does not reduce to an integer constant
从这里不仅可以看出const修饰的变量并不代表就是常量,而且case后面必须为常量值或常量表达式。
a.当表达式的值与case中的常量匹配时,该case后面的语句就会执行。
b.default(如果有的话)可以出现在case列表的任意位置,但习惯上总是把default放在最后,它在其他的case均无法匹配时被选中执行。
c.如果没有default,而且所有的case均不匹配,那整条switch语句便什么都不做。
许多人可能觉得如果所有的case均不匹配,应该给出一个运行时错误信息,提示无匹配。在C语言中,几乎从来不进行运行时错误检查,运行时检查与C语言的设计理念相违背。按照C语言的理念,程序员应该知道自己正在干什么,而且保证自己的所作所为是正确的。
switch存在的问题:
其一:可以在switch的左花括号之后声明一些变量,但变量不会被初始化。
例如,可以在switch的左花括号之后声明一些变量,从而进行一些局部存储的分配。在最初的编译器里,这是一个技巧——绝大多数用于处理任何复合语句的代码都可以被复用,可以用于处理switch语句中由花括号包住的那部分代码。所以在这个位置上声明一些变量会被编译器很自然的接受,尽管在switch语句中为这些变量加上初始值是没有什么用处的,因为它绝不会被执行——语句从匹配表达式的case开始执行。示例如下:
1 | int main(){ |
编译时警告:
printf(“%d\n”,a);
warning: ‘a’ is used uninitialized in this function [-Wuninitialized]
运行结果显示:
0
4
在C语言中,当建立一个块时,一般总是这样开始的:
{
语句
}
你总是可以在两者之间增加一些声明,如:
{
声明
语句
}
当分配动态内存代价较高时,你可能会采用这种局部存储的方法,但有可能的话要尽量避免,编译器可以自由的忽略它,它可以通过函数调用来分配所有局部块需要的内存空间。
另一种用法是声明一些完全局部于当前块的变量:
1 | int main(){ |
运行输出:
a = 1, b = 2
若增加代码:
1 | int main(){ |
编译出错:
printf(“temp = %d\n”,temp);
error: ‘temp’ undeclared (first use in this function)
可见,变量temp完全局部于if语句的代码块。
C++在这方面又进了一步,允许语句和声明以任意的顺序出现,甚至允许变量的声明出现在for表达式的内部:
1 | for(int i = 0; i<100; i++){ ... } |
其二:switch内部任何语句都可以加上标签。
switch内部任何语句都可以加上标签,并在执行时跳转到那里,这就有可能破坏程序流的结构化。示例:
1 | int main(){ |
上述代码执行时,程序循环输出“loop”。
同时,平时在其他地方,为了避免破坏程序流的结构化,应当尽量不要使用goto语句。
其三:对case可能出现的值太过于放纵了。
所有的case都是可选的,任何形式的语句——包括带标签的语句都是允许的。这就意味着有些错误很难检测出来。代码示例:
1 | int main(){ |
上述代码将default的字母l变为数字1,编译警告:
defau1t: i++;
warning: label ‘defau1t’ defined but not used [-Wunused-label]
此时default由语句变为一个标签,程序仍然能够执行,执行输出结果:
loop
3
其四:最大的缺点——fall through
switch不会在每个case标签后面的语句执行完毕后自动终止。一旦执行某个case语句,程序将会依次执行后面所有的case,除非遇到break语句。代码示例:
1 | int main(){ |
输出结果为:
1 | case 2 |
这称之为“fall through”,意思是:如果case语句后面不加break,就依次执行下去,以满足某些特殊情况的要求。但实际上,这是一个非常不好的特性,因为几乎所有的case都需要以break结尾。在大多数情况下,你不希望因这个缺省的行为而不得不加上一条额外的break语句来改变它。
其五:break到底中断了什么。
先看一段有bug的代码:
1 | int main(){ |
程序输出:
step 1
b = 1
所以,从上面可以体会到,break语句跳出的是最近的那层循环语句或switch语句。if中的break语句使得代码直接跳出switch语句。在C语言中,不要低估”break“语句对控制结构的影响,慎重使用。
故综上,switch语句的一般形式为:
1 | switch(表达式){ |
本文标题:C存在fall through的switch语句
文章作者:Mr Bluyee
发布时间:2018-07-22
最后更新:2019-07-15
版权声明:The author owns the copyright, please indicate the source reproduced.