第3章程序语句3.1程序的语句与结构3.2顺序结构3.3选择结构3.4循环结构3.5本章小结【主要内容】基本程序语句;同类程序语句的特点、相互间的联系、选用条件等;流程图分析程序的效率训练;自顶向下算法设计的训练;读程序的训练;语句调试要点的介绍。【学习目标】掌握用基本语句进行顺序、选择和循环结构程序设计的方法;掌握表达式语句的格式,理解表达式与表达式语句的区别;掌握C语言的基本控制结构和基本控制语句的使用方法;掌握简单的程序调试方法;了解测试用例选取方法。1.C程序语句类型从程序流程的角度来看,程序可以分为三种基本结构,即顺序结构、分支(选择)结构和循环结构。这三种基本结构可以组成所有的复杂程序。C语言提供了多种语句来实现这些程序结构。C程序的执行部分是由语句组成的,程序的功能也是由执行语句实现的。C语句可分为如表3.1所示的几类。3.1程序的语句与结构表3.1C程序语句类型语句类型形式或种类变量声明语句数据类型标识符;表达式语句表达式;函数调用语句函数名(实际参数表);空语句;复合语句{语句A;语句B;…}控制语句(1)判断选取控制语句(2)循环控制语句(3)其他控制语句2.C程序的构成顺序结构、分支结构和循环结构可以相互嵌套,在循环中可以有分支、顺序结构,分支中也可以有循环、顺序结构。不管哪种结构,我们均可广义地把它们看成一个语句。在实际编程过程中常将这三种结构相互结合以实现各种算法,设计出相应程序。当要编程处理的问题较大时,编写出的程序就往往很长、结构重复多,造成可读性差,难以理解,解决这个问题的方法是将C程序设计成模块化结构。所谓模块化程序结构,就是把较长的、复杂的C程序分为若干模块,每个模块都编写成一个C函数,然后通过主函数调用函数及函数调用函数来实现一大型问题的C程序编写,因此可以说:C程序=主函数+子函数从程序的模块化结构来看,C程序由函数组成,必须有一个主函数,可以有0个到多个子函数,具体格式参见表3.2。表3.2C程序的构成C程序说明函数类型f1(形式参数);子函数f1的声明函数类型f2(形式参数);子函数f2的声明……全局变量说明;全局变量:所有函数都能用的变量voidmain()/*main函数开始*/主函数{局部变量的说明;局部变量:只有声明该变量的函数能用的变量语句序列;}/main函数结束*/函数类型f1(形式参数)/*f1函数开始*/子函数f1的定义{局部变量说明;语句序列;}/*f1函数结束*/函数类型f2(形式参数)子函数f2的定义{局部变量说明;语句序列;}/*f2函数结束*/…顺序结构的执行流程如图3.1所示,当A执行完后,无条件地执行B。语句按照书写的顺序由上至下逐条执行。3.2顺序结构图3.1顺序结构的流程【例3-1】顺序结构程序的例子。从键盘输入4位学生的学号和英语考试成绩,打印这4人的学号和成绩,最后输出4人的英语平均成绩。1/*顺序结构程序的例子*/2#includestdio.h3intmain()4{5intnumber1,number2,number3,number4;/*设4个学号*/6floatgrade1,grade2,grade3,grade4;/*设4个成绩*/7floatave;/*平均成绩*/89printf(inputnumber:\n);/*提示*/10/*输入4个学号*/11scanf(%d%d%d%d,&number1,&number2,&number3,&number4);12printf(inputgrader:\n);/*提示*/13/*输入4个成绩*/14scanf(%f%f%f%f,&grade1,&grade2,&grade3,&grade4);1516ave=(grade1+grade1+grade1+grade1)/4;/*计算平均分*/17printf(%d:%f\n,number1,grade1);18printf(%d:%f\n,number2,grade2);19printf(%d:%f\n,number3,grade3);20printf(%d:%f\n,number4,grade4);21printf(average=%f\n,ave);22return0;23}程序结果:inputnumber:1234inputgrader:869275641:86.0000002:92.0000003:75.0000004:64.000000average=86.000000可以看到,这个程序中,重复的类似变量和语句比较多,在后续的章节中可以看到改进的处理方法。对于选择程序结构,先判断给定的条件,再根据判断的结果来控制程序的流程。C有三种形式的条件语句,如表3.3所示。3.3选择结构表3.3C 条 件 语 句单路选择if(表达式)语句A;双路选择if语句if(表达式)语句A;else语句B;多路选择switch语句switch(表达式){case常量1:语句A;case常量2:语句B;default:语句C;}3.3.1二选一结构——if语句if语句有单路选择和双路选择两种基本形式,其对应的执行流程如图3.2所示。图3.2选择结构执行流程1.单路选择的if语句(1)基本格式:if(表达式)语句A;(2)执行过程:执行if语句时,首先计算紧跟在if后面一对圆括号中的表达式的值。如果表达式的值为非零(“真”),则执行其后的if子句,然后执行if语句后面的下一条语句;如果表达式的值为零(“假”),则跳过if子句,直接执行if语句后面的下一条语句。2.双路选择的if语句(1)基本格式:if(表达式)语句A;else语句B;(2)执行过程:执行if-else语句时,首先计算紧跟在if后面一对圆括号内表达式的值。如果表达式的值为非零(“真”),则执行if子句,然后跳过else子句,去执行if-else语句之后的下一条;如果表达式的值为零(“假”),则跳过if子句,会执行else子句,执行完之后接着执行if-else语句之后的下一条语句。说明:(1)图3.2中的语句A、B既可以是一条语句,也可以是复合语句。以后各种C语句的语法中凡出现“语句”的地方,其含义都相同。(2)图3.2中的表达式,理论上是条件或逻辑表达式,其结果为“真”或“假”。【例3-2】if语句的例子1。用if语句实现表3.4的功能。表3.4y与x的关系式1取值条件1x2y=2其余【解】if(x2)y=1;elsey=2;【例3-3】if语句的例子2。用if语句实现表3.5的功能。表3.5y与x的关系式1取值条件00≤x≤21x2y=2x0【解法一】if(x=0&&x=2)y=0;if(x2)y=1;if(x0)y=2;程序的执行过程是怎样的呢?直接读程序是不大容易看得清楚的,画出流程图就一目了然了,见图3.3。图3.3例3-3方法1从流程图3.3中,我们可以发现什么问题呢?(1)若x满足0≤x≤2,则在第一次遇到的条件判断中为真,得到结果y=0,后面再判断x2和判断x0就是多余做的工作了。(2)若x满足x2,则判断x0就是多余做的工作了。由此,我们可以对此程序得出评价——可读性好,效率不高。【解法二】由方法1改进后的流程如图3.4所示。if(x=0)&&(x=2)y=0;else{if(x2)y=1;elsey=2;}图3.4例3-3方法2改进后的程序同方法一相比,能看出x和y的关系吗?答:是不太容易看清楚,也就是说,此程序的可读性不好。那是不是有程序又清晰,执行效率又高的方法呢?我们看一下解法三。图3.5例3-3方法3【解法三】由方法2改进后的流程如图3.5所示。改进后的效果依然是从流程图3.5看起来比较直观,对应程序如下:y=0;if(x2)y=1;elseif(x0)y=2;到此,程序可读性好、执行效率高的目标就达到了。相对于程序语句,流程图可以帮助我们更直观清晰地观察程序的执行状况。下面举例说明嵌套的if-else语句中的对应规则。实例1:if()if()语句1;else语句2;实例2:if(){if()语句1;}else语句2;在上面的两个嵌套的条件语句if-else实例中,else和哪个if配对,都有两种可能性。为了避免二义性,C语言中有相应的规则。对于实例2,哪个if算是与else最近呢?从语法上看,{}中的内容是复合语句,应该是第一个if后语法上要求的语句,即属于这个if的一部分,所以else应该和第一个if匹配。注:复合语句是用{}括起来的一组语句。if语句的嵌套结构中,else总是与最近的if匹配的。【例3-4】if语句的例子3。求a、b、c三个整数中最大者(a、b、c的值通过键盘输入)。我们在第1章中已经介绍了设计算法的一般步骤,首先分析一下要处理的数据a、b、c之间可能的关系,可以有表3.6所示的情形。表3.6例3-4数据分析按照设计算法的第一个步骤,应该选择上述第一种情形来设计处理的流程,待算法设计完毕,再去测试数据的特殊情形和临界状态,如果有问题,再进行修改。【解法一】根据题目的要求,可以给出相应的顶部伪代码以及细化的伪代码,如表3.7~表3.9所示。一般情形a、b、c不等数据可能出现的情形特殊或临界状态a、b、c中有相等的表3.7例3-4方法一伪代码1顶部伪代码描述比较三个数a、b、c,找到其中的大者表3.8例3-4方法一伪代码2第一步细化输入三个数a、b、c先取a、b比较若a大,则a与c比较,取大者否则,b与c比较,取大者输出结果表3.9例3-4方法一伪代码3第二步细化输入三个数a、b、cifaba与c比较ifacmax=aelsemax=celseb与c比较ifbcmax=belsemax=c输出max图3.6例3-4方法一流程图表3.10例3-4特例情形测试用例情形结果a=b=cmax=ca=bmax取b、c中大者a=cmax取b、c中大者b=cmax取a、c中大者测试样例验证通过后,根据第二步细化结果或流程图就可以编程了,程序如下:1/*求出整数a、b、c三者中大者,放入max*/2#includestdio.h3intmain()4{5inta,b,c,max;67scanf(%d,%d,%d,&a,&b,&c);/*通过键盘输入a、b、c*/8if(ab)9{10if(ac)11{12max=a;13}14else15{16max=c;/*max取a、c中的大者*/17}18}19else20{21if(bc)22{23max=b;24}25else26{27max=c;/*max取b、c中的大者*/28}29}30printf(max=%d,max);/*输出结果*/31return0;32}成对括号,成对输入。上机练习输入程序时,括号的输入,最好养成一次输入一对的习惯,如main(){},然后在{}中输入程序语句。这样做的好处是在程序较长时,也不会有忘记配对括号的输入问题。输入时丢括号,也是初学者经常会犯的编译错误,往往要花大量的时间还不容易找到错误,这也是因为编译错误提示得并不明确。【解法二】方法一中,既然max记录最大值,则可以在a与b的比较时就使用它,改进的伪代码如表3.11所示,对应的流程如图3.7所示。表3.11例3-4方法二伪代码第一步细化输入三个数a、b、c先取a、b比较,大者放入maxmax与c比较,大者放入max输出结果图3.7例3-4方法二流程图根据图3.7所示的流程,对应条件ab的二选一语句很容易写出来:if(ab)max=a;elsemax=b;对应条件maxc的二选一语句,写的时候会有如下问题。实现情形一:if(maxc)printf(…);else{max=c;printf(…);}这种情形只有一个输出与流程不完全对应。实现情形二:if(maxc)max=max;elsemax=c;printf(…);其中的max=max语句实属多余。怎么让程序与流程图完全对应起来呢?实现情形三:把流程中的maxc条件改成maxc。if(ma