1
四、选择语句五,switch语句语法格式
2
四、选择语句在 C/C++中选择语句常用于构成条件分支,选择语句主要 有 两种形式:一种是 if语句,另一种是 switch语句,形成顺序开关分支多路选择。
1.单路分支 if 语句在 C/C++语言中 if关键字提供了两种基本的控制结构形式。
值得特别强调地说只有两种原始的独立的 if控制结构形式,其余的形式都是这两种形式的嵌套与灵活的组合。
if 语句圆括号中的表达式可为算术或指针类型,通常是关系或逻辑表达式,称为条件表达式。
3
后续语句复合语句
if
非零零表达式if (表达式 )
{ 语句序列 ;}
后续语句;
(1) 语法格式 (2) 程序流程图图 if (表达式)语句语法格式与流程
4
上面的 if 结构称为单路分支选择控制结构。该 if语句的执行过程为,if语句首先对条件表达式进行求值计算且完成所有的副作用。若结果为非零值,则执行 if 下的单条语句或花括号包含的复合语句,否则不执行单条语句或复合语句。
当语句序列只有一条语句时花括号通常省略不写。
条件表达式将非 0 值作为真,将 0 值作为假。
if 条件表达式的影响范围为最外层花括号包括的语句序列或紧随其后的单条语句。而后续语句是不为 if 选择语句所控制的随后的语句,通常是流程的必经点。
可视,if(表达式) {语句序列 ;}”或,if(表达式)语句 ;”为一条独立的语句。
5
[例 ] 求三个数的极大值与极小值,
#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
6
[例 ] 对三个数从小到大排序对两个数排序非常简单,就是交换排序。对于两个数 a,b,
如果 b<a则将其次序交换,其代码为三个赋值语句,t=a; a=b;
b=t;
对于任意次序的三个数 a,b,c 排序的问题,可以归于两个数的排序问题,其思路是选出三个数中的两个数,小的前移大的后移,排序时可以存在各种不同的路由判断。下面的函数
f 先对后面两个变量进行比较。第一次比较的结果较大的变量在第三个位置。第二次用第一次比较后大的结果和第一个变量比,比较之后最大的放置到第三个位置。第三次对前面两个位置的变量比,比较之后最小的放置在前面第一个位置。
7
#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); }
8
2.双路分支 if~else语句
if~else语句结构称为双路分支选择控制结构,在两种不同的动作中做出选择即二者选一。
if~else语句的执行过程为:
if语句对表达式进行求值计算,若结果为非零值,则执行
if下的复合语句 1,否则执行复合语句 2。两个语句中仅执行一个语句。复合语句仅只有一条语句的时候花括号可以省略。
if(e)中的表达式 e非零分支影响范围是复合语句 1,else
分支即 e为零的影响范围是复合语句 2。整个,if (表达式 )语句
1;else 语句 2; "语句可视为一条独立的语句。
9
注意:
因为语句是表达式跟分号结尾构成的,而这里的语法格式中特地又添写了一个分号,只是强调分号的重要性,if 条件表达式严格精炼地语法格式应写成:
"if(表达式)语句 1 else 语句 2 "

"if(表达式)语句 "
10
if(表达式 )
{复合语句 1;}
else
{复合语句 2;}
后续语句;
If ~ else
后续语句复合语句 1
复合语句 2
零非零表达式
(1) 语法格式 (2) 程序流程图图 [ if(表达式)语句 1; else 语句 2; ]语法格式与流程
11
考虑到分号本身是一个空语句,如下的语句在程序中是正确的格式:
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。
12
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
为假时得以执行。
13
花括号用来控制 if结构的条件之影响范围。 花括号括起来的语句在 C/C++语言中对应一个独立的程序块,因此通过加上花括号可以对 if条件的影响范围施加灵活的控制,如:
if(e1)
{ if(e2) s1; }
else
s2;
花括号强制性地限制 if(e2)的作用范围局限于花括号控制的程序块,这是值得强调的一个重要编程性质。
此时 else语句与第一个 if语句匹配。
14
“if(e1) { if(e2) s1;} else s2;,
视为 if~else双路分支选择结构,其 if分支下内嵌 if语句。
表达式 e1首先完成求值计算,结果为假则执行 s2语句。
表达式 e1结果为真,则接着进行表达式 e2的求值计算,
e2结果为真执行 s1语句。
值得注意 e1结果为真 e2结果为假则 s1语句与 s2语句都不执行。
15
[例 ] 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 ;
}
16
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
}
17
上面 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 表达式的影响范围于其内是一个重要的编程特点。
18
[例 ] 符号函数的多种实现对于上面的函数有几种 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;
}
y=f(x)=
-1 x<0
0 x=0
1 x>0
19
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;
} }
20
同一个三叉分支函数可以对应不同的流程实现,f1 函数直接根据数学上函数的定义进行程序设计,这是直扑问题的解题思路。 f2函数引入临时变量转递计算结果,节奏略嫌缓慢。
函数 f1 和 f2 中的 if 语句的关系是平行的关系,平行的关系导致判断独立进行,但容易卷入多余的逻辑判断。函数
f3 和 f4 是 if~else 嵌套关系,两者功能是等价的。
嵌套关系构成路由机制,先执行前面的逻辑判断,且最多仅进入其中的一个分支语句。
21
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选择控制结构可视为一条语句,常用于多分支的控制处理中。
22
但由于前面的 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;]。
23
if(表达式 1)
{复合语句 1;}
else if(表达式 2)
{复合语句 2;}
......
else if(表达式 n)
{复合语句 n;}
else
{复合语句 m;}
后续语句;
if~else
if~else
后续语句复合语句 m
复合语句 n
复合语句 1
复合语句 2
零非零表达式 1
零非零表达式 2
零非零表达式 3
(1) 语法格式 (2) 程序流程图 if ~ else if~else 程序流程条件分支
24
关于 if 和 if~else结构,下面是值得注意的几点:
if语句下复合语句涉及到的花括号 {}的后面不要加分号
";",分号 ";"本身是一条空语句,花括号 {}的后面加分号 ";“
以后会构成另外的逻辑语义解释甚至语法错误。
在 if 语句的嵌套结构中,else 总是与距离其最近的未配对的 if匹配,一个 else匹配一个 if。多层嵌套的情况下,从最内层一一配对。
else只能伴生在 if语句的条件判断下,if语句则可以独立运行。介入花括号可以对 if语句的条件表达式的影响范围人为地重新定界,有助于灵活地控制程序的流程与走向。
25
在 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),
酌情选取两者之一。
26
5.条件运算符
C/C++有一个唯一的三目运算符即条件运算符,?:”。
条件运算符的优先级别高于赋值运算符与逗号运算符。条件运算符与三个操作数一起构成条件表达式,其语法格式为:
表达式 1? 操作数 2,操作数 3 e1? e2:e3
条件表达式的运算流程根据下面的规则确定:
a,首先执行第一个表达式,完成所有的副作用。
b,如果第一个表达式求值为真 (一个非 0值 ),第二个操作数求值。
c,如果第一个表达式求值为假 ( 一个 0值 ),第三个操作数求值。
27
条件表达式的结果是第二个操作数或第三个操作数的值。在条件表达式中后两个操作数只有一个求值。对于表达式嵌套的情况,后面操作数中仅只有一个求值。
第一个表达式为算术或指针类型,条件表达式结果的类型取决于第二及第三个操作数转换的共同类型,以下规则用于第二及第三个操作数:
a,如果 e2,e3是同类型的,则结果是那个类型的。
例如,e2,e3是 int型,则表达式是 int型。
在 C++中如果第二和第三个操作数是同类型的左值,则表达式的结果是左值表达式。 在 C中条件表达式的结果不是左值。
28
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;
29
第三个操作数可以是另外的条件表达式,形成条件表达式的嵌套。条件运算符是右结合的。结合性用于确定嵌套的操作数绑定的紧密程度,与其间操作数的先后运算次序没有直接的关系。
编译器对条件表达式从左到右扫描,寻找三个操作数的匹配。问号,?”之前的表达式是整数 (含 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;
30
而表达式 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;
31
前节的符号函数可用下面条件表达式的嵌套表示,
如:
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表示该表达式的结果。
32
五,switch语句语法格式
switch语句构成的多路顺序分支选择是一种简捷的结构控制形式。 switch语句的语法格式如下所示,方括号包括的内容表示可以省略的项目,break关键字用于退出 switch控制体。 break 关键字仅用于终止当前的 switch控制体即控制跳转到紧随其后的后续语句。
switch语句的表达式要求是整型 (含字符型或枚举型 )不能是浮点型数据。整型常数表达式为编译阶段可以静态求值的整数构成的表达式,相当于 case语句的入口。最后一个分支可省略 break语句。可用 goto 语句把控制转向嵌套结构的外层的标号处。
33
switch(表达式)
{ case 整型常数表达式 k,[语句序列 k; break;]
case 整型常数表达式 j,语句序列 j; [break;]
case 整型常数表达式 i,语句序列 i; [return语句 ;]
......
case 整型常数表达式 n:
语句序列 n; [ goto AfterSwitch; ]
[ default,语句序列 m; break;]
}
[AfterSwitch,]
后续语句;
switch语句的语法格式
34
switch~case开关分支的 执行流程 是:
switch语句根据表达式中的值进行分支选择,首先计算出表达式的值,然后用其结果值与各个 case后的整型常数表达式的值进行比较判断,测试是否相等,只要找到相等的匹配,就立即跳转到与之匹配的语句序列中进行计算。
如果在各整型常数表达式中没有搜索到相等的匹配,则执行 default下的语句序列或跳过 switch~case控制体若此时程序没提供 default语句。
35
在一个给定的 switch语句中,在 case语句中的整型常数表达式的值不能彼此相同,default也仅能出现一次。
流程一经进入 case分支中,就顺序往下执行分支中的语句,直到遇到某一分支的 break 或 goto语句或 return语句,
此时退出该分支;否则,继续往下执行。
每一个分支的先后次序不影响优化搜寻的匹配结果。
在刻意省略 break的情况下,程序的运算状况差异很大。
36
在 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);
37
[例 ] 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;
38
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之下布置跳出语句,因而次序可以任意,因此是好的编程风格。
39
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;
}
40