第
3
章
程序控制结构
经过第2章的学习,我们已经能够合理地表达程序中的数据,并能够对这些
数据进行基本的运算。但实际问题的求解过程往往是十分复杂的,它不仅包括
顺序的计算步骤,还包括许多可供选择的不同计算路径,或者一些需要重复执
行若干次的计算块。而这些计算路径的选择和计算块的重复控制都依赖于对
某些设定条件的判断。对一个实际问题求解过程中所包含的各个计算块间的
逻辑关系的完整描述就体现了程序的控制结构。

程序的控制结构包括顺序、选择、循环,这三种基本结构经过反复嵌套可以
组成各种复杂程序。C语言提供了多种语句来实现这三种基本控制结构。本章
介绍这些基本语句及其在常用算法(枚举法、迭代法)中的应用,并以此为基础
使读者熟练掌握结构化程序设计方法,为后面各章的学习打下基础。

..3.1 
结构化程序设计

20 世纪60 年代末,国际著名计算机专家E.W.Dijkstra首次提出了“结构
化程序设计”的思想。它规定了一套方法,使程序具有合理的结构,以保证程序
的正确性。这种方法要求程序设计人员按照一定的结构形式来设计和编写程
序,使程序易读、易理解、易修改和易维护。C语言是一种适合结构化程序设计
方法的语言。

3.1 
结构化程序
1.
一般地,实际问题的求解过程可由顺序的计算步骤、可选择的不同计算路
径、需要重复执行若干次的计算块三类子过程组成。在结构化程序中我们将之
分别对应于顺序、选择、循环三种基本结构,这三种结构之间允许嵌套,由这三
种基本结构经过反复嵌套即可构成任意复杂的结构化程序。

为使读者更好地理解结构化程序的概念,我们首先引入局部程序块的概
念。局部程序块是指一对花括号之间的一段C语言程序,其在逻辑上是一个整
体。因此,在逻辑上可以认为一个局部程序块是一条单一语句,C语言中称之为
复合语句,或者语句块。在算法流程图中可以用矩形框来表示一个语句块,这
样做能够隐藏语句块内部语句的逻辑关系,而使得整个程序中各个语句块间的


64程序设计教程(C语言微课版) 
逻辑关系更为清晰。
顺序结构的功能是按照各个语句块排列的先后次序依次执行,其对应的流程图画法
如图3-1中①所示。C语言中的所有非转移语句(比如:赋值语句等)都支持顺序结构。
图3-
1 
五种基本控制结构流程图的规范画法

选择结构的功能是根据给定的条件从两条或多条可能的路径中选择一条,其对应的
流程图画法如图3-1中②、⑤所示。C语言中的if语句和switch语句支持选择结构。
循环结构的功能是满足给定条件时反复执行内嵌语句块,直至条件不满足时离开。
依据条件判定的先后可分为当型循环(先判定型)、直到型循环(后判定型)两种结构,其对


第3章程序控制结构65
应的流程图画法如图3-1中③、④所示。C语言中的while语句和for语句支持当型循环
结构,do-while语句支持直到型循环结构。
为使用流程图描述结构化程序,必须限制流程图只能使用图3-1所给出的5种基本
控制结构。任何复杂的程序流程图都应由这五种基本控制结构组合或嵌套而成。这样规
定的主要原因是,在传统流程图中表示程序控制流程的箭头可以不受任何约束的随意转
移控制,这会破坏结构化程序设计的精神。
3.1.2结构化程序设计方法
结构化程序设计方法是在语句级上的问题分解与抽象,即经过多层分解后的子问题
是能够用C语句直接表达和计算的。因此,它是程序设计工作中最根本的方法,其基本
思想可以概括为以下3点: 
(1)采用顺序、选择和循环三种基本结构作为程序设计的基本单元。这样设计的程
序有以下4个特征:只有一个入口;只有一个出口;无死语句;无死循环。
(2)尽量避免使用跳转语句。如果要使用,尽可能不使用多于一个跳转的语句标号; 
且只在一个单入口单出口的程序结构内使用跳转语句,并且只往前跳转,不允许往后
跳转。
(3)采用“自顶向下、逐步求精”的程序设计方法。
在运用计算机求解问题时,如果问题比较简单,则比较容易确定算法,甚至直接编出
程序。因此,对于一个复杂的大问题,我们希望将其分解细化为若干个独立的小问题,然
后逐一解决这些小问题,必要时再对每个小问题进一步分解,如此反复,直至分解后的小
问题均能直接运用C语句运算。这种通过多层分解、逐步求精,最后完全确定算法的技
术,通常称为“自顶向下、逐步求精”的程序设计方法,它也是结构化程序设计的最好方法。

自顶向下、逐步求精法程序设计的一般步骤如下: 

(1)对实际问题进行全局分析,确定数学模型。
(2)确定程序的总体结构,将整个问题分解成若干相对独立的子问题。
(3)确定子问题的具体功能及其相互关系。
(4)将子问题进一步细化,直至能用高级语言表示为止。
1的设计过程为例进行详细为更好地说明结构化程序设计方法的应用过程,以例3.
分析和讲解。
1 
tarentart~en

例3.输入两个整数st、d,编程求sd内的所有素数。

问题分析:该问题本质是在一定范围内寻找满足特定条件的整数。这里的一定范围
由输入量直接给出;特定条件即判断某个整数是否是素数。依据素数的定义,对于整数i, 
需要在2~i-1范围内判断是否存在i的约数,若存在则不是素数;反之,即为素数,直接
输出i即可。

“自顶向下、逐步求精”法程序设计过程如图3-2所示。根据问题的复杂程度,在其中
的第(3)、(4)步之间还可以继续添加子问题的细化步骤,细化至每一个子问题都可以用
C语句直接实现为止。


66 
程序设计教程(C语言微课版) 

(1)程序总体结构(2)子问题的细化(3)子问题的进一步细化(4)编码
子问题1:数据表示
(用变量定义语句实现) 
子问题2:输入start、end 
(用输入语句实现) 
设标志位flag:0表示非素数;1 表示素数
初始假设i是素数,flag置1 
(用赋值语句实现) …… 子问题3:输出start~ 
end内的素数
解题思路:对范围内的每
一个整数i进行判断和输
出。(用循环结构实现) 
子问题3-1:i是素数吗? 
解题思路:对2~i-1范围内的
每一个整数j进行判断。(用循
环结构实现) 
子问题3-1-1:j是i的
约数吗? 
解题思路:判断i%j是否
为0,为0则flag置0。
(用单分支if语句实现) 
子问题3-2:输出
判断flag,是1则输出i 
(用单分支if语句实现) 

图3-
2 
“自顶向下、逐步求精”法程序设计过程示意图

温馨提示:设置标志位flag是程序设计中的一种常用技巧,它的引入可以使程序的
逻辑更为清晰,使语句块之间的耦合关系更为松散,便于结构化程序设计方法的应用。
在本章后续内容的相应部分已给出本例题的关键代码段,请读者在学完本章后自行
组合并调试解决该问题的完整程序。


..3.2 
顺序结构

2 
顺序结构

3. 
顺序结构是最基本、最简单的程序控制结构,它由若干语句块组成,各块按照排列次
序依次执行。这里所谓的块是指非转移语句(如表达式语句等)或者三种基本结构之一。
顺序结构应用于比较简单的、不需要根据条件选择执行不同语句,也不需要反复(多次)完
成特定操作的程序。一般地,所有的C程序都可以划分为:变量定义、数据输入、计算、结
果输出4部分。即宏观来看,所有的C程序都是顺序结构程序。
本小节首先介绍输入、输出在C语言中的实现方法,然后再从结构化程序设计的角
度分析顺序结构程序设计的要点及应注意的地方。

3.1 
输入输出在C语言中的实现
2.
所谓输入输出是以计算机为主体的输入和输出,即键盘输入和显示器输出。输入和
输出是用户与程序实现交互的唯一途径。想使程序每次运行能对不同的数据进行计算就
必须设置输入;一个有效的程序必须包含运算结果的输出。

在C语言中,数据的输入和输出主要是通过调用scanf函数和printf函数实现的


第3章 程序控制结构 67 
(scanf和printf不是C语言标准中规定的语句,而是C编译系统提供的函数库中的标准
函数)。因此,必须熟练掌握scanf函数和printf函数的应用。
在使用标准库函数的程序中应添加相应的预编译命令。例如:使用标准输入输出库
函数时应在源程序开头包含以下预编译命令: 
#include<stdio.h> 
或 
#include "stdio.h" 
其中,stdio是standardinput&outupt的缩写。
常用的输入输出库函数包括:格式输入输出、单字符输入输出两类。
1.格式输出函数
格式输出函数(printf函数)关键字的尾字母f即为“格式”(format)之意。其功能是
按用户指定的格式,把指定的数据输出到显示器屏幕上。在前面的例题中我们已多次使
用过这个函数。printf函数是一个标准库函数,它的函数原型在头文件"stdio.h"中。但
作为一个特例,不要求在使用printf函数之前必须包含stdio.h文件。
printf函数调用的一般形式为() 
printf("格式控制串",输出表列) 
其中: 
格式控制串用于指定输出格式。格式控制串由格式说明和原样输出两部分组成。
(1)格式说明是以%开头的字符串,在%后面跟有各种格式字符,以说明输出数据的
类型、形式、长度、小数位数等。如:"%d"表示按十进制整型输出;"%ld"表示按十进制
长整型输出;"%c"表示按字符型输出等。
(2)原样输出部分可由任意字符组成,在输出中起提示作用。
输出表列中给出了各个输出项,要求格式说明和各输出项必须在数量和类型上一一
对应,输出时将输出列表中各输出项的值按照格式说明的要求输出在对应格式说明的位
置上。比
如:例3.1的子问题3-2中输出i的部分,采用printf函数可写为 
printf("%d ",i); 
例3.2 printf函数的应用。程序如下: 
#include<"stdio.h"> 
int main(){ 
int a=88,b=89;

68 程序设计教程(C语言微课版) 
printf("%d %d\n",a,b); 
printf("%d,%d\n",a,b); 
printf("%c,%c\n",a,b); 
printf("a=%d,b=%d",a,b); 
return 0; 
}
运行结果为 
88 89 ↙ 
88,89 ↙ 
X,Y ↙ 
a=88,b=89 
温馨提示:本例中4次输出了a、b的值,但由于格式控制串不同,输出的结果也不相
同。第4行的输出语句格式控制串中,两格式串%d之间加了一个空格(非格式字符),所
以输出的a、b值之间有一个空格。第5行的printf语句格式控制串中加入的是非格式字
符逗号,因此输出的a、b值之间加了一个逗号。第6行的格式串要求按字符型输出a,b 
值。第7行中为了提示输出结果又增加了原样输出部分进行提示。
printf输出格式说明的一般形式为 
%[标志][输出最小宽度][.精度][长度]类型
其中,方括号[]中的项为可选项。各项的意义介绍如下: 
(1)类型:类型字符用以表示输出数据的类型,其格式符和意义如表3-1所示。
表3-1 printf格式字符
格式字符意 义
d,i 以十进制形式输出带符号整数(正数不输出符号) 
o 以八进制形式输出无符号整数(不输出前缀0) 
x,X 以十六进制形式输出无符号整数(不输出前缀Ox) 
u 以十进制形式输出无符号整数
f 以小数形式输出单、双精度实数
e,E 以指数形式输出单、双精度实数
g,G 以%f或%e中较短的输出宽度输出单、双精度实数
c 输出单个字符
s 输出字符串

第3章 程序控制结构 69 
(2)标志:标志字符为-、+、#、空格4种,其意义如表3-2所示。
表3-2 printf标志字符
标志意 义
- 结果左对齐,右边填空格
+ 输出符号(正号或负号) 
空格输出值为正时冠以空格,为负时冠以负号
# 对c、s、d、u类无影响;对o类,在输出时加前缀o;对x类,在输出时加前缀0x;对e、g、f类
当结果有小数时才给出小数点 
(3)输出最小宽度:用十进制整数表示输出的最少位数。若实际位数多于定义的宽
度,则按实际位数输出,若实际位数少于定义的宽度则补以空格或0。
(4)精度:精度格式符以"."开头,后跟十进制整数。本项的意义是:如果输出数字, 
则表示小数的位数;如果输出的是字符,则表示输出字符的个数;若实际位数大于所定义
的精度数,则截去超过的部分。
(5)长度:长度格式符为h、l两种。h表示按短整型量输出,l表示按长整型量输出。
例3.3 printf中格式说明的使用。程序如下: 
#include<stdio.h> 
int main(){ 
int a=15; 
float b=123.1234567; 
double c=12345678.1234567; 
char d='p'; 
printf("a=%d,%5d,%o,%x\n",a,a,a,a); 
printf("b=%f,%lf,%5.4lf,%e\n",b,b,b,b); 
printf("c=%lf,%f,%8.4lf\n",c,c,c); 
printf("d=%c,%8c\n",d,d); 
return 0; 
}
运行结果为 
a=15, 15,17,f ↙ 
b=123.123459, 123.123459,123.1235,1.231235e+002 ↙ 
c=12345678.123457, 12345678.123457, 12345678.1235 ↙ 
d=p, p ↙ 
温馨提示:本例第7行中以4种格式输出整型变量a的值,其中"%5d"要求输出宽
度为5,而a值为15只有两位故补三个空格。第8行中以4种格式输出实型量b的值。
其中"%f"和"%lf"格式的输出相同,说明"l"符对"f"类型无影响。"%5.4lf"指定输出宽度
为5,精度为4,由于实际长度超过5故应该按实际位数输出,小数位数超过4位部分被截

70 程序设计教程(C语言微课版) 
去。第9行输出双精度实数,"%8.4lf"由于指定精度为4位,故截去了超过4位的部分。第
10行输出字符量d,其中"%8c"指定输出宽度为8故在输出字符p之前补加7个空格。
2.格式输入函数
格式输入函数(scanf函数),即按用户指定的格式从键盘上把数据输入到指定的变量
之中。scanf函数是一个标准库函数,它的函数原型在头文件"stdio.h"中,与printf函数
相同。C语言也允许在使用scanf函数之前不必包含stdio.h文件。
scanf函数的一般形式为 
scanf("格式控制串",地址表列); 
其中: 
格式控制串的作用与printf函数相同,但原样输入部分需要用户自己输入,且必须与
源代码中的完全一致。因此,建议scanf中的格式控制串越简单越好,不要在数据间(格
式说明间)加间隔符,以免用户输入时由于不知道源代码中写的间隔符是什么,而使得输
入值不能被正确获取,最终导致程序出错。
地址表列中给出各变量的地址,地址是由地址运算符& 后跟变量名组成的。例如: 
&a,&b,分别表示变量a和变量b的地址。这个地址就是编译系统在内存中给a、b变量
分配的地址。在C语言中,使用地址这个概念,把变量的值和变量的地址这两个不同的
概念区别开来。变量的地址是C编译系统分配的,用户不必关心具体的地址是多少。
例如,例3.1中子问题2:输入start、end。采用scanf函数可写为 
scanf("%d%d",&start,&end); 
scanf格式说明的一般形式为 
%[*][输入数据宽度][长度]类型
其中有方括号[]的项为任选项。各项的意义如下: 
(1)类型:表示输入数据的类型,其格式符和意义如表3-3所示。
表3-3 scanf格式字符
格式字符意义格式字符意义
d,i 输入十进制整数f或e 输入实型数(用小数形式或指数形式) 
o 输入无符号八进制整数c 输入单个字符
X,x 输入无符号十六进制整数s 输入字符串
u 输入无符号十进制整数 
(2)"*"符:用以表示该输入项读入后不赋予相应的变量,即跳过该输入值。如: 
scanf("%d %*d %d",&a,&b);

第3章 程序控制结构 71 
当输入为:1 2 3时,把1赋予a,2被跳过,3赋予b。
(3)输入数据宽度:用十进制整数指定输入的宽度(即字符数)。例如: 
scanf("%5d",&a); 
输入:12345678,只把12345赋予变量a,其余部分被截去。又如: 
scanf("%4d%4d",&a,&b); 
输入:12345678,将把1234赋予a,而把5678赋予b。
(4)长度:长度格式符为l和h,l表示输入长整型数据(如%ld)和双精度浮点数
(如%lf)。h表示输入短整型数据。
使用scanf函数还必须注意以下几点: 
(1)scanf函数中没有精度控制,如“scanf("%5.2f",&a);”是非法的。不能企图用此
语句输入小数为2位的实数。
(2)scanf函数中要求给出变量地址,如给出变量名则会出错。如“scanf("%d",a);” 
是非法的,应改为“scnaf("%d",&a);”才是合法的。
(3)在输入多个数值数据时,若格式控制串中没有指定使用特定的原样输入部分作
输入数据之间的间隔,实际输入时默认可用空格、Tab或回车作间隔。C编译在碰到空
格、Tab、回车或非法数据(如对"%d"输入"12A"时,A 即为非法数据)时即认为该数据输
入结束。
(4)在输入字符数据时,如果格式控制串中无原样输入部分,则认为所有输入的字符
均为有效字符。例如: 
scanf("%c%c%c",&a,&b,&c); 
输入为:d e f,则把d' '赋予a,' '赋予b,e' '赋予c;只有当输入为:def时,才能把d' '赋予
a,,e' '赋予b,f' '赋予c。如果在格式控制中加入空格作为间隔,如: 
scanf("%c %c %c",&a,&b,&c); 
则输入时各数据之间可加空格。
例3.4 scanf函数的应用。程序如下: 
#include<stdio.h> 
int main (){ 
int a,b,c; 
printf("input a,b,c\n"); 
scanf("%d%d%d",&a,&b,&c); 
printf("a=%d,b=%d,c=%d",a,b,c); 
return 0; 
}

72 程序设计教程(C语言微课版) 
温馨提示:在本例中,由于scanf函数本身不能显示提示串,故先用printf语句在屏
幕上输出提示,请用户输入a、b、c的值。执行scanf语句,则退出VC++屏幕进入用户屏
幕等待用户输入。用户输入7 8 9后按下Enter键。此时,系统又将返回屏幕。在
scanf语句的格式串中由于没有原样输入部分在“%d%d%d”之间作输入时的间隔,因此, 
在输入时要用一个以上的空格或回车键作为每两个输入数之间的间隔。如: 
7 8 9 
或 
789 
3.单字符输出函数
字符输出函数(putchar函数)的功能是在显示器上输出单个字符。其一般形式为 
putchar(字符变量/常量) 
例如: 
putchar('A'); /*输出大写字母A*/ 
putchar(x); /*输出字符变量x 的值*/ 
putchar('\101'); /*输出字符A*/ 
putchar('\n'); /*换行*/ 
温馨提示:若输出为控制字符则执行相应的控制功能,不在屏幕上显示。
4.单字符输入函数
单字符输入函数(getchar函数)的功能是从键盘上输入并返回一个字符。其一般形
式为 
getchar(); 
通常把输入的字符赋予一个字符变量,构成赋值语句,如: 
char c; 
c=getchar(); 
例3.5 输入输出单个字符。程序如下: 
#include<stdio.h> 
int main(){

第3章 程序控制结构 73 
char c; 
printf("input a character:\n"); /*提示输入语句*/ 
c=getchar(); /*单字符输入*/ 
putchar(c); /*单字符输出*/ 
return 0; 
}
运行结果为 
input a character:↙ 
j ↙ 
j
温馨提示: 
(1)getchar函数只能接受单个字符,输入数字也按字符处理。输入多于一个字符
时,只接收第一个字符。
(2)使用本函数前必须包含文件"stdio.h"。
(3)在VC++屏幕下运行含本函数程序时,将退出VC++ 屏幕进入用户屏幕等待用
户输入。输入完毕再返回VC++屏幕。
(4)程序5、6行可用下面两行的任意一行代替: 
putchar(getchar()); 
printf("%c",getchar()); 
知识小档案:行缓冲
行缓冲是指见到换行符的时候把缓冲区的内容送到指定位置。例如,在如下程序中, 
执行时输入了abc↙,此时缓冲区中有a、空格、b、c、回车,共5个字符,程序执行scanf只
会取走其中的前三个字符,紧接着再执行getchar就会使end1得到第四个字符c,再执行
getchar就会使end2得到第五个字符“↙”,因此程序执行最终的输出结果如图3-3所示。 
#include<stdio.h> 
#include<string.h> 
int main(){ 
char end1,end2, a,b,c; 
printf("input:"); 
scanf("%c%c%c",&a,&b,&c); 
end1=getchar(); 
end2=getchar(); 
printf("output:a=%c b=%c c=%c end1=%c end2=%c\n",a,b,c,end1,end2); 
return 0; 
}

74 程序设计教程(C语言微课版) 
执行结果如图3-3所示。
图3-3 行缓冲示例的执行结果
3.2.2 顺序结构程序设计方法与示例
程序是由数据和运算组成的,而数据又是运算的基础,数据的表示形式也决定着可以
对其进行哪些运算以及运算的先后次序。
比如:C语言要求变量必须“先定义、再使用”,而且在利用变量的值参与运算之前必
须保证该变量已经有了合理的初值,否则就需要先给它赋初值(赋值语句或者输入语句), 
然后才能用它参与各种运算。之所以这样规定,主要是因为当执行变量定义语句时,系统
会在内存为该变量分配相应大小的空间(若干字节),并在变量名与内存空间之间建立关
联关系(将该空间的首字节地址记为该变量的地址),但不会改变该空间内的数据值。因
此,当直接使用该变量名读取数据时,只能得到一个随机数。
基于上述原因并结合结构化程序设计思想,我们认为C程序在宏观上都可以看作是
顺序结构的,且由定义、输入、计算、输出4部分组成,其中前3部分可根据问题的实际需
要选择省略或添加。
定义部分需要对问题进行分析,提取出解决问题过程中需要用到的各个量,包括变量
和常量,确定每个量的名称、类型、初值、含义等。
输入部分主要实现变量赋初值,可以用赋值语句或者输入语句来完成。如果希望每
次程序执行可以处理不同的数据,就必须使用输入语句。输入部分只需对完成运算必需
的量赋初值,而对用于存放运算结果的量则不必赋值。
计算部分主要由各种表达式语句构成,确定好各个表达式语句的先后次序即可。
输出部分的功能就是用输出语句以要求的格式输出结果。有时,计算部分和输出部
分是混杂在一起的,即边计算边输出,如例3.1就属于这种情况。
例3.6 计算一元二次方程ax2+bx+c=0的实根,a、b、c 由键盘输入,设b2-4ac>0。
问题分析:由求根公式可知,若令: 
p = -b 
2a , q = b2 -4ac 
2a 
则有: 
x1 =p +q, x2 =p -q 
程序如下: 
#include<stdio.h> 
#include "math.h" /*由于要调用数学函数库中的函数,所以必须用#include 将数学函
数库的头文件math.h 包含进程序*/

第3章 程序控制结构 75 
int main() 
{ 
float a,b,c disc,x1,x2,p,q; /*定义部分*/ 
scanf("a=%f,b=%f,c=%f",&a,&b,&c); /*输入部分*/ 
disc=b*b-4*a*c; /*计算δ*/ 
p=-b/(2*a); 
q=sqrt(disc/(2*a)); /*sqrt()求平方根的函数*/ 
x1=p+q; 
x2=p-q; /*计算2 个方程的根*/ 
printf("\nx1=%5.2f\nx2=%5.2f\n",x1,x2); /*输出部分*/ 
return 0; 
}
运行结果是: 
输入: 
a=2.3,b=6.7,c=3.1 ↙ 
输出: 
x1=-0.58 
x2=-2.34 
例3.7 已知三条边a、b、c,求三角形面积。
问题分析:由计算三角形面积的海伦公式可得: 
area= s(s-a)(s-b)(s-c) 
其中:s=(a+b+c)/2。
设定义: 
(1)实型变量a、b、c,表示三角形的三边长,需要输入。
(2)实型变量s、area,存放结果,包括中间结果和最终结果。
程序如下: 
#include<stdio.h> 
#include "math.h" 
int main(){ 
float a,b,c; /*定义部分*/ 
float s,area; 
scanf("%f, %f, %f",&a,&b,&c); /*输入三条边a、b、c*/ 
s=1.0/2*(a+b+c); /*计算部分*/ 
area=sqrt(s*(s-a)*(s-b)*(s-c)); 
printf("area=%8.3f \n",area); / *输出部分*/ 
return 0; 
}
程序运行情况如下:

76 程序设计教程(C语言微课版) 
3,4,5 ↙ 
area= 6.000 
请多试几组数据进行程序正确性的测试。想一想,这个程序还有什么地方需要改进? 
温馨提示:注意此例中第7行s=1.0/2*(a+b+c)如写作s=1/2*(a+b+c),则
无论输入的a、b、c为何值,其计算结果始终为0。因为1/2在C表达式中的计算结果要
保持为整型,即取为0而不是0.5。这一点请读者在将数学表达式转换为C语言表达式
时一定要特别注意。
.. 3.3 选择结构
在程序设计中,经常要求计算机根据不同的条件或情况选择不同的处理过程。C语
言中的if语句和switch语句支持选择结构。
选择结构可分为单分支、双分支和多分支三种情况。一般的,采用if语句实现单选
择结构程序;采用if-else语句实现双选择结构程序;采用if-else-if语句实现多选择结构。
当if语句中的执行语句又是if语句时,则构成了if语句的嵌套。虽然if-else-if语句、嵌
套if语句都能实现多选择结构程序,但用switch语句实现多选择结构的程序更加简洁明
了。用switch(k)语句实现的难点在于k表达式的设计和构造。
3.3.1if语句
3.3.1 if语句
用if语句可以构成选择结构。它根据给定的条件进行判断,以决定是否执行某个分
支程序段。C 语言的if语句经常使用3 种基本形式:if形式、if-else形式和if-else-if 
形式。
1.双分支if-else形式
if-else是if语句的标准形式,它的语句形式为 
if(表达式) 
语句1; 
else 
语句2; 
图3-4 if-else形式执行过程
其语义是:如果表达式的值为真(非0),则
执行语句1,否则执行语句2。为加以区别,称此
处的表达式为条件判断表达式,称此处的语句1 
和语句2 为内嵌语句。其执行过程可表示为
图3-4,即先计算条件判断表达式的值,根据条件
判断表达式的值选择执行语句1或者语句2。语
句1和语句2只有一个将被执行。

第3章 程序控制结构 77 
注意: 
(1)else分句不允许单独使用。
(2)关键字if应与关键字else对齐,内嵌语句缩进若干格。C语言对缩进的要求没
有Python那么严格,“分层缩进、对齐书写”是为了使程序具有更好的可读性。
使用if语句时还应注意以下问题(适用于if的3种基本形式): 
(1)if语句中的条件判断表达式必须用括号括起来,且通常是逻辑表达式或关系表
达式,但也可以是其他表达式(如赋值表达式等),甚至也可以是一个变量。例如: 
if(a=5) 语句; 
if(b) 语句; 
都是允许的。只要表达式的值为非0,就表示逻辑“真”。又如: 
if(a=5)…; 
其中表达式的值永远为非0,所以其后的语句总是要执行的。当然这种情况在程序中不
一定会出现,但在语法上是合法的。再又如,有程序段: 
if(a=b) 
printf("%d",a); 
else 
printf("a=0"); 
本语句的语义是,把b值赋予a,如为非0则输出该值,否则输出“a=0”字符串。这种用
法在程序中是经常出现的。
(2)if语句中的内嵌语句应为单个语句且语句之后必须加分号。如果要想在满足条
件时执行一组(多个)语句,则必须把这一组语句用{}括起来组成一个复合语句。但要注
意的是在“}”之后不能再加分号。例如: 
if(a>b) { 
a++; 
b++; 
}e
lse{ 
a=0; 
b=10; 
} 
2.单分支if形式
if形式是if-else形式缺省else分句的特殊情况,它的语句形式为 
if(表达式) 语句

78 程序设计教程(C语言微课版) 
其语义是:如果表达式的值为真(非0),则执行其后的语句,否则不执行该语句。其
过程可表示为图3-5。
图3-5 if语句基本形式执行过程
比如:例3.1中子问题3-1-1,判断i%j是否为0,若为0则flag置0。采用if形式可
写为 
f(i%j==0) flag=0; 
子问题3-2,判断flag,是1则输出i。采用if形式可写为 
f(flag) printf("%d ",i); 
注意:该用单分支结构时千万不要画蛇添足而写成双分支结构,若将例3.1中子问
题3-1-1写成如下双分支结构,则会造成灾难性的后果:输出start~end内的所有数! 请
大家自行分析原因。 
if(i%j==0) flag=0; else flag=1; 
温馨提示:if-else语句可以书写在一行上,只需将各语法单位用空格间隔开即可。
虽然语法上允许,但考虑程序可读性,不提倡这样书写。
3.条件运算符和条件表达式
如果在条件语句中只执行单个的赋值语句时,常可使用条件表达式来实现。不但使
程序简洁,也提高了运行效率。
条件运算符由“?”和“:”组成,它是一个三目运算符,即有3个参与运算的量。
由条件运算符组成条件表达式的一般形式为 
表达式1 ? 表达式2 : 表达式3 
其求值规则为:如果表达式1的值为真,则以表达式2的值作为条件表达式的值,否
则以表达式3的值作为整个条件表达式的值。条件表达式通常用于赋值语句之中。
例如条件语句: 
if(a>b) max=a; 
else max=b;

第3章 程序控制结构 79 
可用条件表达式写为“max=(a>b)? a:b;”执行该语句的语义是:如a>b为真,则
把a赋予max,否则把b赋予max。
使用条件表达式时,还应注意以下几点: 
(1)条件运算符的运算优先级低于关系运算符和算术运算符,但高于赋值符。因此, 
max=(a>b)?a:b可以去掉括号而写为max=a>b?a:b。
(2)条件运算符“?”和“:”是一对运算符,不能分开单独使用。
(3)条件运算符的结合方向是自右至左。例如:a>b?a:c>d?c:d应理解为a> 
b?a:(c>d?c:d),这也就是条件表达式嵌套的情形,即其中的表达式3又是一个条件表
达式。例
3.8 输入两个整数,输出其中的大数。
方法一:单分支if语句方法二:双分支if-else语句方法三:条件表达式
#include<stdio.h> 
int main() 
{ 
int a,b,max; 
printf("input: "); 
scanf("%d%d",&a,&b); 
max=a; 
if(max<b) 
max=b; 
printf("max=%d",max); 
return 0; 
} 
#include<stdio.h> 
int main() 
{ 
int a, b; 
printf("input:"); 
scanf("%d%d",&a,&b); 
if(a>b) 
printf("max=%d\n",a); 
else 
printf("max=%d\n",b); 
return 0; 
} 
#include<stdio.h> 
int main() 
{ 
int a,b,max; 
printf("input:"); 
scanf("%d%d",&a,&b); 
printf("max=%d", a> b? 
a:b); 
return 0; 
} 
温馨提示:同一个问题可以运用不同的语法结构编写代码,但通常情况下,应选择其
中逻辑表达最简单也最清晰的结构,这样的程序可读性较好。比如,该例中方法二就是首
选结构。
4.多分支if-else-if形式
if-else-if是if语句用于多分支处理的形式,是程序编写中进行多路选择的常用方法, 
其语句形式为 
if(表达式1) 
语句1; 
else if(表达式2) 
语句2; 
else if(表达式3) 
语句3; 
. 
else if(表达式m)

80 程序设计教程(C语言微课版) 
语句m; 
else 
语句n; 
其语义是:依次判断表达式的值,当出现某个值为真时,则执行其对应的语句。然后
跳转到整个if语句之外继续执行程序。如果所有的表达式均为假,则执行语句n。然后
继续执行后续程序。if-else-if语句的执行过程如图3-6所示。
图3-6 if-else-if形式执行过程
5.if语句的嵌套
在if语句中又包含一个或多个if语句,则构成if语句的嵌套形式。其一般形式可表
示如下: 
if(表达式) 
if 语句; 
或者为 
if(表达式) 
if 语句; 
else 
if 语句; 
在嵌套内的if语句可能又是if-else型的,这将会出现多个if和多个else重叠的情
况,这时要特别注意if和else的配对问题。
例如: 
if(表达式1) 
if(表达式2) 
语句1; 
else 
语句2;

第3章 程序控制结构 81 
其中的else究竟是与哪一个if配对呢? 
一种理解方式是: 
if(表达式1) 
if(表达式2) 
语句1; 
else 
语句2; 
另一种理解方式是: 
if(表达式1) 
if(表达式2) 
语句1; 
else 
语句2; 
为了避免这种二义性,C语言规定,else总是与它前面复合语句内最近的尚未配对的
if配对,因此对上述例子应按前一种情况理解。
例3.9 比较两个数的大小关系。
方法一:嵌套if形式方法二:if-else-if形式
#include<stdio.h> 
int main() 
{ 
int a,b; 
printf("please input a,b: "); 
scanf("%d%d",&a,&b); 
if(a!=b) 
if(a>b) 
printf("a>b\n"); /*a>b*/ 
else 
printf("a<b\n"); /*a<b*/ 
else 
printf("a=b\n"); /*a==b*/ 
return 0; 
} 
#include<stdio.h> 
int main() 
{ 
int a,b; 
printf("please input a,b: "); 
scanf("%d%d",&a,&b); 
if(a==b) 
printf("a=b\n"); /*a==b*/ 
else if(a>b) 
printf("a>b\n"); /*a>b*/ 
else 
printf("a<b\n"); /*a<b*/ 
return 0; 
} 
温馨提示:本例用if-else-if语句实现,程序逻辑更加清晰。因此,在一般情况下,应
尽量避免使用if语句的嵌套结构,如果非用不可,建议用复合语句的形式表明其逻辑关
系而不是仅仅通过缩进对齐的形式说明其配对关系。
学习完if语句后,请改进例3.7,实现在计算三角形面积之前,先判断输入的三条边
是否合法,而后再进行计算。并且当用户输入的三条边不合法时(比如:3、4、8),能给出
“输入数据不合法”的提示。

82 程序设计教程(C语言微课版) 
3.3.2switch 
语句
3.3.2 switch语句
在用if-else-if或者if嵌套实现的多分支结构中,随着分支数的增加,其结构的复杂性
也会显著增加,不仅会减弱程序的可读性还会降低执行效率。C语言还提供了另一种用
于多分支选择的switch语句,其一般形式为: 
switch(表达式){ 
case 常量表达式1: 语句序列1; 
case 常量表达式2: 语句序列2; 
. 
case 常量表达式n: 语句序列n; 
default: 语句序列n+1; 
}
其语义是:先计算switch后表达式的值,将该表达式的值依次与case后常量表达式
的值比较,若相等,则从该常量表达式后的语句序列开始执行,直到遇到break语句或遇
到switch语句的结尾花括号为止;若都不相等,则执行default后的语句序列。
使用switch语句还应注意: 
(1)case和default不能单独使用。
(2)各case和default子句的先后顺序可以变动,而不会影响程序执行结果。
(3)default子句可以省略,也可以出现在任意位置。
(4)case和default后的语句序列可包含多条语句,且不必使用复合语句的形式。
(5)在case后的各常量表达式的值不能相同,否则会出现错误。
(6)多条case分句可以共用同一个语句序列。
(7)一般地,case后的语句序列应以break语句结束。如果没有break语句,则会顺
序依次执行之后case的语句序列,直至遇到break或者switch语句的结尾花括号。
例3.10 输入一个1~7的整数,转换成星期输出。
程序如下: 
#include<stdio.h> 
int main() 
{ 
int a; 
printf("input an integer number: "); 
scanf("%d",&a); 
switch (a){ 
case 1: printf("Monday\n"); 
case 2: printf("Tuesday\n"); 
case 3: printf("Wednesday\n"); 
case 4: printf("Thursday\n");