美文网首页
C存在fall through的switch语句

C存在fall through的switch语句

作者: Mr_Bluyee | 来源:发表于2018-07-22 17:50 被阅读18次

switch语句的一般形式如下:

switch(表达式){
    case 常量表达式:零条或多条语句
    case 常量表达式:零条或多条语句
    default: 零条或多条语句
}

一个遵循标准的C编译器至少允许一条switch语句中有257个case标签(ANSI C标准),这是为了允许switch满足一个8bit字符的所有情况(256个可能的值加上EOF)。
每个case结构由3部分组成:关键字case;紧随其后的常量值或常量表达式;再紧接一个冒号。看一个例子:

int main(){
    int i=0,j=0;
    const int a = 2;
    switch(i){
        case 1: j=1;
        case a: j=2;
        case 3: j=3;
        default: j=4;
    }
    return 0;
}

编译出错:
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开始执行。示例如下:

int main(){
    int i=0,j=0;
    switch(i){
        int a = 2;
        case 1: j=1;
        case 2: j=2;
        case 3: j=3;
        default: j=4;
        printf("%d\n",a);
    }
    printf("%d\n",j);
    return 0;
}

编译时警告:
printf("%d\n",a);
warning: 'a' is used uninitialized in this function [-Wuninitialized]
运行结果显示:
0
4

在C语言中,当建立一个块时,一般总是这样开始的:
{
语句
}
你总是可以在两者之间增加一些声明,如:
{
声明
语句
}
当分配动态内存代价较高时,你可能会采用这种局部存储的方法,但有可能的话要尽量避免,编译器可以自由的忽略它,它可以通过函数调用来分配所有局部块需要的内存空间。
另一种用法是声明一些完全局部于当前块的变量:

int main(){
    int a = 2,b = 1;
    if(a > b){
        int temp = a;
        a = b;
        b = temp;
    }
    printf("a = %d, b = %d\n",a,b);
    return 0;
}

运行输出:
a = 1, b = 2
若增加代码:

int main(){
    int a = 2,b = 1;
    if(a > b){
        int temp = a;
        a = b;
        b = temp;
    }
    printf("temp = %d\n",temp);
    printf("a = %d, b = %d\n",a,b);
    return 0;
}

编译出错:
printf("temp = %d\n",temp);
error: 'temp' undeclared (first use in this function)
可见,变量temp完全局部于if语句的代码块。
C++在这方面又进了一步,允许语句和声明以任意的顺序出现,甚至允许变量的声明出现在for表达式的内部:

for(int i = 0; i<100; i++){ ... }
其二:switch内部任何语句都可以加上标签。

switch内部任何语句都可以加上标签,并在执行时跳转到那里,这就有可能破坏程序流的结构化。示例:

int main(){
    int i = 2;
    switch(i){
        case 5+3: do_again:
        case 2: printf("loop\n");goto do_again;
        default: i++;
        case 3: ;
    }
    return 0;
}

上述代码执行时,程序循环输出“loop”。
同时,平时在其他地方,为了避免破坏程序流的结构化,应当尽量不要使用goto语句。

其三:对case可能出现的值太过于放纵了。

所有的case都是可选的,任何形式的语句——包括带标签的语句都是允许的。这就意味着有些错误很难检测出来。代码示例:

int main(){
    int i = 2;
    switch(i){
        case 5+3: ;
        case 2: printf("loop\n");
        defau1t: i++;
        case 3: ;
    }
    printf("%d\n",i);
    return 0;
}

上述代码将default的字母l变为数字1,编译警告:
defau1t: i++;
warning: label 'defau1t' defined but not used [-Wunused-label]
此时default由语句变为一个标签,程序仍然能够执行,执行输出结果:
loop
3

其四:最大的缺点——fall through

switch不会在每个case标签后面的语句执行完毕后自动终止。一旦执行某个case语句,程序将会依次执行后面所有的case,除非遇到break语句。代码示例:

int main(){
    switch(2){
        case 1: printf("case 1\n");
        case 2: printf("case 2\n");
        case 3: printf("case 3\n");
        case 4: printf("case 4\n");
        default: printf("default\n");
    }
    return 0;
}

输出结果为:

case 2
case 3
case 4
default

这称之为“fall through”,意思是:如果case语句后面不加break,就依次执行下去,以满足某些特殊情况的要求。但实际上,这是一个非常不好的特性,因为几乎所有的case都需要以break结尾。在大多数情况下,你不希望因这个缺省的行为而不得不加上一条额外的break语句来改变它。

其五:break到底中断了什么。

先看一段有bug的代码:

int main(){
    int i = 2,a = 0,b = 1;
    switch(i){
        case 1: 
            printf("case 1\n");
            break;
        case 2: 
            if(a == 0){
                printf("step 1\n");
                if(b ==1)
                    break;
                printf("step 2\n");
            }//代码意图是跳到这里
            printf("step 3\n");
            b++;
            break;
        default: printf("default\n");
    }//事实是跳到这里
    printf("b = %d\n",b);
    return 0;
}

程序输出:
step 1
b = 1
所以,从上面可以体会到,break语句跳出的是最近的那层循环语句或switch语句。if中的break语句使得代码直接跳出switch语句。在C语言中,不要低估”break“语句对控制结构的影响,慎重使用。
故综上,switch语句的一般形式为:

switch(表达式){
    case 常量表达式:
             零条或多条语句;
              break;
    case 常量表达式:
              零条或多条语句;
              break;
    default: 
              零条或多条语句;
}

相关文章

  • C存在fall through的switch语句

    switch语句的一般形式如下: 一个遵循标准的C编译器至少允许一条switch语句中有257个case标签(AN...

  • 雨点的成长2

    C语言循环语句 switch语句 格式: switch(标识符){ case a:...;break; c...

  • Swift复习系列:控制流之Switch语句

    Switch语句 与Objective-C中的Switch语句不同,在Swift中,Switch语句在每个case...

  • c# 学习笔记3

    条件判断 C# 中 switch 语句的语法: switch 语句必须遵循下面的规则: switch 语句中的 e...

  • JavaScript - 控制语句

    if语句:if(条件)...else(条件)...else switch:switch(expression){c...

  • 2017年9月11号学习总结

    一、主要学习内容 1、利用switch语句实现多分支选择 switch语句的格式: switch(表达式){ c...

  • Go入门系列(五)流程控制

    目录:一、判断二、循环二、复合逻辑 一、判断 if...else...嵌套 switch 语句 如果使用 fall...

  • js常用流程控制语句总结

    一、if语句 二、switch语句 JavaScript switch语句虽然参考的C语言的写法,但是有特殊性 s...

  • switch语句原理与效率

    *1)switch语句,找CALL的时候经常碰到switch,如果有效的识别出switch语句,只要找到其中一个C...

  • C switch 语句

    用if else 处理多分支的时候,如果分支过多,那将会显得很不方便,而且容易出错。比如我们输入一个数字,然后返回...

网友评论

      本文标题:C存在fall through的switch语句

      本文链接:https://www.haomeiwen.com/subject/lmfrmftx.html