
6.3 if()...else...语句
if()...else...语句是一种复杂的语句,它有两种形式,一种只带有if()...部分,另一种带有if()...与else...两个部分。
完整的if()...else...语句有两个分支,会在条件为真(true)与条件为假(false)的情况下分别执行。未得到执行的那一个分支里面的语句会被程序忽略。if()...形式的语句没有false分支,如果条件成立,程序就执行true分支,如果不成立,则会跳过这个if结构。
if语句有两种形式,其语法规则如下:
□第一种是简单形式,也就是不带false分支的形式:

□第二种是完整形式,也就是既带有true分支,又带有false分支的形式:

简单形式与完整形式都要对expression求值。如果求值结果是true,那么就执行statement1。对于完整形式来说,如果求值结果是false,那么就执行statement2。无论是true还是false,程序执行完整个if结构之后都会继续执行下一条语句,也就是statement3。
请注意,这两种形式中的statement1与statement2后面都没有写分号。因为它们既可以是以分号结尾的简单语句,也可以是用一对大括号来标识的复合语句。如果是复合语句,那么其中可以包含许多条以分号结尾的简单语句,但这个复合语句本身是不以分号结尾的。
条件语句有一种简单的用法,可以判断出某个整数值是偶数还是奇数。我们在做这样的判断时,正好可以使用前面提到的求余运算符(%)。下面这个函数采用简单形式的if语句做判断:

这个函数首先把判断结果定为false(或者说,首先假设这个值不是偶数),然后用简单形式的if语句来判断它与2相除的余数,并在余数为0时修改判断结果,最后返回这个判断结果。这里的if()...语句只有一个分支,我们想在num % 2为0的情况下执行该分支,因此需要使用==这个关系运算符来判断num % 2的结果是不是0。假如不这么写,而是直接将num % 2用作条件,那么当num % 2为0时,程序会把它视为布尔值false,从而跳过该分支。所以,我们必须通过适当的写法,让程序把num % 2为0时的情况视为true(真),从而进入该分支。在条件为假的情况下,程序不执行if分支,而是直接把函数开头设置给isEven变量的默认值作为判断结果返回给调用方。
这个函数也可以改用另一种写法来实现,这种写法会在能够判断出结果时立刻通过return语句返回该结果,因而会出现多条return语句。它不像刚才那种写法那样,把判断结果保存在isEven变量里面,等到函数即将结束时再返回:

这个函数所使用的判断条件相当简单,只有当num为奇数时,这项条件才能成立,因此,在这种情况下,我们应该让函数返回false,以表示这个数不是偶数。该函数有两个退出点,其中一个会在条件成立的情况下触及,另一个会在条件不成立的情况下触及。如果num是偶数,那么第一条return语句是不会执行到的。
这个函数用最简省的写法来实现是:

如果num是偶数,那么num % 2就是0(这相当于false),我们对这个结果做逻辑非(NOT)运算(相当于把false变为true),并将运算结果返回给调用方。
如果用完整形式的if语句来写,那么这个函数[1]的代码应该是:

这个函数没有给isOdd变量设定初始值,因此,我们必须设法保证,无论调用方传入的num是奇数还是偶数,该变量都会正确地设置成true或false。在本例中,if语句的每一个分支都只包含一条简单的语句,用来给isOdd变量赋值。
为了让大家更多地了解if语句的用法,我们来写这样一个函数,以判断某年是否为闰年。西方世界直到1752年才开始普遍采用目前这种闰年制度。一个太阳年(solar year)约为365.25日,也就是365又1/4日,如果把一个公历年定为365日,那么每4年就短大概1天,因此需要把年份能够为4所整除的公历年定为闰年,让这一年有366日。于是,我们先试着用下面这种写法来实现这个判断某年是否为闰年的函数:

在函数体中,我们只要能确认year参数所表示的这个年份必定不是闰年,或者必定是闰年,那就直接用return语句把结果返回给调用方,而不让函数再继续执行。另外要注意,第二个条件表达式(也就是判断year与4相除的余数是否为0的那个表达式)有好几种写法,例如:

在这三种写法里面,第一种最直观,第三种最简省。当然,它们的效果都是相同的。
除了刚才那个函数,我们还要给这个程序编写下面的代码:


在main()函数的主体里面,我们用两种办法打印判断结果,一种是通过if()...else...语句,另一种是通过三元条件运算符,后者更符合C语言的风格。这两种方法都没错。第二种方法里面的%s用来指代某个字符串(也就是用一对双引号括起来的某条字符序列),printf()会把这个字符串放在%s所处的位置上。
新建一份名为leapYear1.c的文件,并将上述代码录入该文件。然后编译并运行程序。你应该会看到类似下面这样的输出结果。

这个程序的结果正确吗?不正确,因为它没有把整百的年份处理好。2000年是闰年,但1900年不是。为什么有一些整百的年份虽然能为4所整除,但却不是闰年呢?这是因为,太阳年其实要比365.25天稍微短一点,也就是相当于365.2425天,换句话说,就是365.25-0.0075天,这可以表示成365+1/4-3/400。于是,这意味着如果每4年设置一个闰年,那么每400年就要多算3天,因此,我们必须把4个整百年份之中的那三个不能为400所整除的年份定为平年。这个函数的正确写法会比刚才看到的稍微复杂一点。这个问题我们在讲完下一节之后再说。
[1] 注意,这次的版本是在num为奇数时返回true。——译者注