C语言学习指南:从规范编程到专业级开发
上QQ阅读APP看书,第一时间看更新

6.4 switch()...语句

if()...else...语句所使用的表达式是一个条件表达式,这种表达式只可能有两种求值结果:true和false。如果是true,程序就转入if分支;如果是false,程序就转入else分支。如果我们想要判断的这个表达式会有两种以上的求值结果,而又想让程序转到与每一种结果相对应的分支里面去执行,那该怎么办呢?

这可以通过switch()...语句实现。这种语句会根据表达式的求值结果,选出一条与该结果相匹配的分支,并进入这条分支,向下执行。

switch()...语句的语法是:

程序会对这里的expression求值。接下来的那一部分(也就是{与}之间的那一部分)叫作case语句块(case-statement block),其中可以包含一个或多个case:标签,另外,还可以有一个default:标签。程序会把expression的求值结果跟case语句块里面每个case:标签中的值(也就是constant-1、constant-2等值)对比,如果发现它与某个标签中的值相等,那么就从该标签所标注的那个地方开始往下执行。这些标签都要按照case<value>:的格式来写。如果expression的求值结果不能跟任何一个case:标签中的常量值相匹配,并且case语句块中有default:标签,那么就从default:标签所标注的地方开始往下执行。

虽然C语言并不要求case语句块里一定要有default:标签,但最好还是写出这样一个标签,哪怕仅在下面打印一条错误信息也好,这样能够确保这个switch语句不会出现意外的结果。因为有时你会调整case标签中的常量值,或者会忘记处理expression可能取到的某个值,default:标签能够提醒你注意这些问题。

与if()...else...语句中的两个分支类似,case:与default:标签所标注的既可以是简单语句,也可以是复合语句。

刚才那个函数里面的闰年判断逻辑如果改用switch语句来写,那就是下面这样:

请注意,代码块中的case1:、case 2:与case 3:标签本身并不是语句,switch语句决定了自己应该转入的标签之后,会从该标签所在的那个地方开始继续往下执行,在执行过程中就算遇到其他标签也不停止。如果有多个标签上下叠放,那么上面那些标签所标注的其实跟最下面那个标签所标注的是同一条语句,无论switch语句转入的是哪个标签,它开始执行的那条语句都是最下面那个标签所标注的那条语句。具体到本例来说,无论switch转入的是case 1:、case 2:、case 3:还是default:,它所执行的第一条语句都是default:标签所标注的那条语句,也就是return false;。

由于我们只关注year与4相除的余数是不是0,而不关注余数为1、2、3的那三种情况,因此可以把switch语句里面的另外三个标签简化掉:

程序在执行isLeapYear()函数中的switch结构时,如果遇到return语句,那么不仅会跳出switch,而且会从整个函数里面返回。

我们经常需要让switch语句在执行完某个分支之后就及时结束,而不要再继续执行下一个分支。也就是说,我们只想让switch语句在跳转到与表达式相匹配的标签之后,将该标签所对应的这段代码执行完就好,不再继续进入下一个标签。因此,我们需要采用一种办法,让程序能够从switch语句的case代码块中跳出来,这个办法就是break。

break语句的作用是让程序从该语句所在的代码块里面跳出来,跳到这个代码块的末尾。

为了演示break语句的用法,我们编写一个calc()函数,让该函数根据operator这个字符参数所表示的某种运算来处理它所收到的两个操作数(也就是operand1与operand2),并返回运算结果:

我们想让这个函数的适用范围大一些,因此,把这两个操作数的类型都设计成double。如果调用方传入的是int或float值,那么程序会执行隐式类型转换,把它们自动转成double。虽然这个函数的返回值类型是double,但调用方可以将该值手动转换成它所需要的类型,比方说,如果调用方传入的是两个int型的操作数,那么可以把函数的返回值明确地转换成int。

用来表示运算的字符总共有5个,我们针对每个字符都编写相应的case分支。对于除法来说,我们还会进一步检查除数是否为0。

你可以做个实验,把整个if()...else...语句注释掉,然后直接做除法,看看程序在除数为0的情况下会怎样。

对于求余运算来说,由于该运算要求两个操作数都必须是整数,因此,我们要将operand1与operand2参数的值手工转换成int类型。

你可以再做个实验,把这两个手工转型操作去掉,然后拿各种实数作为参数来调用calc()函数,让该函数在这些实数之间做求余运算,看看程序会出现什么问题。

另外还要注意default:分支的作用,它可以把calc()函数不支持的运算拦截下来。在switch语句里面内置这样的错误检测逻辑是很有用的,因为这个程序将来可能会由其他许多位程序员做出各种各样的修改,default:分支或许能把修改之后所产生的某些问题暴露出来。

为了把这个程序写完整,我们还需要补充下面这些代码。请把这段代码与刚才的calc()函数合起来写在名为calc.c的源文件中:

在这个程序中,我们用5种有效的运算符分别调用calc()函数,然后再拿一种无效的运算符来调用它。其中,第四次调用所要执行的是除法,我们故意将除数设为0,看看程序会有什么表现。请保存这份文件,然后编译并运行程序。你应该会看到类似下面这样的输出信息。

我们看到,无论执行的是有效运算还是无效运算,程序都能合适地予以处理。另外,别忘了做刚才说的那两项实验。

如果要测试的变量只有一个,而这个变量的取值可能有许多种,那么就比较适合拿switch()...语句来判断。下一节我们要讲解另一种判断方式,它比switch()...语句更灵活。