第5章 选择结构程序设计 本章内容提要 (1)关系表达式和逻辑表达式。 (2)if语句。 (3)条件表达式。 (4)switch语句。 第4章讲述的是顺序结构程序设计,顺序结构的程序总是按照由上而下的顺序来执 行。但是,并非所有的问题都可以用顺序结构的程序来解决。有时一个问题的求解需要 根据情况的不同选择不同的处理方式,这就要用到选择结构。 本章先介绍选择结构的必要知识:关系运算和逻辑运算,然后讲解if语句、条件表达 式及switch语句。 5.1 关系运算符和关系表达式 选择结构需要根据条件决定程序的走向,而条件通常是关系表达式或逻辑表达式。 本节先介绍关系运算符和关系表达式。 5.1.1 关系运算符 C语言中的关系运算符有6个:>、>=、<、<=、==、!=。 说明:>=代表≥,<=代表≤,==代表“等于”,!=代表“不等于”。 它们都是双目运算符,结合性都是自左至右。 它们的级别都比算术运算符低,都比赋值运算符和逗号运算符高。6个运算符中, >、>=、<、<=的级别相同,==、!=的级别相同,前4个级别高于后两个。 说明:要详细了解各种运算符的优先级别,可参阅附录E。 5.1.2 关系表达式 1.关系表达式 如果一个表达式最后进行的是关系运算,则该表达式就是关系表达式。 关系运算 符和关系 表达式 第5章 选择结构程序设计 77 下面几个表达式中,前3个是关系表达式,第4个是赋值表达式,最后一个是逗号表 达式。 c>a+b //运算符由高到低的顺序是+、> a>b==c //运算符由高到低的顺序是>、== a==b<c //运算符由高到低的顺序是<、== a=b>c //运算符由高到低的顺序是>、= a>b==c,a=b!=c //运算符由高到低的顺序是>、==和!=、=、, 2.关系表达式的值 关系表达式的值是一个逻辑值,数学中用“真”“假”来表示,C语言中用1和0来表 示。当表达式成立时,其值为1;当表达式不成立时,其值为0。 设变量a、b、c的值分别是1、2、3,则下面几个表达式的值分别如注释中所示。 c>=a+b //关系表达式,值为1 a>b==c //关系表达式,值为0 a==b<c //关系表达式,值为1 a=b>c //赋值表达式,值为0,因a 的值为0,a 为何为0? 因b>c 不成立 a>b==c,a=b!=c //逗号表达式,值为1,执行后a 的值为1 f=c>b>a //赋值表达式,值为0,因c>b>a 的结果为0 需要指出的是,表达式的求解,一次只能进行一个运算符的运算,例如: c>b>a 相当于是: (c>b)>a 求解时要先算c>b,得到一个结果(结果是1),然后再拿这个结果与a比较,即1>a, 显然不成立,故c>b>a的结果是0(假)。 注意:计算c>b>a时,最后与a比较的是c>b的结果,而不是b。 考考你:表达式3==3==3的值是多少? 5.2 逻辑运算符和逻辑表达式 一个复杂的条件往往是由几个简单的条件组成的,这些简单条件之间通常用逻辑运 算符来连接,从而构成逻辑表达式。 5.2.1 逻辑运算符 C语言中的逻辑运算符有3个:&&(逻辑与)、||(逻辑或)、!(逻辑非)。 逻辑与(&&)是“并且”的意思,表示只有两边的条件都成立,“与”的结果才是1。 例如: 逻辑运算 符和逻辑 表达式 78 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) a>=b&&a>=c //命题: a 是最大的 逻辑或(||)是“或者”的意思,表示只要有一个条件成立,结果就是1。例如: a>b||a>c //命题: a 不是最小的 图5-1 运算符的优先级 逻辑非(!)是“否定”“取反”的意思,如果表达式原来是 “真”(1),取反之后就是“假”(0);如果表达式原来是“假”,则 取反后就是“真”。例如: !(5>3) //值为0 !(2==1) //值为1 上面两个逻辑表达式的值分别是0、1。 && 和||都是双目运算符,! 是单目运算符。它们的优 先级别如图5-1所示。 5.2.2 逻辑表达式 1.逻辑表达式及其逻辑值 如果一个表达式最后进行的是逻辑运算,那么该表达式就是逻辑表达式。逻辑表达 式的值也是逻辑值:真或假,如前所述,C语言中用1和0表示真和假。 2.逻辑运算符的运算对象 逻辑表达式中,参与逻辑运算的运算对象通常都是条件表达式,例如,判断一个年份 是否闰年的表达式: year%4==0&&year%100!=0||year%400==0 但实际上,任何表达式都可以参与逻辑运算,例如: 'A'&&b=3 !3&&5-2||'A'&&(a=1,b=2,a+b)&&(a=b) 说明:不管什么类型的表达式,当它参与逻辑运算时,用的都是它的逻辑值。 那么,怎么确定这些表达式的逻辑值? 例如,'A'的逻辑值是真还是假? b=3的逻辑 值又是什么? C语言中,任何表达式(包括变量、常量)都有逻辑值,只要表达式的值非0,则认为它 的逻辑值为“真”(即1);只有表达式的值为0时,其逻辑值才为“假”(即0)。例如: (1)3+2的值是5,若参与逻辑运算,则取它的逻辑值1。 (2)-5参与逻辑运算时,逻辑值也是1。 (3)若“floatx=1.2;”,则x的逻辑值为1。 (4)!'A'的逻辑值为0。 (5)x=-2的逻辑值为1。 第5章 选择结构程序设计 79 (6)设a、b都是int型变量,则(a=1,b=2,a-b+1)的逻辑值是0。 (7)5>3&&8<4-!0的逻辑值为0。 考考你:表达式!3&&5-2||'A'&&(a=1,b=2,a+b)&&(a=b)的值是多 少? 表达式(a=b=c=3)&&a==b==c的值又是多少? 3.逻辑表达式求解时的短路效应 逻辑与(&&)和逻辑或(||)的两侧都各有一个式子,在求解包含&& 或||的表达式 时,都是先计算运算符左边的式子,一旦能确定整个表达式的值,右边就不再求解了。 例如: int a=1,b=10,c=2; printf("%d,", (a=b)||(c=b)); printf("%d,%d,%d\n",a,b,c); 运行结果: 1,10,10,2 可以看到,c的值还是原来的2,说明c=b并没有被执行。 原因:表达式(a=b)||(c=b)的计算顺序是先求解||左侧的a=b,其值是10(因a=b 是赋值),其逻辑值是1,由于||左侧已经为真,整个逻辑表达式的值已经可以确定,右侧 c=b不需要再求解了(这称为短路效应),故c的值是原来的2不变。 同理,对于&& 运算符,一旦左侧为0,也不需要求解右侧的值(短路效应)。 编程经验:在编写包含运算符&& 的表达式时,把最有可能为假的简单条件写 在表达式的最左边,在编写包含运算符||的表达式时,把最有可能为真的简单条件写在表 达式的最左边,这样做有助于减少程序的运行时间,提高程序的效率。 5.3 if 语 句 选择结构可由两个语句来实现:if语句和switch语句,本节介绍if语句。 5.3.1 if 语句的格式 if语句的格式如下: if(表达式) //本行没有分号 语句1 [else 语句2] 说明:[]表示其中的内容可以省略,即整个else分支可以省略。 注意:整个if语句格式所表示的部分,是一条语句。 if语句的两 种格式 80 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) if语句的执行过程:首先求解表达式的逻辑值,若为真,则执行语句1,否则执行语句 2(若无else分支,则什么也不做)。 由if语句构成的选择结构使程序的走向形成了两个分支,程序执行时只能根据条件 (表达式)是否成立选择其中一个分支,其流程如图5-2所示。 图5-2 if语句流程图 例5.1 从键盘输入两个整数,若a>b则计算a-b的 值,否则计算a+b的值。 #include <stdio.h> int main() { int a,b,result; scanf("%d%d",&a,&b); if(a>b) result=a-b; else result=a+b; printf("%d\n", result); return 0; } 5.3.2 if 语句的使用说明 1.if后面括号中的表达式可以是任何类型的表达式 if后面括号中的表达式,可以是逻辑表达式、关系表达式、算术表达式等,还可以是常 量或变量。不管什么类型的表达式,都取它的逻辑值,例如: if(a>b) if(a==b) if(a!=0 && b!=1) if(1) //相当于if(1!=0),这个条件总是成立的 if(x) //相当于if(x!=0) if(a=b) //相当于if((a=b)!=0) if(a/10) //相当于if((a/10)!=0) 例5.2 从键盘输入一个整数,若不为0,则输出1;若为0,则输出0。 程序代码如下: #include <stdio.h> int main() { int a; scanf("%d",&a); if(a) //相当于if(a!=0) printf("1\n"); else 第5章 选择结构程序设计 81 printf("0\n"); return 0; } 2.if或else后面只能跟一条语句 语法上,if或else,都只能“管”一条语句,这条语句称为if的子句或else的子句。如 果程序中需要if或else“管”多条语句,则必须将它们放在花括号内组成一条复合语句。 例5.3 从键盘输入两个整数,若都是偶数,则各自减半后输出它们,否则各自乘以2 后输出它们。 程序代码如下: #include <stdio.h> int main() { int a,b; scanf("%d%d",&a,&b); if(a%2==0&&b%2==0) { a/=2; b/=2; } else { a*=2; b*=2; } printf("%d,%d\n",a,b); return 0; } 提示:C语言中,任何能放置一条语句的地方,都可以放置一条复合语句。 说明:书写代码时,if和else的子句,相对于if和else,要缩进4个空格或一个 Tab键跳过的距离,以示它们被if或else管辖。同一级别的语句,缩进的距离应该相同。 代码的书写采用缩进格式是为了使程序清晰美观,缩进格式对编译没有影响,编译时这些 空白都将被忽略。 试一试:把例5.3代码中else子句中的花括号去掉,运行程序输入2和4,看结果是 多少,为什么是这个结果? 单步调试找出原因。若把if子句中的花括号去掉,又会怎样? 3.if和else的子句可以是空语句 当条件成立(或不成立)不需要做任何处理时,就可以写一个空语句。 例5.4 从键盘输入一个整数,求其绝对值。 代码可以写成下面两种形式。 82 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) #include <stdio.h> int main() { int a; scanf("%d",&a); if(a>=0) ; //不做任何处理 else a=-a; printf("%d\n",a); return 0; } 或 #include <stdio.h> int main() { int a; scanf("%d",&a); if(a<0) a=-a; else ; //不做任何处理 printf("%d\n",a); return 0; } 想一想:上面两段代码中,各有一条空语句(分号),表示什么都不做,这两个分号 可否删掉不要? 想一想:有些人编程,喜欢把上面代码中的空语句写成“a=a;”,即: if(a<0) a=-a; else a=a; //该语句的执行过程是什么? 让计算机做这个操作有没有意义? 或 if(a>=0) a=a; //该语句的执行过程是什么? 让计算机做这个操作有没有意义? else a=-a; 4.if语句可以没有else分支 当条件不成立不需要做任何事情时,可以省略else分支,例如,例5.4中右侧的代码 就可以简化为 #include <stdio.h> int main() { int a; scanf("%d",&a); if(a<0) a=-a; printf("%d\n",a); return 0; } 第5章 选择结构程序设计 83 编程经验:任何一个条件其实都有两种写法,例如,若条件能写成a>b,那么就 一定可以写成a<=b;如果可以写成a==b,肯定也可以写成a!=b,当条件反过来写 时,if和else的子句需要互换,如例5.4中的代码所示。两种写法中,一般会有一种写法 能使后面的代码更简单、更容易实现。编程过程中当需要写条件时,通常要先在脑子里把 两种写法比较一下,选择使代码简单的那一种。 注意:像上面这种代码,省略else分支不影响程序的运行结果,就一定要省略。 那些无意义的代码即便自己愿意写,别人看起来也会觉得累。 5.3.3 嵌套的if 语句 if语句中,在if子句或else子句的位置上,都可以再使用一条if语句,使得if语句嵌套。 一条if语句最多只能处理两种情况,嵌套的if语句可用来处理3种以上的情况。 一般来说,若有3种情况,则需要2条if语句嵌套;若有4种情况,则需要3条if语句 嵌套;……;若有n 种情况,则需用n-1条if语句嵌套。if的嵌套可以有多层。 常见的嵌套方式有如下4种: if(表达式1) if(表达式2) … [else …] if(表达式1) if(表达式2) … else … [else …] if(表达式1) if(表达式2) … else … else if(表达式3) … [else …] if(表达式1) … else if(表达式2) … [else if(表达式3) … [else …]] 说明:嵌套的if语句的书写风格,应该是else跟与它配对的那个if对齐,各个子句也 应该跟与它同级别的其他子句对齐,以使程序结构清晰,易于理解。 编程经验:编程时,通常都是使用最后一种嵌套方式,即第一个if语句只处理一 种情况,把剩下的情况都放在else后面处理。用这种方式的好处是可以避免if和else配 对错误。 下面的程序段用来求解y的值:当x>0时,y=1;当x=0时,y=0;当x<0时,y=-1。 程序中的if语句原本应该是右侧的代码,因为其中一个else分支什么都不做,所以被省 略了,但由于使用的嵌套方式不当,当程序省略else分支时,运行结果出现了错误。 #include <stdio.h> int main() { int x,y=0; scanf("%d",&x); 嵌套的if语 句及编程 的常见问 题 84 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) if(x>=0) if(x>0) y=1; else y=-1; 省略else 分支 if(x>=0) if(x>0) y=1; else ; else y=-1; printf("%d\n",y); return 0; } 运行程序,当输入0时,结果如下: 这段代码中的else看起来是跟第一个if配对,实际上它是跟第二个if配对。因为C 语言规定:else总是与它前面最近的、尚未配对的if配对。 说明:else和if的配对关系与else跟哪个if对齐无关,即不要以为else跟哪个 if对齐它就跟哪个if配对,缩进只是为了方便阅读,编译时所有缩进的空白都被忽略。 在编译器看来,上面的if语句相当于是这样写的: if(x>=0) if(x>0) y=1; else y=-1; 若采用最后一种嵌套方式,让第一个if只处理其中一种情况,把剩下的情况都放在 else后面处理,就不会出现配对错误的问题。 if(x>0) y=1; else if(x<0) y=-1; 说明:这段代码的最后也省略了else分支。 例5.5 判断一个学生成绩属于哪个分数段。程序代码如下: #include <stdio.h> int main() { int x; scanf("%d", &x); if(x>=90) printf("优"); 第5章 选择结构程序设计 85 else if(x>=80) printf("良"); else if(x>=70) printf("中"); else if(x>=60) printf("及格"); else printf("差"); return 0; } 本例用的也是前面推荐的嵌套方式。 想一想:为什么代码中“if(x>=80)printf("良");”中的条件是“x>=80”而不 是“x>=80&&x<90”? 若运行时输入的成绩是95,运行结果会不会是“良”或“优良”或 “优良中及格差”? 提示:在if(x>=80)前面有个else。 编程经验:尽量简化条件,能省则省,可以缩短表达式的求解时间。 有些编程者喜欢把上面的代码写成下面的格式,但写成下面的格式后,程序在逻辑关 系上不如上面的清晰,故不推荐使用。 #include <stdio.h> int main() { int x; scanf("%d", &x); if(x>=90) printf("优"); else if(x>=80) printf("良"); else if(x>=70) printf("中"); else if(x>=60) printf("及格"); else printf("差"); return 0; } 5.3.4 if 语句应用举例 例5.6 从键盘输入4个整数,找出其中的最大值。