第5章 选择结构程序设计 在第2章中已介绍了选择结构,它是3种基本结构之一。在大多数程序中都会包含选择结构。它的作用是,根据所指定的条件是否满足,决定从给定的两组操作选择其一。在本章中介绍如何用C语言实现选择结构。 在C语言中选择结构是用if语句实现的。if语句最常用的形式如下: if (关系表达式)语句1 else 语句2 例如: if ( x>0 ) y=1; else y=-1;其中x>0是一个关系表达式; ">”是一个关系运算符。 5.1 关系运算符和关系表达式 关系运算是逻辑运算中比较简单的一种。所谓“关系运算”实际上是“比较运算”。将两个值进行比较,判断其比较的结果是否符合给定的条件。例如,a>3是一个关系表达式,大于号(>)是一个关系运算符,如果a的值为5,则满足给定的“a>3”条件,因此关系表达式的值为“真”(即“条件满足”) ;如果a的值为2,不满足“a>3”条件,则称关系表达式的值为“假”. 5.1.1 关系运算符及其优先次序 C语言提供6种关系运算符: ① < (小于) ② <= (小于或等于) ③ > (大于) ④ >= (大于或等于) 优先级相同 (高) ⑤ == (等于) ⑥ != (不等于)优先级相同 (低) 关于优先次序: (1) 前4种关系运算符(<, <=, >, >=)的优先级别相同,后2种也相同。前4种高于后2种。例如, ">”优先于“==" 。而“>”与“<”优先级相同。 (2) 关系运算符的优先级低于算术运算符。 (3) 关系运算符的优先级高于赋值运算符。 以上关系见图5-1. 例如: 图 5-1c>a+b 等效于 c> (a+b) a>b==c 等效于 (a>b) ==c a==b<c 等效于 a== (b<c) a=b>c 等效于 a= (b>c) 5.1.2 关系表达式 用关系运算符将两个表达式(可以是算术表达式或关系表达式、逻辑表达式、赋值表达式、字符表达式)连接起来的式子,称关系表达式。例如,下面都是合法的关系表达式: a>b,a+b>b+c, (a=3) > (b=5) , ′a′<′b′, (a>b) > (b<c) 关系表达式的值是一个逻辑值,即“真”或“假”。例如,关系表达式“5==3”的值为“假”, "5>=0”的值为“真”. C语言没有逻辑型数据(C++有逻辑型变量和逻辑型常量,以true表示“真”,以false表示“假”) 。在C的逻辑运算中,以“1”代表“真”,以“0”代表“假”。例如,a=3, b=2, c=1,则:  关系表达式“a>b”的值为“真”,表达式的值为1.  关系表达式“ (a>b) ==c”的值为“真”(因为a>b的值为1,等于c的值),表达式的值为1.  关系表达式“b+c<a”的值为“假”,表达式的值为0. 如果有以下赋值表达式:  d=a>b 则d的值为1.  f=a>b>c 则f的值为0 (因为“>”运算符是自左至右的结合方向,先执行 "a>b" 得值为1, 再执行关系运算“1>c" ,得值0,赋给f). 5.2 逻辑运算符和逻辑表达式 用逻辑运算符将关系表达式或逻辑量连接起来的式子就是逻辑表达式。在BASIC和Pascal语言中有以下形式的逻辑表达式(AND是逻辑运算符): (a>b) AND (x>y) 如果a>b且x>y,则上述逻辑表达式的值为“真”。下面介绍C语言中的逻辑运算符和逻辑运算。 5.2.1 逻辑运算符及其优先次序 C语言提供3种逻辑运算符: (1) && 逻辑与 (相当于其他语言中的AND) (2) ||逻辑或 (相当于其他语言中的OR) (3) ! 逻辑非 (相当于其他语言中的NOT) "&&”和“||”是“双目(元)运算符”,它要求有两个运算量(操作数),如(a>b) && (x>y) , (a>b) || (x>y) . "!”是“一目(元)运算符”,只要求有一个运算量,如 ! (a>b) . 逻辑运算举例如下: a&&b 若a、b为真,则a&&b为真。 a||b 若a、b之一为真,则a||b为真。 !a 若a为真,则!a为假。表5-1为逻辑运算的“真值表”。用它表示当a和b的值为不同组合时,各种逻辑运算所得到的值。表 5-1 逻辑运算的真值表 ab!a!ba&&ba||b真真假假真真真假假真假真假真真假假真假假真真假假 在一个逻辑表达式中如果包含多个逻辑运算符,例如: !a&&b||x>y&&c图 5-2按以下的优先次序: (1) !(非)→&&(与)→||(或), 即“!”为三者中最高的。 (2) 逻辑运算符中的“&&”和“||”低于关系运算符, "!”高于算术运算符,见图5-2. 例如: (a>b) && (x>y) 可写成a>b&&x>y (a==b) || (x==y) 可写成a==b||x==y (!a) || (a>b) 可写成 !a||a>b5.2.2 逻辑表达式 如前所述,逻辑表达式的值应该是一个逻辑量“真”或“假”. C语言编译系统在表示逻辑运算结果时,以数值1代表“真”,以0代表“假”,但在判断一个量是否为“真”时,以0代表“假”,以非0代表“真”。即将一个非零的数值认作为“真”。例如: (1) 若a=4,则!a的值为0。因为a的值为非0,被认作“真”,对它进行“非”运算,得“假”, “假”以0代表。 (2) 若a=4, b=5,则a&&b的值为1。因为a和b均为非0,被认为是“真”,因此a&&b的值也为“真”,值为1. (3) a、b值分别为4、5, a||b的值为1. (4) a、b值分别为4、5, !a||b的值为1. (5) 4&&0||2的值为1. 通过这几个例子可以看出,由系统给出的逻辑运算结果不是0就是1,不可能是其他数值。而在逻辑表达式中作为参加逻辑运算的运算对象(操作数)可以是0 (“假”)或任何非0的数值(按“真”对待)。如果在一个表达式中不同位置上出现数值,应区分哪些是作为数值运算或关系运算的对象,哪些作为逻辑运算的对象。例如: 5>3 && 8<4-!0表达式自左至右扫描求解。首先处理“5>3" (因为关系运算符优先于&&) 。在关系运算符两侧的5和3作为数值参加关系运算, "5>3”的值为1(代表真)。再进行 "1 && 8<4-!0”的运算,8的左侧为“&&" ,右侧为“<”运算符,根据优先规则,应先进行“<”的运算,即先进行8<4-!0”的运算。现在4的左侧为“<" ,右侧为“-”运算符,而“-”优先于“<" ,因此应先进行“4-!0”的运算,由于“!”的级别最高,因此先进行“!0”的运算,得到结果1。然后进行“4-1”的运算,得到结果3,再进行“8<3”的运算,得0,最后进行“1&&0”的运算,得0. 实际上,逻辑运算符两侧的运算对象不但可以是0和1,或者是0和非0的整数,也可以是字符型、实型或指针型等。系统最终以0和非0来判定它们属于“真”或“假”. 例如: ′c′ && ′d′的值为1(因为′c′和′d′的ASCII值都不为0,按“真”处理),所以1 && 1的值为1. 可以将表5-1改写成表5-2形式。表5-2 逻辑运算的真值表 ab!a!ba && ba || b非0非00011非0001010非01001001100 在逻辑表达式的求解中,并不是所有的逻辑运算符都被执行,只是在必须执行下一个逻辑运算符才能求出表达式的解时,才执行该运算符。举例如下。 (1) a && b && c 只有a为真(非0)时,才需要判别b的值,只有a和b都为真的情况下才需要判别c的值。只要a为假,就不必判别b和c(此时整个表达式已确定为假)。如果a为真,b为假,不判别c,见图5-3. (2) a||b||c 只要a为真(非0) ,就不必判断b和c。只有a为假,才判别b. a和b都为假才判别c,见图5-4. 也就是说,对&&运算符来说,只有a≠0,才继续进行右面的运算。对||运算符来说,只有a=0,才继续进行其右面的运算。因此,如果有下面的逻辑表达式:图 5-3图 5-4 (m=a>b) && (n=c>d) 当a=1, b=2, c=3, d=4, m和n的原值为1时,由于“a>b”的值为0,因此m=0,而“n=c>d”不被执行,因此n的值不是0而仍保持原值1。这点请读者注意。 熟练掌握C语言的关系运算符和逻辑运算符后,可以巧妙地用一个逻辑表达式来表示一个复杂的条件。 例如,要判别用year表示的某一年是否闰年。闰年的条件是符合下面二者之一:①能被4整除,但不能被100整除,如2008. ②能被4整除,又能被400整除,如2000。可以用一个逻辑表达式来表示: (year%4==0 && year%100!=0) ||year%400==0 当year为某一整数值时,如果上述表达式值为真(1) ,则year为闰年;否则year为非闰年。 可以加一个“!”用来判别非闰年: ! ( (year%4==0 && year%100!=0) ||year%400==0) 若此表达式值为真(1) , year为非闰年。也可以用下面逻辑表达式判别非闰年: (year%4!=0) || (year%100==0 && year%400!=0) 若表达式值为真,year为非闰年。请注意表达式中右面的括号内的不同运算符(%, !=, &&, ==)的运算优先次序。 5.3 if 语 句 if语句是用来判定所给定的条件是否满足,根据判定的结果(真或假)决定执行给出的两种操作之一。 5.3.1 if语句的3种形式 C语言提供了3种形式的if语句。 1. if(表达式) 语句 例如: if (x>y) printf (" %d" , x) ; 这种if语句的执行过程见图5-5 (a) . 图 5-5 2. if(表达式)语句1 else语句2 例如: if (x>y) printf (" %d" , x) ; else printf (" %d" , y) ; 见图5-5 (b) . 3. if(表达式1) 语句1 else if(表达式2)语句2 else if(表达式3) 语句3  else if(表达式m) 语句m else语句n 流程图见图5-6. 图 5-6 例如: if (number>500) cost=0.15; else if (number>300) cost=0.10; else if (number>100) cost=0.075; else if (number>50) cost=0.05; else cost=0; 说明: (1) 3种形式的if语句中在if后面都有表达式,一般为逻辑表达式或关系表达式。例如: if (a==b && x==y) printf (" a=b, x=y" ) ; 在执行if语句时先对表达式求解,若表达式的值为0,按“假”处理,若表达式的值为非0,按“真”处理,执行指定的语句。假如有以下if语句: if (3) printf (" O.K." ) ; 是合法的,执行结果输出“O.K." ,因为表达式的值为3,按“真”处理。由此可见,表达式的类型不限于逻辑表达式,可以是任意的数值类型(包括整型、实型、字符型、指针型数据)。例如,下面的if语句也是合法的: if (′a′) printf (" %d" , ′a′) ; 执行结果:输出′a′的ASCII码97. (2) 第二、第三种形式的if语句中,在每个else前面有一分号,整个语句结束处有一分号。例如:if (x>0) else各有一个分号;print (" %f" , x); printf(" %f" , -x);这是由于分号是C语句中不可缺少的部分,这个分号是if语句中的内嵌语句所要求的。如果无此分号,则出现语法错误。 注意: 不要误认为上面是两个语句(if语句和else语句)。它们都属于同一个if语句。else子句不能作为语句单独使用,它必须是if语句的一部分,与if配对使用。 (3) 在if和else后面可以只含一个内嵌的操作语句(如上例),也可以有多个操作语句,此时用花括号“{}”将几个语句括起来成为一个复合语句。例如: if (a+b>c && b+c>a && c+a>b) { s=0.5*(a+b+c); area=sqrt(s*(s-a)*(s-b)*(s-c)); printf (" area=%6.2f" , area) ; } else printf (" it is not a trilateral" ) ; 注意在else上面的花括号“}”外面不需要再加分号。因为{}内是一个完整的复合语句,不需另附加分号。 例5.1 输入两个实数,按代数值由小到大的顺序输出这两个数。 这个问题的算法很简单,只需要做一次比较即可。对类似这样简单的问题可以不必先写出算法或画流程图,而直接编写程序。或者说,算法在编程者的脑子里,相当于在算术运算中对简单的问题可以“心算”而不必在纸上写出来一样。 程序如下: #include void main () { float a, b, t; scanf (" %f, %f" , &a, &b) ; if (a>b) { t=a; a=b; b=t; } printf (" %5.2f, %5.2f\\n" , a, b) ; }运行情况如下: 3.6, -3.2↙ -3.20, 䦂MT Extra|Ap3.60例5.2 输入3个数a、b、c,要求按由小到大的顺序输出。 解此题的算法比上一题稍复杂一些。可以用伪代码写出算法: if a > b 将a和b对换 (a是a、b中的小者) if a > c 将a和c对换 (a是a、c中的小者,因此a是三者中最小者) if b > c 将b和c对换 (b是b、c中的小者,也是三者中次小者) 然后顺序输出a、b、c即可。 按此算法编写程序: #include void main () { float a, b, c, t; scanf (" %f, %f, %f" , &a, &b, &c) ; if (a>b) { t=a; a=b; b=t; } /* 实现a和b的互换 */ if (a>c) { t=a; a=c; c=t; } /* 实现a和c的互换 */ if (b>c) { t=b; b=c; c=t; } /* 实现b和c的互换 */ printf (" %5.2f, %5.2f, %5.2f\\n" , a, b, c) ; }运行情况如下: 3, 7, 1↙ 䦂MT Extra|Ap1.00, 䦂MT Extra|Ap3.00, 䦂MT Extra|Ap7.005.3.2 if语句的嵌套 在if语句中又包含一个或多个if语句称为if语句的嵌套。一般形式如下: if() if() 语句1 else语句2内嵌if else if() 语句3 else语句4内嵌if 应当注意if与else的配对关系。else总是与它上面的最近的未配对的if配对。假如写成: if() if()语句1 else if() 语句2 else 语句3内嵌if编程序者把else写在与第一个if(外层if)同一列上,希望else与第一个if对应,但实际上else是与第二个if配对,因为它们相距最近。因此最好使内嵌if语句也包含else部分(如5.3.2小节最早列出的形式),这样if的数目和else的数目相同,从内层到外层一一对应,不致出错。 如果if与else的数目不一样,为实现程序设计者的企图,可以加花括号来确定配对关系。例如: if () { if () 语句1 }内嵌if else 语句2 这时“{ }”限定了内嵌if语句的范围,因此else与第一个if配对。 例5.3 有一函数: 图 5-7y=-1(x<0) 0(x=0) 1(x>0) 编一程序,输入一个x值,输出y值。 可以先写出算法: 输入x 若 x < 0,则y =-1 若 x = 0,则y = 0 若 x > 0,则y = 1 输出y 或: 输入x 若 x < 0,则y = -1 否则: 若 x = 0,则y = 0 若 x > 0,则y = 1 输出y 也可以用流程图表示,见图5-7. 有以下几个程序,请读者判断哪个是正确的?