400 likes | 535 Views
第四章 逻辑运算和选择控制语句. 四、选择语句 五、 switch 语句语法格式. 四、选择语句 在 C/C++ 中选择语句常用于构成条件分支,选择语句主要 有 两种形式:一种是 if 语句,另一种是 switch 语句,形成顺序开关分支多路选择。 1. 单路分支 if 语句 在 C/C++ 语言中 if 关键字提供了两种基本的控制结构形式。 值得特别强调地说只有两种原始的独立的 if 控制结构形式,其余的形式都是这两种形式的嵌套与灵活的组合。 if 语句圆括号中的表达式可为算术或指针类型,通常是
E N D
第四章 逻辑运算和选择控制语句 四、选择语句 五、switch语句语法格式
四、选择语句 • 在C/C++中选择语句常用于构成条件分支,选择语句主要有两种形式:一种是if语句,另一种是switch语句,形成顺序开关分支多路选择。 • 1.单路分支 if 语句 • 在C/C++语言中if关键字提供了两种基本的控制结构形式。 • 值得特别强调地说只有两种原始的独立的if控制结构形式,其余的形式都是这两种形式的嵌套与灵活的组合。 • if 语句圆括号中的表达式可为算术或指针类型,通常是 • 关系或逻辑表达式,称为条件表达式。
if 非零 复合语句 表达式 零 后续语句 if (表达式) { 语句序列; } 后续语句; (2) 程序流程图 (1) 语法格式 图 if (表达式)语句语法格式与流程
上面的 if 结构称为单路分支选择控制结构。该if语句的 • 执行过程为:if语句首先对条件表达式进行求值计算且完成 • 所有的副作用。若结果为非零值,则执行 if 下的单条语句或 • 花括号包含的复合语句,否则不执行单条语句或复合语句。 • 当语句序列只有一条语句时花括号通常省略不写。 • 条件表达式将非 0 值作为真,将 0 值作为假。 • if 条件表达式的影响范围为最外层花括号包括的语句序 • 列或紧随其后的单条语句。而后续语句是不为 if 选择语句所 • 控制的随后的语句,通常是流程的必经点。 • 可视“if(表达式){语句序列;}”或“if(表达式)语句;”为一 • 条独立的语句。
[例]求三个数的极大值与极小值: • #include<stdio.h> • void main (void) • { int min,max,x,y,z; • scanf ("%d,%d,%d",&x,&y,&z); • min=max=z; • if (x <min) min=x; if(x>max) max=x; • if (y <min) min=y; if(y>max) max=y; • //交互运行的情况如下: • printf ("min=%d,max=%d\n",min,max); • //3,2,7 • } //min=2,max=7
[例]对三个数从小到大排序 • 对两个数排序非常简单,就是交换排序。对于两个数 a,b, • 如果b<a则将其次序交换,其代码为三个赋值语句: t=a; a=b; • b=t; • 对于任意次序的三个数 a,b,c 排序的问题,可以归于两个 • 数的排序问题,其思路是选出三个数中的两个数, 小的前移大 • 的后移,排序时可以存在各种不同的路由判断。下面的函数 • f 先对后面两个变量进行比较。第一次比较的结果较大的变 • 量在第三个位置。第二次用第一次比较后大的结果和第一个 • 变量比,比较之后最大的放置到第三个位置。第三次对前面 • 两个位置的变量比,比较之后最小的放置在前面第一个位 • 置。
#include<stdio.h> • void f (int a,int b,int c) • { int t; • if (c<b) {t=b; b=c; c=t;} • if (c<a) t=a, a=c, c=t; • if (b<a) {t=a; a=b; b=t;} • printf ("%d,%d,%d\t", a,b,c); • } • void main (void) { f(1,0,2), f(9,7, 4); }
2.双路分支if~else语句 • if~else语句结构称为双路分支选择控制结构,在两种不 • 同的动作中做出选择即二者选一。 • if~else语句的执行过程为: • if语句对表达式进行求值计算, 若结果为非零值,则执行 • if下的复合语句1,否则执行复合语句2。两个语句中仅执行一 • 个语句。复合语句仅只有一条语句的时候花括号可以省略。 • if(e)中的表达式e非零分支影响范围是复合语句1,else • 分支即e为零的影响范围是复合语句2。整个“if (表达式)语句 • 1;else 语句2; "语句可视为一条独立的语句。
注意: • 因为语句是表达式跟分号结尾构成的,而这里的语法格 • 式中特地又添写了一个分号,只是强调分号的重要性,if 条 • 件表达式严格精炼地语法格式应写成: • "if(表达式)语句1 else 语句2 " • 或 • "if(表达式)语句"
If ~ else 非零 表达式 复合语句1 零 复合语句2 后续语句 if(表达式) {复合语句1;} else {复合语句2;} 后续语句; (2) 程序流程图 (1) 语法格式 图 [ if(表达式)语句1;else 语句2;]语法格式与流程
考虑到分号本身是一个空语句,如下的语句在程序中是考虑到分号本身是一个空语句,如下的语句在程序中是 • 正确的格式: • if(e) ; statement; // if(e) ; 相当于 e; • “if(e) ; ”视为一条语句, if(e)的控制范围是其后紧跟的 • 语句,此时其后紧跟的语句是一条空语句“;”,语句 • statement则不受if(Expre)的影响,即程序无条件执行语句 • statement。 • 下面的语句比较两数的是否相等: • if(x==y) cout<<"x equal y"<<endl; • else cout<<"x not equal y"<<endl; • if语句可独立运行,else则必须与if成对使用,有一个 • else则必须相应地匹配一个if。
3.if~else语句或if语句的嵌套 • if~else语句或if语句允许嵌套使用,例如 “if(表达式)语 • 句;“中的语句可以是另一个”if(表达式)语句1;else语句2; "。 • if(e1) • if(e2) 语句s1; • else 语句s2; • “ if(e2) s1; else s2; ”语句视为if条件表达式下的内嵌 • 语句。首先执行表达式 e1 的计算,结果为真则执行表达式 • e2的计算,表达式e1结果为假则不执行表达式e2的计算, • 更不执行语句s1或语句s2。语句s1在表达式e1与表达式e2 • 同时为真时得以执行; 语句s2在表达式e1为真与表达式e2 • 为假时得以执行。
花括号用来控制if结构的条件之影响范围。 花括号括起 • 来的语句在C/C++语言中对应一个独立的程序块,因此通过 • 加上花括号可以对if条件的影响范围施加灵活的控制,如: • if(e1) • { if(e2) s1;} • else • s2; • 花括号强制性地限制if(e2)的作用范围局限于花括号控 • 制的程序块,这是值得强调的一个重要编程性质。 • 此时else语句与第一个if语句匹配。
“if(e1) { if(e2) s1;} else s2; ” • 视为if~else双路分支选择结构,其if分支下内嵌 if语句。 • 表达式e1首先完成求值计算,结果为假则执行s2语 • 句。 • 表达式e1结果为真, 则接着进行表达式e2的求值计算, • e2结果为真执行s1语句。 • 值得注意e1结果为真e2结果为假则s1语句与s2语句都 • 不执行。
[例] if表达式的影响区域与求三个数的极大值 • long amax (long x,long y,long z) • { long max=x; • if (y>max) max=y; • if (z>max) max=z; • return max; • } • long bmax (long x,long y,long z) • { long max=x; • if (z>y) { if (z>x) max=z; } • else if (y>x) max=y; • return max ; • }
long errmax (long x,long y,long z) • { long max=x; • if(z>y) if(z>x) max=z; • else if(y>x) max=y; • return max; • } • #include<iostream.h> • void main (void) • { cout<<amax(5,7,4)<<endl; //输出7 • cout<<bmax(5,6,4)<<endl; //输出6 • cout<<errmax(5,6,4)<<endl; //输出5 • cout<<errmax(4,6,5)<<endl; //输出4 • }
上面amax函数求极大值以max为核心,if语句的思路清上面amax函数求极大值以max为核心,if语句的思路清 • 晰嵌套层次少,程序优雅流畅,bmax函数采用if ~else语句 • 求若干数中的极大值没有抓住问题的关键程序略嫌不美。 • errmax函数求极大值时去掉bmax函数 if(z>y)后的花括 • 号, 程序不存在语法错误,但逻辑含义是不同的。对于z<y例 • 如x<z<y或z < x <y的情形语句序列: • max = x; if (z>y) if (z>x) max = z; • else if (y>x) max=y; • 执行的结果是max=x而不是期望的结果max=y。 因此 • 花括号限制if 表达式的影响范围于其内是一个重要的编程特 • 点。
-1 x<0 0 x=0 1 x>0 y=f(x)= • [例]符号函数的多种实现 • 对于上面的函数有几种C函数实现形式: • int f1(int x) int f2(int x) • { { int y; • if (x<0) return -1; if (x<0) y= -1; • if (x==0) return 0; if (x==0) y= 0; • if (x>0) return 1; if (x>0) y= 1; • } return y; • }
int f3 (int x) int f4(int x) • { int y; { int y; • if(x<0) y= -1; if (x<=0) • else if (x==0) y=0; • if(x==0) y=0; else y=-1; • else y= 1; else y= 1; • return y; return y; • } }
同一个三叉分支函数可以对应不同的流程实现,f1 函数 • 直接根据数学上函数的定义进行程序设计,这是直扑问题的 • 解题思路。f2函数引入临时变量转递计算结果,节奏略嫌缓 • 慢。 • 函数 f1 和 f2 中的 if 语句的关系是平行的关系,平行的 • 关系导致判断独立进行,但容易卷入多余的逻辑判断。函数 • f3 和 f4 是 if~else 嵌套关系,两者功能是等价的。 • 嵌套关系构成路由机制,先执行前面的逻辑判断,且最 • 多仅进入其中的一个分支语句。
4.if~else if~else语句 • 前节例题中 f3 函数里 if~else 语句else分支下是另一个 • if~else 语句。 如果 if~else 语句的嵌套全部发生在外层 • if~else语句else分支下,就得到常用的if~else if~else语 • 句。 • 该类型的语句在多个分支中仅执行表达式为非零的那个 • if下的语句,如果所有表达式都为零,则执行最后一个 else • 下的语句。 • if ~ else if~else选择控制结构是if ~ else 双路选择控制 • 结构的常用的派生形式,分支的层层嵌套全发生在else分支 • 下,整个if ~else if~else选择控制结构可视为一条语句,常 • 用于多分支的控制处理中。
但由于前面的 if 语句中的条件表达式事先完成求值判断 • 计算,对于后面的条件表达式构成屏蔽的短路效果;因此在 • 构成程序的路由机制时,注意if ~ else if~else选择控制结构 • 多层嵌套的本质特点,务必仔细安排表达式的先后次序,将 • 多发的重要的事件安排在较外层的 if 分支下,避免函数调用 • 型条件表达式的副作用。 • if ~ else if~else控制结构路由的特点是前面if下的条件 • 为真,后面的分支被跳过;分支越靠前,分支下的代码越被 • 优先处理。这有利于信息轻重缓急的有次序过滤。这一特点 • 是switch语句不具备的。 • 语句[if(e1||e2||e3)s1;]可以等价地展开为[if(e1)s1;else • if(e2)s1; else if(e3) s1;]。
if~else if~else 非零 复合语句1 表达式1 零 非零 复合语句2 表达式2 零 非零 复合语句n 表达式3 零 后续语句 复合语句m if(表达式1) {复合语句1;} else if(表达式2) {复合语句2;} ...... else if(表达式n) {复合语句n;} else {复合语句m;} 后续语句; (1) 语法格式 (2) 程序流程 图 if ~ else if~else 程序流程条件分支
关于if 和if~else结构,下面是值得注意的几点: • if语句下复合语句涉及到的花括号{}的后面不要加分号 • ";",分号";"本身是一条空语句,花括号{}的后面加分号";“ • 以后会构成另外的逻辑语义解释甚至语法错误。 • 在if 语句的嵌套结构中,else 总是与距离其最近的未 • 配对的if匹配,一个else匹配一个if。多层嵌套的情况下, 从 • 最内层一一配对。 • else只能伴生在if语句的条件判断下, if语句则可以独 • 立运行。介入花括号可以对 if语句的条件表达式的影响范围 • 人为地重新定界,有助于灵活地控制程序的流程与走向。
在if语句中不要将关系运算符==与赋值符=混淆。赋值在if语句中不要将关系运算符==与赋值符=混淆。赋值 • 运算符 = 导致左操作数的更新,且表达式的结果可拥有非零 • 的真值,关系运算符==则仅进行逻辑判断而无此副作用。 • 例如: • if(k=5) i=6; 与if(k==5) i=6; • 具有不同的计算结果。 • 前者导致整型变量k再定义为5, i再定义为6, 后者则仅当 • 原先k为5时i才再定义6。 • if(表达式)等价于if(表达式!=0)或if(e)等价于if(e!=0), • 酌情选取两者之一。
5.条件运算符 • C/C++有一个唯一的三目运算符即条件运算符“?:”。条 • 件运算符的优先级别高于赋值运算符与逗号运算符。条件运 • 算符与三个操作数一起构成条件表达式,其语法格式为: • 表达式1 ? 操作数2 :操作数3 e1? e2:e3 • 条件表达式的运算流程根据下面的规则确定: • a.首先执行第一个表达式, 完成所有的副作用。 • b.如果第一个表达式求值为真(一个非0值),第二个操 • 作数求值。 • c.如果第一个表达式求值为假( 一个0值 ),第三个操作 • 数求值。
条件表达式的结果是第二个操作数或第三个操作数的条件表达式的结果是第二个操作数或第三个操作数的 • 值。在条件表达式中后两个操作数只有一个求值。对于表达 • 式嵌套的情况,后面操作数中仅只有一个求值。 • 第一个表达式为算术或指针类型,条件表达式结果的类 • 型取决于第二及第三个操作数转换的共同类型,以下规则用 • 于第二及第三个操作数: • a.如果e2,e3是同类型的,则结果是那个类型的。 • 例如:e2,e3是int型,则表达式是int型。 • 在C++中如果第二和第三个操作数是同类型的左值,则 • 表达式的结果是左值表达式。 在C中条件表达式的结果不是 • 左值。
b.如果e2,e3是算术类型,则执行常用的算术转换以将b.如果e2,e3是算术类型,则执行常用的算术转换以将 • 它们转换为共同的类型。例如e2是short型,e3是double • 型,则表达式的结果是double型,即sizeof(e1>e2:e3)=8。 • c.如果e2或e3作为void类型的函数调用,则运算的结 • 果为无值返回。 • d.允许e2,e3为结构变量或对象,此时e2,e3必须是 • 可以转换为相同数据类型的表达式,这种转换可以通过类型 • 转换函数进行,否则是错误的。 • 例如对于结构声明和定义语句[struct Data {int x;} • s={1};]和整型变量定义[int e =1;] • 表达式e =e1? e2:e3相当于: • if(e1!=0) e= e2; • else e= e3;
第三个操作数可以是另外的条件表达式,形成条件表达第三个操作数可以是另外的条件表达式,形成条件表达 • 式的嵌套。条件运算符是右结合的。结合性用于确定嵌套的 • 操作数绑定的紧密程度,与其间操作数的先后运算次序没有 • 直接的关系。 • 编译器对条件表达式从左到右扫描,寻找三个操作数的 • 匹配。问号“?”之前的表达式是整数(含bool型)、浮点或指 • 针类型,冒号 ":"两侧的类型给出结果的类型。 • 表达式e =e1? e2:e3?e4:e5根据右结合性处理为e =e1? • e2:(e3?e4:e5),效果上相当于: • if(e1) e= e2; • else if(e3) e= e4; • else e= e5;
而表达式e =(e1? e2:e3)?e4:e5类似于左结合性的理 • 解,在效果上相当于: • if(e1) if(e2) e= e4; • else e= e5; • else if(e3) e= e4; • else e= e5; • 条件运算符常用来计算最大值和最小值、或绝对值, • 如: • max=a>b ? a: b; • min=a<b ?a : b; • abs= x>= 0? x:-x;
前节的符号函数可用下面条件表达式的嵌套表示,前节的符号函数可用下面条件表达式的嵌套表示, • 如: • signx= x>0 ? 1: x<0 ?: -1:0; • x为正数,结果为1,x为负数,结果为-1,x为零,结果 • 为0。 • 如果a,b同为算术型的左值变量,则下面的表达式得到a • 与b的最大值减去最小值,接着直接除以480。运算的结果 • 依然是左值表达式。 • ( a>b ? a: b -= a<b ?a : b )/=480; • 三目运算符表达式e1?e2:e3本质上是语句"if(e1) • e=e2;else e=e3;“的一个洗练描述,仅在简单场所替代双路 • 分支语句,其中e表示该表达式的结果。
五、switch语句语法格式 • switch语句构成的多路顺序分支选择是一种简捷的结构 • 控制形式。switch语句的语法格式如下所示,方括号包括的 • 内容表示可以省略的项目,break关键字用于退出switch控 • 制体。break 关键字仅用于终止当前的switch控制体即控制 • 跳转到紧随其后的后续语句。 • switch语句的表达式要求是整型(含字符型或枚举型)不 • 能是浮点型数据。整型常数表达式为编译阶段可以静态求值 • 的整数构成的表达式,相当于case语句的入口。最后一个分 • 支可省略break语句。可用goto 语句把控制转向嵌套结构的 • 外层的标号处。
switch(表达式) • { case 整型常数表达式k:[语句序列k; break;] • case 整型常数表达式j: 语句序列j; [break;] • case 整型常数表达式i: 语句序列 i;[return语句;] • ...... • case 整型常数表达式n: • 语句序列n;[ goto AfterSwitch; ] • [ default: 语句序列m; break;] • } • [AfterSwitch:] • 后续语句; • switch语句的语法格式
switch~case开关分支的执行流程是: • switch语句根据表达式中的值进行分支选择,首先计算 • 出表达式的值,然后用其结果值与各个case后的整型常数表 • 达式的值进行比较判断,测试是否相等,只要找到相等的匹 • 配,就立即跳转到与之匹配的语句序列中进行计算。 • 如果在各整型常数表达式中没有搜索到相等的匹配,则 • 执行default下的语句序列或跳过switch~case控制体若此时 • 程序没提供default语句。
在一个给定的switch语句中,在case语句中的整型常数在一个给定的switch语句中,在case语句中的整型常数 • 表达式的值不能彼此相同,default也仅能出现一次。 • 流程一经进入case分支中,就顺序往下执行分支中的语 • 句,直到遇到某一分支的break 或goto语句或 return语句, • 此时退出该分支;否则,继续往下执行。 • 每一个分支的先后次序不影响优化搜寻的匹配结果。 • 在刻意省略break的情况下,程序的运算状况差异很 • 大。
在case分支下的语句序列中可以引入局部变量,但不能在case分支下的语句序列中可以引入局部变量,但不能 • 引入局部对象。switch的内部语句块新定义的变量须在某一 • 可路过的case分支中。 • 而if语句下的复合语句中可以引入局部变量和局部对 • 象。 在每一分支中引入break语句是稳健的作风。 • “AfterSwitch:”是与goto语句匹配的标号。 • 当switch的复合语句只有一条语句时,可以写为: • switch (e) 整型常量表达式1: 语句1; • 例如:对于[intz=2;]下面的两条语句时等价的: • 1. if(z!=1) printf ("%d \n",z); • 2. switch (z) case 2: printf ("%d \n",z);
[例] switch分支求自然数的和sum=1+2+...+n=n*(n+1)/2 • #include<stdio.h> • long Sumof1toN(int n); • void main(void) • { printf("1+2=%d\n",Sumof1toN(2)); • printf("Sumof 1 to 100=%d\n",Sumof1toN(100)); • } • long Sumof1toN(int n) • { long sum=0; • switch (n) • { case 0: return 0; • case 2+1: sum+=3;
case 2: sum+=2; • case '1'-48: sum+=1;break; • default: sum=n*(n+1)/2; break; • } • return sum; • } • 注意:[case‘1’]等价于[case 49],因为‘1’等于49,应 • 避免此类隐含的标号重名的错误。 • 上面分支属于下落式结构,下落式的特点是case分支之 • 间无break语句打断,分支语句的次序不能自由变动。而下面 • 的函数分支由于每一个case之下布置跳出语句,因而次序可 • 以任意,因此是好的编程风格。
long Sumof1toN(int n) • { long sum=0; • switch (n) • { case 2: sum=2+1; goto AfterSwitch; • case 0: return 0; • case 3: sum=3+2+1; break; • case 1: sum=1; goto AfterSwitch; • default: sum=n*(n+1)/2; break; • } // 常量表达式3+2+1在编译阶段系统会自动静态地 • 计算为6。 • AfterSwitch: return sum; • }