《编写高质量代码:改善c程序代码的125个建议》——建议23:正确地使用switch语句

简介:

本节书摘来自华章计算机《编写高质量代码:改善c程序代码的125个建议》一书中的第3章,建议23,作者:马 伟 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

建议23:正确地使用switch语句

相对于if语句而言,switch语句可以更方便地应用于多个分支的控制流程。C89指明,一个switch语句最少可以支持257个case语句,而C99则要求至少支持1023个case语句。然而,在实际开发环境中,为了程序的可读性与执行效率,应该尽量减少switch语句中的case语句。
除此之外,switch语句与if语句不同的是,switch语句只能够测试是否相等,因此,case语句后面只能是整型或字符型的常量或常量表达式;而在if语句中还能够测试关系与逻辑表达式。
建议23-1:不要忘记在case 语句的结尾添加break语句
在switch语句中,每个case 语句的结尾不要忘记添加break语句,否则将导致多个分支重叠。当然,除非有意使多个分支重叠,这样可以免去break语句。下面我们来看一个实际示例,如代码清单3-2所示。

代码清单3-2 switch示例
#include <stdio.h>

void print_week(unsigned int day);

void print_week(unsigned int day)
{
    switch(day)
    {
    case 1:
            printf("Monday\n");
            break;
    case 2:
            printf("Tuesday\n");
            break;
    case 3:
            printf("Wednesday\n");
            break;
    case 4:
            printf("Thursday\n");
            break;
    case 5:
            printf("Friday\n");
            break;
    case 6:
            printf("Saturday\n");
            break;
    case 7:
            printf("Sunday\n");
            break;
    default:
            printf("error\n");
            break;
    }
}
int main (void)
{
    print_week(3);
    return 0;
}

在代码清单3-2中,在print_week函数中通过switch语句实现根据数字输出星期名称的功能。执行代码清单3-2,程序将输出“Wednesday”。
现在,如果将case 1~case 4的break语句去掉,如代码清单3-3所示,程序会输出什么结果呢?
代码清单3-3 switch去掉break示例

void print_week(unsigned int day)
{
    switch(day)
    {
    case 1:
            printf("Monday\n");
    case 2:
            printf("Tuesday\n");
    case 3:
            printf("Wednesday\n");
    case 4:
            printf("Thursday\n");
    case 5:
            printf("Friday\n");
            break;
    case 6:
            printf("Saturday\n");
            break;
    case 7:
            printf("Sunday\n");
            break;
    default:
            printf("error\n");
            break;
    }
}
int main (void)
{
    print_week(2);
    return 0;
}

在代码清单3-3中,由于case 1~case 4缺少break语句,因此将导致多个分支重叠,其运行结果如图3-2所示。


c753bac7f848fe0c9f08f912532cec83917230d0

图3-2 代码清单3-3的运行结果
建议23-2:不要忘记在switch语句的结尾添加default语句
在switch语句中,default 语句主要用于检查默认情况,或者处理错误情况,如下面的示例代码所示:
default:
    printf("error\n");
    break;

如果在switch语句中去掉default 语句,那么switch语句将失去对默认情况与错误情况的处理能力。所以,奉劝大家不要偷懒,老老实实把每一种情况都用case 语句来完成,而把真正对默认情况的处理交给default 语句来完成。即使程序真的不需要default 处理,也应该保留此语句:

default:
    break;

这样做并非画蛇添足,可以避免令人误以为你忘记了default处理。
建议23-3:不要为了使用case 语句而刻意构造一个变量
在实际编程应用中,switch中的case 语句应该只用于处理简单的、容易分类的数据。如果数据并不简单,却为了使用case 语句而刻意构造一个变量,那么这种变量很容易令我们得不偿失。因此应该严格避免这种变量,并使用if/else if/else结构来处理这类程序,如下面的示例代码所示:

char ch = c[0];
switch (ch)
{
case 'a':
    f1();
    break;
case 'b':
    f2();
    break;
case 'c':
    f3();
    break;
default:
    break;
}

在上面的程序中,字符变量ch的值是取字符数组c[]的第一个字符,与case语句中的常量值逐一进行比较。很显然,这种方法存在一个严重的问题。
例如,如果字符数组c[]中存储的是“ab”字符串,那么c[0]会取第一个字符“a”与case 语句进行匹配,因此会匹配到第一个case语句,并调用f1() 函数。然而,如果字符数组c[]中存储的是其他以字符a 开头的字符串(比如“abc”“abcd”“abcde”等),因为c[0] 始终会取第一个字符的关系,因此它们同样会匹配第一个case 语句而调用f1() 函数。其他的case语句同理。很显然,这并不是我们想要的结果。
由此可见,当为了使用case 语句而刻意构造一个变量时,真正的数据可能不会按照我们所希望的方式映射到case 语句。因此,我们应该严格避免为了使用case 语句而刻意构造一个变量,并使用if/else if/else结构来处理这类程序,如下面的示例代码所示:

if(0 == strcmp("ab",c))
{
    f1();
}
else if(0 == strcmp("bc",c))
{
    f2();
}
else if(0 == strcmp("cd",c))
{
    f3();
}
else
{
}

建议23-4:尽量将长的switch语句转换为嵌套的switch语句
有时候,当一个switch语句中包括很多个case语句时,为了减少比较的次数,可以把这类长switch语句转为嵌套switch语句,即把发生频率高的case 语句放在一个switch语句中,作为嵌套switch语句的最外层;把发生频率相对低的case语句放在另一个switch语句中,放置于嵌套switch语句的内层。
例如,下面的代码把发生频率相对较低的情况放置于默认的case语句内。

void print_week(unsigned int day)
{
    switch(day)
    {
    case 1:
            printf("Monday\n");
            break;
    case 2:
            printf("Tuesday\n");
            break;
    case 3:
            printf("Wednesday\n");
            break;
    case 4:
            printf("Thursday\n");
            break;
    case 5:
            printf("Friday\n");
            break;
    default:
            switch(day)
            {
            case 6:
                    printf("Saturday\n");
                    break;
            case 7:
                    printf("Sunday\n");
                    break;
            default:
                    printf("error\n");
                    break;
            }
    }
}

在上面的代码中,假设case 6与case 7不经常发生,因此将它们放置到嵌套switch语句的最内层。从表面看,虽然这样损失了程序的一定可读性,但当case语句很多,并且确实有些case语句发生的频率比较低时,这种解决方案还是可取的。

相关文章
|
存储 C语言
《编写高质量代码:改善c程序代码的125个建议》—— 导读
众所周知,C语言是一门既具有高级语言特点,又有汇编语言特点的通用计算机编程语言,无论是操作系统(如Microsoft Windows、Mac OS X、Linux和UNIX等)、嵌入式系统与普通应用软件,还是目前流行的移动智能设备开发,随处都可以看见它依然矫健的身影。
1889 0