第3章C语言的控制结构 通过前两章的学习,我们了解和掌握了程序设计的思想及基础知识。这一章将介绍结构化程序的三种基本结构: 顺序结构、选择结构和循环结构,重点是实现选择结构和循环结构的程序设计方法。 本章要点 理解结构化程序设计的思想及方法。 掌握实现选择结构的程序设计方法: if、switch语句。 掌握实现循环结构的程序设计方法: while、do-while、for语句。 3.1结构化程序设计 结构化程序设计由Esdger W.Dijkstra在1969年提出,是以模块化设计为中心,将待开发的软件系统划分为若干个相互独立的模块,这样使完成每一个模块的工作变得单纯而明确,为设计一些较大的软件打下了良好的基础。结构化程序设计是一种程序设计的技术,用三种基本的控制结构,通过组合和嵌套实现任何单入口单出口的程序——这就是结构化程序设计基本原理。这三种控制结构是顺序结构、选择结构和循环结构。 3.1.1程序的基本结构 1. 顺序结构 顺序结构表示程序中的各操作是按照它们出现的先后顺序依次被执行,每个操作都被执行且只被执行一次。 2. 选择结构 选择结构表示程序的处理步骤出现了分支,它需要根据某一特定的条件选择其中的一个分支执行。选择结构有单选择、双选择和多选择三种形式。 3. 循环结构 循环结构表示程序反复执行某个或某些操作,直到某条件为“假”(或为“真”)时才可终止循环。在循环结构中最主要的是什么情况下执行循环,哪些操作需要循环执行。 循环结构的基本形式有两种: 当型循环和直到型循环。 当型循环: 表示先判断条件,当满足给定的条件时执行循环体,并且在循环终端处流程自动返回到循环入口; 如果条件不满足,则退出循环体直接到达流程出口处。因为是“当条件满足时执行循环”,即先判断循环条件后执行,所以称为当型循环。 直到型循环: 表示从循环入口处直接执行循环体,在循环终端处判断条件,如果条件不满足,返回入口处继续执行循环体,直到条件为“真”时再退出循环到达流程出口处,是先执行循环体后判断。因为是“直到条件为‘真’时为止”,所以称为直到型循环。 3.1.2结构化程序设计的特点 结构化程序中的任意基本结构都具有唯一入口和唯一出口,并且程序不会出现死循环。程序的静态形式与动态执行流程之间具有良好的对应关系。 1. 优点 由于模块相互独立,因此在设计其中一个模块时,不会受到其他模块的牵连,可将原来较为复杂的问题化简为一系列简单模块的设计。模块的独立性还为扩充已有系统、建立新系统带来了不少的方便,因为可以充分利用现有的模块作积木式的扩展。 按照结构化程序设计的观点,任何算法功能都可以通过由三种基本程序结构的组合而成的程序模块来实现。 结构化程序设计的基本思想是采用“自顶向下,逐步求精”的程序设计方法和“单入口单出口”的控制结构。“自顶向下,逐步求精”的程序设计方法从问题本身开始,经过逐步细化,将解决问题的步骤分解为由基本程序结构模块组成的结构化程序框图; “单入口单出口”的思想认为一个复杂的程序,如果它仅是由顺序、选择和循环三种基本程序结构通过组合、嵌套构成,那么这个新构造的程序一定是一个单入口单出口的程序。据此就很容易编写出结构良好、易于调试的程序来。 2. 缺点 (1) 用户要求难以在系统分析阶段准确定义,致使系统在交付使用时产生许多问题。 (2) 用系统开发每个阶段的成果来进行控制,不能适应事物变化的要求。 (3) 系统开发周期长。 3.1.3结构化程序设计的方法 1. 自顶向下 程序设计时,应先考虑总体,后考虑细节; 先考虑全局目标,后考虑局部目标。不要一开始就过分追求细节,先从最上层总目标开始设计,逐步使问题具体化。 2. 逐步细化 对复杂问题,应设计一些子目标作为过渡,逐步细化。 3. 模块化设计 一个复杂问题,肯定是由若干稍简单的问题构成。模块化是把程序要解决的总目标分解为子目标,再进一步分解为具体的小目标,每一个小目标称为一个模块。 3.1.4结构化程序设计的步骤 1. 分析问题 对要解决的问题,首先必须分析清楚,明确问题的要求,列出所有已知量,找出问题的求解范围、解的精度等。 2. 建立数学模型 对实际问题进行分析之后,找出它的内在规律,就可以建立数学模型。只有建立了模型的问题,才可能利用计算机来解决。 3. 设计算法 建立数学模型后,还不能着手编写程序,必须根据数据结构,设计解决问题的算法。设计算法时一般要注意: (1) 算法的逻辑结构尽可能简单。 (2) 算法所要求的存储量应尽可能少。 (3) 避免不必要的循环,减少算法的执行时间。 (4) 在满足问题要求条件下,使所需的计算量最小。 4. 编写程序 把整个程序看作一个整体,先全局后局部,自顶向下,一层一层分解处理,如果某些子问题的算法相同而仅参数不同,可以用子程序来表示。 5. 调试运行 将整个程序编译、调试后运行程序得出结果。 6. 分析结果 根据运行结果分析程序,通过几组数据验证程序的正确性。 7. 写出程序的文档 主要是对程序中的变量、函数或过程作必要的说明,解释编程思路,画出程序框图,讨论运行结果等。 3.2顺序结构程序设计 程序设计的三种基本结构为顺序结构、分支结构、循环结构,由这三种结构组成各种复杂的程序。C语言提供了多种语句来实现这些结构。本节讨论顺序结构程序设计以及C语言的基本语句。顺序结构设计的程序自顶向下,按照每条语句书写的顺序依次执行,每条语句都执行,而且都只执行一次。顺序结构,不涉及复杂的算法,只是由一些基本的C语言语句组成,是一种最简单的程序设计结构。 【例3-1】从键盘输入任意3个数据,求其平均值。 算法设计: (1) 求平均值,故变量应定义为浮点型数比较合适; (2) 输入数据,求平均值; (3) 输出平均值。 程序代码: #include stdio.h void main() { float a,b,c,d;/*变量的定义*/ scanf("%f%f%f",&a,&b,&c); /*输入函数后加一个分号*/ d=(a+b+c)/3; /*表达式后面加一个分号*/ printf ("%f",d); /*输出函数后面加一个分号*/ } 程序说明: (1) 本程序所有语句按照书写顺序逐条执行。 (2) 所有语句都是以分号结尾,这种语句称为表达式语句。 除了表达式语句,C语言还有其他类型的语句。例如,函数调用语句、控制语句、复合语句、空语句,下面做详细介绍。 任何高级语言编写的程序,都是由若干语句按照一定结构组织在一起的,语句用来向计算机系统发出操作指令,每个语句经编译以后形成若干条机器指令。一个完整的C语言程序包含若干条语句,大体结构如图3-1所示。 图3-1C语言程序结构 其中C程序的执行部分是由语句组成的,也是程序的主体,程序的具体功能就是由各种语句实现的。 1. 表达式语句 表达式语句由表达式加上分号(; )组成,其一般形式为: 表达式; 执行表达式语句就是计算表达式的值。 例如: a=b+c;/* 赋值语句 */ a+b;/* 加法运算语句,但计算结果不能保留,无实际意义 */ i++;/* 自增1语句,i值增1,与"i=i+1;"等价 */ 2. 函数调用语句 由函数名、实际参数加上分号“; ”组成。其一般形式为: 函数名(实际参数表); 执行函数语句就是调用函数体并把实际参数赋予函数定义中的形式参数,然后执行被调函数体中的语句,求取函数值 (在后面函数中再详细介绍)。 例如: printf("C Program");/* 调用库函数,输出字符串 */ 3. 控制语句 控制语句用于控制程序的流程,以实现程序的三种结构方式。C语言有9种控制语句。按其功能可以分成以下三类。 (1) 条件判断语句: if语句、switch语句; (2) 循环执行语句: do-while语句、while语句、for语句; (3) 转向语句: break语句、goto语句、continue语句、return语句。 4. 复合语句 把多个语句用括号“{}”括起来组成的一个语句称为复合语句。 在程序中应把复合语句看成是单条语句,而不是多条语句。例如: while(i=10) { sum=sum+i; i++;} printf("%d",sum); while循环体内“{}”内的语句为复合语句,复合语句内的各条语句都必须以分号“;”结尾,在括号“}”外不能再加分号。 5. 空语句 只由分号“;”组成的语句称为空语句。空语句是什么也不执行的语句。在程序中空语句可用来作空循环体。 例如: while(getchar()!='\n') ; 本语句的功能是,只要从键盘输入的字符不是换行符则重新输入。 循环体内只有一个分号,为空语句。 6. 赋值语句 在赋值表达式后面加上一个分号就是赋值语句,如sum=sum+i是赋值表达式,而“sum=sum+i;”就是赋值语句。赋值语句是C语言中最常用的一种语句,功能和特点都与赋值表达式相同。 其一般形式为: 变量=表达式; 赋值语句的使用需要注意以下几点。 (1) 由于在赋值符“=”右边的表达式也可以又是一个赋值表达式,因此,下述形式为: 变量=(变量=表达式); 是成立的,从而形成嵌套的情形。其展开之后的一般形式为: 变量=变量=…=表达式; 例如: a=b=c=d=e=5; 按照赋值运算符的右结合性,因此实际上等效于: e=5; d=e; c=d; b=c; a=b; (2) 注意在变量说明中给变量赋初值和赋值语句的区别。 给变量赋初值是变量说明的一部分,赋初值后的变量与其后的其他同类变量之间仍必须用逗号间隔,而赋值语句则必须用分号结尾。 例如: int a=5,b,c; (3) 在变量说明中,不允许连续给多个变量赋初值。 例如,下述说明是错误的: int a=b=c=5; 必须写为: int a=5,b=5,c=5; 而赋值语句允许连续赋值,即可以写为“a=b=c=5;”。 (4) 注意赋值表达式和赋值语句的区别。 赋值表达式是一种表达式,它可以出现在任何允许表达式出现的地方,而赋值语句只能作为语句单独使用。 下述语句是合法的: if((x=y+5)0) z=x; 语句的功能是,若表达式x=y+5大于0,则z=x。 下述语句是非法的: if((x=y+5;)0) z=x; 因为“x=y+5;”是语句,不能出现在表达式中。 【例3-2】从键盘输入一个小写字母,要求改用大写字母输出。 算法设计: (1) 输入一个字符存入变量s1; (2) 利用ASCII值转换大小写(大写字母A的ASCII为65,小写字母a的ASCII值为97,大小写字母的值相差32); (3) 输出转换后的数据。 程序代码: #include stdio.h void main() { char s1,s2;/*定义变量*/ scanf("%c",&s1); s2=s1-32; /*转换成大写字母*/ printf("%c \n",s2); /*输出结果*/ } 运行情况: 输入数据b 输出结果B 【例3-3】从键盘输入任意一个3位整数,求其各位数字之和。 程序代码: #include stdio.h void main() { int n=0,x=0,y=0,z=0,sum=0; scanf("%d",&n); /*输入一个3位整数*/ x=n/100; /*求百位数上的数字*/ y=(n/10)%10;/*求十位数上的数字*/ z=n%10;/*求个位数上的数字*/ sum=x+y+z; printf("sum=%d\n",sum); } 运行情况: 输入345 则结果为sum=12 【例3-4】求方程ax2+bx+c=0的根,a,b,c由键盘输入,假设b2-4ac>0。 算法设计: 一元二次方程的求根公式为: x1=-b+b2-4ac2a,x2=-b-b2-4ac2a 令p=-b2a,q=b2-4ac2a, 则x1=p+q,x2=p-q 程序代码: #includemath.h #include stdio.h void main() { float a,b,c,disc,x1,x2,p,q; scanf("%f %f %f",&a,&b,&c); disc=b*b-4*a*c; p=-b/(2*a); q=sqrt(disc)/(2*a); x1=p+q;x2=p-q; printf("\nx1=%5.2f\nx2=%5.2f\n",x1,x2); } 运行情况: 从键盘输入1 2 1时,得到两个相等的实根为-1,输入2 3 1,得到两个不相等的实根-0.50和-1.00。注意输入的数据必须保证disc大于或等于0。 3.3选择结构程序设计 选择结构表示程序的处理步骤出现了分支,它需要根据某一特定的条件选择其中的一个分支执行。选择结构有单分支、双分支和多分支三种形式。其语句有两类,一类是if语句,另一类是switch语句,以下将分别对它们进行介绍。 3.3.1if语句 if语句是选择结构的一种形式,又称为条件分支语句。它是通过对给定条件的判断,来决定所要执行的操作。C语言中提供了3种形式的if语句。 1. 简单if语句 格式: if (表达式) 语句; 功能: 首先计算表达式的值,若表达式的值为“真”(非0),则执行语句,若表达式的值为“假”(0),不执行语句。其流程图如图3-2(a)所示。例如: 图3-2if语句的流程图 if(x<0)x=-x; 【例3-5】从键盘输入3个整数,按由大到小的顺序输出。 算法设计: 由键盘输入3个数存入a,b,c中。先比较b与a的大小,如b比a大就交换两者的值,再比较a与c的大小,如c比a大就交换两者的值,经过上述处理,a变为3个数中的最大数。最后比较b与c的大小,如c比b大就交换两者的值,这样a,b,c中的值就变为由大到小的顺序了。 程序代码: #includemath.h #include stdio.h void main() { int a,b,c,t; scanf("%d%d%d",&a,&b,&c); printf ("a=%d,b=%d,c=%d\n",a,b,c); if (ba) {t=a;a=b;b=t;} /* 执行后a值比b值大 */ if (ca) {t=a;a=c;c=t;} /* 执行后a值比b、c值大 */ if (cb) {t=b;b=c;c=t;} /* 执行后b值比c值大 */ printf (" %d %d %d\n",a,b,c); } 运行情况: 输入数据 5 4 9 输出结果 a=5,b=4,c=9 9 5 4 2. 简单分支if语句 格式: if (表达式) 语句1; else语句2; 例如: if(x=0)y=x; else y=-x; 这种if语句的流程图如图3-2(b)所示。 【例3-6】输入一个整数(大于1小于100),判断是否为同构数。如果一个整数位于它的平方数右边,即称其为同构数,如5、6等。 算法设计: 输入一个整数存入变量m(如m=25),并计算其平方数pm(pm=625)。如果m小于10,取其平方数pm的个位数; 如果m大于10,取其平方数pm的后两位数构成的数(如25),进而判断m是否为同构数。 程序代码: #include stdio.h void main() {int m,pm,y; scanf("%d",&m); pm=m*m; if(m10) y=pm%10;/*如m为1位整数,取其平方数的个位数*/ else y=pm%100;/*如m为2位整数,取其平方数的后两位数*/ if(m==y) printf("%d 是同构数\n",m); elseprintf("%d 不是同构数\n",m); } 运行情况: 输入数据 25 输出结果 25 是同构数 输入数据 15 输出结果 15不是同构数 3. 多重if语句 前面两种if语句一般都用于两个分支的选择结构。对于多个分支选择,可采用if-else-if语句。 格式: if (表达式1) 语句1; else if (表达式2) 语句2;