第4章 算法与控制结构 本章要求: 掌握C语句、C语言程序的结构关系; 理解算法及算法的表示; 掌握顺序结构程序设计 第4章 算法与控制结构 本章要求: 掌握C语句、C语言程序的结构关系; 理解算法及算法的表示; 掌握顺序结构程序设计 掌握选择控制语句——if语句、switch语句以及他们的嵌套应用; 掌握循环控制语句——for语句、while语句、do…while语句以及他们的嵌套应用; 掌握break语句、continue语句的应用; 能够运用3种结构进行综合程序设计。
第4章 算法控制结构 4.1 C语句结构 4.2 算法及算法的表示 4.3 顺序结构 4.4 选择结构 4.5 循环结构 第4章 算法控制结构 4.1 C语句结构 4.2 算法及算法的表示 4.3 顺序结构 4.4 选择结构 4.5 循环结构 4.6 其它控制语句 4.7 应用程序举例
4.1 C语句结构 C语句是C程序的最基本成分。C语句必须由分号“;”结尾,哪怕只有一个分号也能构成一个空语句。在C语言中只有“可执行语句”,没有“非执行语句”。 一个大型C程序的结构如图4-1所示,即一个C程序可以由若干个源程序文件组成,一个源文件可以由若干个函数和预编译命令组成,一个函数又由数据定义部分和执行部分组成。
4.1 C语句结构 C语句分为以下五类: 1.控制语句 控制语句用于控制程序的流程,以实现程序的各种结构方式。它们由特定的语句定义符组成。C语言有9种控制语句,分为以下三类: 1)条件判断语句:if语句、switch语句。 2)循环执行语句:for语句、do while语句、while语句。 3)转向语句:break语句、goto语句、continue语句、return语句。
4.1 C语句结构 2.函数调用语句 函数调用语句由函数名和实际参数加上分号“;”组成。其一般形式为: 函数名(实际参数表); 执行函数调用语句就是调用函数体并把实际参数赋予函数定义中的形式参数,然后执行被调函数体中的语句。 例如: printf("C Program"); /* 调用库函数,输出字符串 */
4.1 C语句结构 3.表达式语句 由表达式加上分号“;”组成。其一般形式为: 表达式; 执行表达式语句就是计算表达式的值。 例如: x=y+z; //赋值语句; y+z;//加法运算(表达式)语句,但计算结果没有保留,无意义 i++; //自增1语句,i值增1,等价于 i=i+1;
关于赋值语句 是表达式语句中使用最多的语句 赋值语句是由赋值表达式再加分号构成的表达式语句。 其一般形式为: 变量=表达式; 功能:将赋值运算符右侧“表达式”的值赋给左侧的变量。 例如,以下均为赋值表达式: x=3 (x的值为3) y=z=-1 (等价于y=(z=-1),y和z的值都为-1) a=(b=10)/(a=2) (b的值为10,a的值为5) y=(7+6)%5/3 (y的值为1) 若在赋值表达式后面加上“;”,则构成赋值语句: x=3; y=z=-1; a=(b=10)/(a=2); y=(7+6)%5/3;
关于赋值语句 说明: 1.赋值符“=” 右边的表达式可以又是一个赋值表达式 因此,下述形式是正确的:变量=(变量=表达式); 其展开之后的一般形式为:变量=变量=…=表达式; 例如: a=b=c=d=e=5;按照赋值运算符的右结合性,因此实际上等效于: e=5; d=e; c=d; b=c; a=b;
2.注意变量说明中给变量赋初值和赋值语句的区别。 关于赋值语句 2.注意变量说明中给变量赋初值和赋值语句的区别。 给变量赋初值是变量说明的一部分,赋初值后的变量与其后的其它同类变量之间仍必须用逗号间隔,而赋值语句则必须用分号结尾。
3.在变量说明中,不允许连续给多个变量赋初值。 如下述说明是错误的: int a=b=c=5; 关于赋值语句 3.在变量说明中,不允许连续给多个变量赋初值。 如下述说明是错误的: int a=b=c=5; 必须写为 int a=5,b=5,c=5; 而赋值语句允许连续赋值。
4.注意赋值表达式和赋值语句的区别。赋值表达式是一种表达式,它可以出现在任何允许表达式出现的地方,而赋值语句则不能。 关于赋值语句 4.注意赋值表达式和赋值语句的区别。赋值表达式是一种表达式,它可以出现在任何允许表达式出现的地方,而赋值语句则不能。 下述语句是合法的:if((x=y+5)>0) z=x; 下述语句是非法的:if((x=y+5;)>0) z=x; 说明:右边的表达式可以是变量、常量、函数调用等表达式。
5.赋值语句中的“=”称为赋值号,它不同于数学中的等号 关于赋值语句 5.赋值语句中的“=”称为赋值号,它不同于数学中的等号 例如: A=A+1在数学中是不成立的,但在程序设计中表示取变量A单元中的值,将其加1后,仍然放回到A变量的存储单元
关于赋值语句 6、赋值符号“=”左边一定只能是变量名,不能是常量、符号常量、表达式。 例如:z=x+y; /*将变量x和变量y的值的和赋值给变量z */ 下面的赋值语句都是错的: 5=x; /* 左边是常量 */ sin(x)=20; /* 左边是函数调用,即是表达式 */
关于赋值语句 7.赋值符号“=”两边的数据类型一般要求应一致。 如果两边的类型不同,则以左边变量或对象属性的数据类型为基准,如果右边表达式结果的数据类型能够转换成左边变量的数据类型,则先强制转换后,赋值给左边的变量。 若都是数值型,但精度不同,强制转换成左边变量的数据精度。 例如,有定义 int x; float y; x=3.5415926; /* 取其整数赋值给x,x的值为4 */ y=123; /* 将整数123转换实数123.0,赋值给y, 执行后,y的值是123.0 */
4.1 C语句结构 4.空语句 只有一个分号的语句,它什么也不做。有时用来作被转向点,或循环语句中的循环体(循环体是空语句,表示循环体什么也不做)。 例如:下面的循环体为空语句。 while(getchar()!=′\n′) ; 5.复合语句 把多个语句用花括号“{ }”括起来组成的语句称复合语句。在程序中应把复合语句看成是单条语句,而不是多条语句。 例如,下面就是一条复合语句。 { x=y+z; a=b+c; printf("%d%d",x,a); }
4.2 算法及算法的表示 4.2.1 算法概述 算法概念 请复习教材P.69.~P.74. 广义: 算法是为完成一项任务所应当遵照的一步一步的规则的、精确的、无歧义的描述,它的总步数是有限的。 狭义: 算法是解决一个问题采取的方法和步骤的描述
4.2 算法及算法的表示 4.2.2 算法的特性 1.有穷性:算法须在执行有穷多个计算步骤后终止 2.确定性:算法的每个步骤必须都是精确定义的、无二义性的; 3.有效性:算法中的每一个步骤必须有效地执行,并能得到确定结果; 4.输入:一个算法中可以没有输入,也可以有一个或多个输入信息 5.输出:一个算法应有一个或多个输出
4.2 算法及算法的表示 4.2.3 算法的表示 一、 自然语言与伪代码表示算法 自然语言就是指人们日常使用的语言,可以是汉语、英语或其它语言。 伪代码是用介于自然语言和计算机语言之间的文字和符号(包括数学符号)来描述算法。
4.2 算法及算法的表示 二、 用流程图表示算法 1.常用的流程符号 l 起止框:表示算法的开始和结束。
4.2 算法及算法的表示 算法的N—S流程图 三、 用N-S流程图表示算法 例:输入10个数,打印输出其中最大的数。
4.2 算法及算法的表示 说明:上面介绍的算法表示是给人阅读和交流的,即是为帮助程序开发人员阅读、编写程序而设计的一种辅助工具,因此算法表述中的文字和符号只要符合人们的约定和习惯即可,人们将算法流程图用计算机语言(如C语言)编写程序时,必须使用符合其语法规则,否则计算机便不能处理。
4.3 顺序结构 例4-3 请问:这个程序是如何执行的? #include "stdio.h" void main() 4.3 顺序结构 例4-3 编一程序,从键盘输入一个大写字母,要求改用小写字母输出(提示:大写字母对应的ASCII码序号比相应的小写字母的ASCII码序号小32)。 请问:这个程序是如何执行的? #include "stdio.h" void main() {char c1,c2; c1=getchar(); printf("%c,%d\n",c1,c1); c2=c1+32; printf("%c,%d\n",c2,c2); } 是从上至下依次顺序执行的 程序运行情况:
4.3 顺序结构 顺序结构程序是按照语句的先后顺序依次执行语句的程序。
4.3 顺序结构 补充例 编一程序,求一内半径r1=10cm外半径r2=20cm的球环的体积。要求按四舍五入保留到小数点后4位。 定义变量 r1,r2,vol 输入变量r1,r2 计算体积vol=4/3*pi*(r23-r13) 输出体积vol #define PI 3.1415926 // 定义符号常量PI代表π void main() { double r1,r2; // r1,r2 表示球的内外半径 double vol; // vol表示体积 printf("Enter r1,r2="); scanf("%lf,%lf",&r1,&r2); // 输入double数据使用者%lf格式控制 vol=4.0/3.0 * PI * (r2*r2*r2 - r1*r1*r1); printf( "V=%10.4lf", vol); } 程序运行结果如下:
4.4 选择结构 例: 输入三个数,然后输出其中最大的数 算法的N—S流程图 算法的N—S流程图
4.4 选择结构 4.4.1 if条件语句 if语句有单分支、双分支和多分支等结构 1.单分支if语句。 使用格式: if (表达式) 语句; 执行过程:如果表达式的值为真,则执行其后的语句, 否则不执行该语句。其过程可表示为图4-13。 图4-13 单分支选择结构的执行过程
4.4 选择结构 说 明: (1)表达式:一般为关系表达式、逻辑表达式,也可为算术表达式,其值按非零为True,零为False进行判断。 4.4 选择结构 说 明: (1)表达式:一般为关系表达式、逻辑表达式,也可为算术表达式,其值按非零为True,零为False进行判断。 (2)在if语句中,条件判断表达式必须用括号括起来,在语句之后必须加分号。 (3)如果当条件成立要执行多个语句,就要使用“{ }”来构成复合语句。 例:已知两个数x和y,比较它们的大小,使得x大于y。 if (x<y) {t=x;x=y;y=t;} /*t为中间变量*/ 图4-13 单分支选择结构的执行过程
4.4 选择结构 2.双分支结构if…else….语句 使用格式: if (表达式) <语句1>; else <语句2>; 语句的执行过程如图4-14所示,即当表达式的值为非零(条件成立)时执行<语句1>,否则执行else后面<语句2>。 例如:输出x,y两个中值较大的一个值。 if (x>y) printf("%d" x); else printf("%d" y); 图4-14 双分支选择结构执行过程
4.4 选择结构 3.if…else if语句(多分支结构) 语句形式: if (表达式1) 语句1; else if (表达式2) 语句2; ……….. else if (表达式n) 语句n; else 语句n+1; 图4-15 多分支if语句执行过程
4.4 选择结构 例4-5 输入一组学生成绩,评定其等级。方法是:90~100分为“A”,80~89分为“B”,70~79分为“C”,60~69分为“D”,60分以下为“E”。 程序段如下: void main() { int score; scanf(“%d”,&score); if (score >=90) printf("A"); else if (score >=80) printf("B"); else if (score >=70) printf("C"); else if (score >=60) printf("D"); else printf("E"); } 注意教材的程序书写格式不好
4.4 选择结构 例4-5 输入一组学生成绩,评定其等级。方法是:90~100分为“A”,80~89分为“B”,70~79分为“C”,60~69分为“D”,60分以下为“E”。 程序段如下: void main() { int score; scanf(“%d”,&score); if (score >=90) printf("A"); else if (score >=80) printf("B"); else if (score >=70) printf("C"); else if (score >=60) printf("D"); else printf("E"); } 思考:该程序段中每个else if语句中的表达式都作了简化,例如第一个else if的表达式本应写为“x>=80 && x<90”,而写为“x>=80”,为什么能作这样的简化? 如果该程序段改写成下面两种形式是否正确?
第一种形式: if (score >=60) printf("D"); else if (score >=70) printf("C"); else if (score >=80) printf("B"); else if (score >=90) printf("A"); else printf("E"); 完全错误
第二种形式: if (score < 60) printf("E"); else if (score <70) printf("D"); else if (score <80) printf("C"); else if (score <90) printf("B"); else printf("A"); 完全正确
4.4 选择结构 算法思想:根据其字符和所对应的ASCII码值进行判断 4.4 选择结构 补充例题:输入一个字符,判断其属于大写字母、小写字母、数字、控制字符或其他字符。 #include"stdio.h" void main() { char c; printf("input a character: "); c=getchar(); if(c<32) printf("This is a control character\n"); else if(c>='0'&&c<='9') printf("This is a digit\n"); else if(c>='A'&&c<='Z') printf("This is a capital letter\n"); else if(c>='a'&&c<='z') printf("This is a small letter\n"); else printf("This is an other character\n"); } 算法思想:根据其字符和所对应的ASCII码值进行判断
4.4 选择结构 4. 使用if语句中应注意以下问题 例如: if(a=5) 语句; if(b) 语句; 都是允许的。 4.4 选择结构 4. 使用if语句中应注意以下问题 (1)在三种形式的if语句中,在if关键字之后均为表达式。该表达式通常是逻辑表达式或关系表达式,但也可以是其它表达式,如赋值表达式等,甚至也可以是一个变量。 例如: if(a=5) 语句; if(b) 语句; 都是允许的。 (2)在if语句中,条件判断表达式必须用括号括起来, 在语句之后必须加分号。 (3)在if语句的三种形式中,所有的语句应为单个语句,如果要想在满足条件时执行一组(多个)语句,则必须把这一组语句用{} 括起来组成一个复合语句。但要注意的是在}之后不能再加分号。
4.4 选择结构 5.if语句的嵌套 当if语句中的执行语句又是if语句时,则构成了if 语句嵌套的情形。其一般形式可表示如下: 4.4 选择结构 5.if语句的嵌套 当if语句中的执行语句又是if语句时,则构成了if 语句嵌套的情形。其一般形式可表示如下: if (表达式1) { if (表达式2) 语句1; } else if (表达式3) 语句2; if(表达式1) if (表达式2) 语句; 在嵌套内的if语句可能又是if-else型的,这将会出现多个if和多个else重叠的情况,这时要特别注意if和else的配对问题。
4.4 选择结构 在嵌套内的if语句可能又是if-else型的,这将会出现多个if和多个else重叠的情况,这时要特别注意if和else的配对问题。 例如: if(表达式1) if(表达式2) 语句1; else 语句2; 其中的else究竟是与哪一个if配对呢? 应该理解为: 还是应理解为: if(表达式1) if(表达式1) if(表达式2) if(表达式2) 语句1; 语句1; else else 语句2; 语句2;
if(表达式1) if(表达式2) 语句1; else 语句2; 4.4 选择结构 C语言规定,else 总是与它前面最近的if配对 因此对上面的例子应按前一种情况理解,即: if(表达式1) if(表达式2) 语句1; else 语句2;
4.4 选择结构 C语言规定,else 总是与它前面最近的if配对 如果要实现后一种情况,则可使用“{ }”将 if(表达式2) 语句1 括起来,就可实现。即写成如下形式: if(表达式1) { if(表达式2) 语句1; } else 语句2;
结果为? x=0 例:分析下面程序段的输出结果: x=0; if (x>=1) if(x>5) x++; else x--; 4.4 选择结构 例:分析下面程序段的输出结果: x=0; if (x>=1) if(x>5) x++; else x--; printf(“x=%d”,x); 结果为? x=0
例:分析下面程序段的输出结果: x=0; if (x>=1) if(x>5) x++; else x--; 4.4 选择结构 例:分析下面程序段的输出结果: x=0; if (x>=1) if(x>5) x++; else x--; printf(“x=%d”,x); 要想使结果为: x=-1 怎么修改程序? { }
4.4 选择结构 例4-6 输入2个数,比较它们的大小关系。 P.79. 例4-6(略)
4.4 选择结构 4.4.2 条件运算符和条件表达式 条件运算符为 ? : 它是一个三目运算符。 条件表达式的一般形式为: 何时用它? 条件语句中只执行单个的赋值语句时用 4.4.2 条件运算符和条件表达式 条件运算符为 ? : 它是一个三目运算符。 条件表达式的一般形式为: 表达式1? 表达式2 :表达式3 其求值规则为:如果表达式1的值为真,则以表达式2 的值作为条件表达式的值,否则以表达式3的值作为整个条件表达式的值。 条件表达式通常用于赋值语句之中。例如条件语句: if(a>b) max=a; else max=b; 可用条件表达式写为 max=(a>b)?a:b; 执行该语句的语义是:如a>b为真,则把a 赋予max,否则把b 赋予max。 为什么要用这种形式? 效率高,程序简洁
4.4 选择结构 使用条件表达式时,还应注意以下几点: 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又是一个条件表达式。
4.4 选择结构 例4-7 将输入的大写字母转换成相应的小写字母 void main() {char ch; 4.4 选择结构 例4-7 将输入的大写字母转换成相应的小写字母 void main() {char ch; scanf("%c",&ch); ch=(ch>='A' && ch<='Z')? ch+32:ch; printf("%c",ch); } 等价 if (ch>='A' && ch<='Z') ch=ch+32;
请回顾程序例4-5(P.77.) 根据输入的一组学生成绩,评定其等级
4.4 选择结构 注意教材的程序书写格式不好 即使这样漂亮的格式,但程序冗长,可读性差 属于多分支的控制机构问题 4.4 选择结构 例4-5 输入一组学生成绩,评定其等级。方法是:90~100分为“A”,80~89分为“B”,70~79分为“C”,60~69分为“D”,60分以下为“E”。 程序段如下: void main() { int score; scanf(“%d”,&score); if (score >=90) printf("A"); else if (score >=80) printf("B"); else if (score >=70) printf("C"); else if (score >=60) printf("D"); else printf("E"); } 注意教材的程序书写格式不好 即使这样漂亮的格式,但程序冗长,可读性差 属于多分支的控制机构问题
解决类似于这种多分支控制结构的问题 有更好的方法吗?
4.4 选择结构 4.4.3 switch语句(开关语句) switch语句使用的一般形式为: switch(表达式) { case 常量表达式1: 语句1; case 常量表达式2: 语句2; … case 常量表达式n: 语句n; default : 语句n+1; } 处理多分支控制结构问题,使程序简洁明了
4.4 选择结构 图4-16 switch语句的执行过程 语句的执行过程如图4-7:首先计算表达式的值,并逐个与其后的常量表达式值相比较,当表达式的值与某个常量表达式的值相等时,即执行其后的语句,然后不再进行判断,继续执行后面所有case后的语句。如表达式的值与所有case后的常量表达式均不相同时,则执行default后的语句。
4.4 选择结构 例4-8 将例4-5使用switch语句来实现的程序如下: void main() { int score; printf(“Enter score=?”); scanf(“%d”,&score); switch(score/10) /*确定分数范围*/ { case 10: case 9: printf("A"); case 8: printf("B"); case 7: printf("C"); case 6: printf("D"); Default:printf("E"); } 教材上的强制类型转换(int)没有必要 程序运行后,结果如下: Enter x=? 75 <回车> CDE
4.4 选择结构 说明: 在switch语句中,“case 常量表达式”只相当于一个语句标号,并不是在该处进行条件判断,表达式的值和某标号相等则转向该标号执行,但不会在执行完该标号的语句后自动跳出整个switch 语句,将继续执行所有后面case语句的情况。 为了避免上述情况, C语言还提供了一种break语句,专用于跳出switch语句。 break 语句使用形式为:break;在每一case语句之后增加break 语句,使每一次执行之后均可跳出switch语句。
case 常量表达式n: 语句n; break; default : 语句n+1; } 4.4 选择结构 使用switch语句的通常形式为: switch(表达式) { case 常量表达式1: 语句1; break; case 常量表达式2: 语句2; break; … case 常量表达式n: 语句n; break; default : 语句n+1; }
switch expr 语句组1 break; 语句组2 语句组n 语句组 …... const 1 const 2 const n default case
4.4 选择结构 例4-8 程序可修改为 void main() {int score; printf("Enter score=?"); 4.4 选择结构 例4-8 程序可修改为 void main() {int score; printf("Enter score=?"); scanf("%d",&score); switch(score/10) { case 10: case 9: printf("A"); break; case 8: printf("B"); break; case 7: printf("C"); break; case 6: printf("D"); break; Default:printf("E"); } 程序运行后,结果如下: Enter x=? 75 <回车> C
4.4 选择结构 void main() { int a; printf("input integer number: "); 补充例题:输入1~7(分别代表星期一到星期天),输出相应的内容。 void main() { int a; printf("input integer number: "); scanf("%d",&a); switch (a) { case 1:printf("Monday\n");break; case 2:printf("Tuesday\n"); break; case 3:printf("Wednesday\n");break; case 4:printf("Thursday\n");break; case 5:printf("Friday\n");break; case 6:printf("Saturday\n");break; case 7:printf("Sunday\n");break; default:printf("error\n"); } }
1.在case后的各常量表达式的值不能相同,否则会出现错误。 4.4 选择结构 使用switch语句时还应注意以下几点: 1.在case后的各常量表达式的值不能相同,否则会出现错误。 2.在case后,允许有多个语句,可以不用{ }括起来。 3.各case和default子句的先后顺序可以变动,而不会影响程序执行结果。 4.default子句可以省略不用。
4.4 选择结构 4.4.4 选择结构的嵌套 在if语句的分支中可以完整地嵌套另一if语句或switch语句,同样switch语句每一个case分支中都可嵌套另一完整的if语句或switch语句。 下面是两种正确的嵌套形式:
4.4.4 选择结构的嵌套
4.4 选择结构 关于选择结构嵌套说明: 1)嵌套只能在一个分支内嵌套,不出现交叉。其嵌套的形式将有很多种,嵌套层次也可以任意多。 2)在swicth结构中,每一个case分支可以完整包含另一个if…else…结构,也可完整包含另一个swicth结构。 为了便于阅读和维护,建议在写含有多层嵌套的程序时,使用缩进对齐方式。
4.4 选择结构 4.4.5 选择结构程序举例 例4-9设计一个求解一元二次方程的程序,要求考虑实根、虚根等情况。 分析:根据输入的a,b,c的值, 一元二次方程有以下几种可能: 1)a=0,不是二次方程。 2)判别式b2-4ac=0,有两个相等实根。 3)判别式b2-4ac>0,有两个不等实根。 4)判别式b2-4ac<0,有两个共轭复根。 图4-17 求解一元二次方程的算法NS图
复合语句 复合语句 4.2 选择结构 思考:若有float x,y; 如果判断x是否等于y如何进行? #include <math.h> void main() { float a, b, c, x1, x2, d; printf("Enter number a,b,c="); scanf("%f,%f,%f",&a,&b,&c); if(abs(a)>0.00001) /*如果a不等于0,思考为什么不写成a!:=0*/ {d= b*b- 4*a*c; if (d>=0) /* 实根情况*/ {x1=(-b+sqrt(d))/(2*a); x2=(-b-sqrt(d))/(2*a); printf("x1=%f,x2=%f\n",x1,x2); } else /* 虚根情况*/ {x1=-b/(2*a); x2=sqrt(fabs(d))/(2*a); printf("x1=%f + %fi\n",x1,x2);/* 输出复数形式的虚根*/ printf("x2=%f - %fi\n",x1,x2); else printf(“不是一元二次方程”); 思考:若有float x,y; 如果判断x是否等于y如何进行? 复合语句 复合语句
例4-10 计算器程序。用户输入运算数和四则运算符,输出计算结果。 4.4 选择结构 4.4.5 选择结构程序举例 例4-10 计算器程序。用户输入运算数和四则运算符,输出计算结果。 void main() { float a,b; char c; printf("input expression: a+(-,*,/)b\n"); scanf("%f%c%f",&a,&c,&b); switch(c) { case '+': printf(" =%f\n",a+b);break; case '-': printf(" =%f\n",a-b);break; case '*': printf(" =%f\n",a*b);break; case '/': if (b==0) printf("Data error:data divided by zero \n"); else printf(" =%f\n",a/b);break; default: printf("input error\n"); }
课堂练习1 根据输入字母输出字符串,如果 输入m则输出Good Morning! 输入n则输出Good Night! 输入h则输出Hello! 输入其他字符则输出?????
#include <stdio.h> main() { int ch; printf("Enter m or n or h or other:"); ch=getchar(); switch(ch) { case 'm': printf("\nGood morning!\n");break; case 'n': printf("\nGood night!\n"); break; case 'h': printf("\nHello!\n"); break; default : printf("\n????????\n"); break; } 思考:可以用if 语句吗?
课堂练习2 已知某公司员工的保底薪水为500,某月所接工程的利润profit(整数)与利润提成的关系如下(计量单位:元):
算法设计要点: 为使用switch语句,必须将利润profit与提成的关系,转换成某些整数与提成的关系。分析本题可知,提成的变化点都是1000的整数倍(1000、2000、5000、……), 如果将利润profit整除1000,则当: profit≤1000 对应0、1 1000<profit≤2000 对应1、2 2000<profit≤5000 对应2、3、4、5 5000<profit≤10000 对应5、6、7、8、9、10 10000<profit 对应10、11、12、……
为解决相邻两个区间的重叠问题,最简单的方法就是利润profit先减1(最小增量),然后再整除1000即可:
main() { long profit; int grade; float salary=500; /*500为保底薪水*/ printf("Input profit: "); scanf("%ld", &profit); grade= (profit – 1.0) / 1000.0; /*将利润-1、再整除1000,转化成 switch语句中的case标号*/
switch(grade) { case 0: break; /*profit≤1000 */ case 1: salary += profit*0.1; break; /*1000<profit≤2000 */ case 2: case 3: case 4: salary += profit*0.15; break; /*2000<profit≤5000 */ case 5: case 6: case 7: case 8: case 9: salary += profit*0.2; break; /*5000<profit≤10000 */ default: salary += profit*0.25; /*10000<profit */ } printf("salary=%.2f\n", salary);
作业(P.102.) 二:1、2、4、5 三:1 五:1、 上机作业(实验教程): 一:仔细阅读实验3的一、二、三、内容(P.18.~P.25.) 二:实验内容(P.25.)的2)、3)、6)、9)、14),其中的2)、14)为必做题。
4.5 循环结构 问题的提出:编写程序:在屏幕上输出整数1~10,每个整数之间留一个空格。 #include <stdio.h> 4.5 循环结构 问题的提出:编写程序:在屏幕上输出整数1~10,每个整数之间留一个空格。 #include <stdio.h> void main() { printf(“1 2 3 4 5 6 7 8 9 10 \n”); } 如果题目要输出1 ~1000,那该如何设计? 用循环结构来实现! 从输出1开始,每次输出一个比前一次大1的整数,重复10次(1000次)!
用for语句实现“在屏幕上输出整数1~10” #include<stdio.h> void main( ) { int i; for(i=1; i<=10; i++) printf(〝%d 〞,i); } 如何输出整数1~100? 10改为100即可 输出结果为: 1 2 3 4 5 6 7 8 9 10
用while语句实现“在屏幕上输出整数1~10” #include<stdio.h> void main() { int i=1; while(i<=10) { printf(〝%d 〞,i); i=i+1; } 输出结果为: 1 2 3 4 5 6 7 8 9 10
用do-while语句实现“在屏幕上输出整数1~10” #include<stdio.h> void main() { int i=1; do { printf(〝%d 〞,i); i++; } while (i<=10); } 输出结果为: 1 2 3 4 5 6 7 8 9 10
循环结构是程序中一种很重要的结构。其特点是:在给定条件成立时,反复执行某程序段,直到条件不成立为止。 给定的条件称为循环条件 反复执行的程序段称为循环体。 1) 用for语句; 2) 用while语句; 3) 用do-while语句; 4) 用goto语句和if语句构成循环; goto语句不提倡
练习1:用goto语句实现“在屏幕上输出整数1~10” #include<stdio.h> void main() { int i=1; loop: if(i<=10) { printf(〝%d 〞,i); i=i+1; goto loop; } 不要写包含goto语句的程序
4.5.1 用for语句实现循环 for([表达式1] ;[ 表达式2] ;[ 表达式3]) 一般形式: 循环体语句; 执行流程: 求解表达式2 循环体语句 假(0) 真(非0) 求解表达式1 求解表达式3 执行流程:
for(循环变量赋初值;循环条件;循环变量增值) { 循环体语句; } 它的执行过程如下: 1) 先求解表达式1。 2)求解表达式2,若其值为真(非0),则执行for语 句中指定的内嵌语句,然后执行下面第3)步;若其值为假(0),则结束循环,转到第5)步。 3) 求解表达式3。 4) 转回上面第2)步继续执行。 5) 循环结束,执行for语句下面的一个语句。
练习2:用for语句实现“在屏幕上输出整数1~10” #include<stdio.h> void main() { int i; for(i=1; i<=10; i++) printf(〝%d 〞,i); } i≦10 假(0) 真(非0) i=1 i++ 打印 i 开始 结束 输出结果为: 1 2 3 4 5 6 7 8 9 10
课堂练习:用for语句实现在屏幕上输出30个 “ *” 此处的循环控制变量只起控制循环次数的作用 课堂练习:用for语句实现在屏幕上输出30个 “ *” i≦30 假(0) 真(非0) i=1 i++ 打印“*” 开始 结束 #include <stdio.h> void main( ) { int i; for(i=1; i<=30; i++) printf(〝*〞); } i 可以改为 j 吗?
#include <stdio.h> void main( ) { int i,sum=0; sum=sum+i 假(0) 真(非0) i=1 i++ 初始化 sum=0 打印sum 开始 结束 练习3:用for语句计算sum=1+2+3+...+99+100 #include <stdio.h> void main( ) { int i,sum=0; for(i=1;i<=100;i++) sum=sum+i; printf("sum=%d\n",sum); }
练习3:用for语句计算sum=1+2+3+...+99+100 #include <stdio.h> void main( ) { int i,sum=0; for(i=1;i<=100;i++) { sum=sum+i; printf("sum=%d\n",sum); } } #include <stdio.h> void main( ) { int i,sum=0; for(i=1;i<=100;i++) sum=sum+i; printf("sum=%d\n",sum); } 请思考:程序改为如右,结果如何?
课堂练习:求任意100个数的和 #include <stdio.h> void main( ) 初始化:total=0.0 { int i; float x, total=0.0; for(i=1;i<=100;i++) { scanf(“%f”,&x); total+=x; /*total=total+x; */ } printf(“total=%f\n",total); 初始化:total=0.0 for i=1 to 100 输入x total=total+x 输出total 请思考: 去掉该复合语句括号,结果如何?
#include <stdio.h> void main( ) { int i,fact=1; 练习4:用for语句计算fact=5!=1*2*3*4*5 #include <stdio.h> void main( ) { int i,fact=1; for(i=1;i<=5;i++) fact=fact*i; /* fact*=i */; printf(“fact=%d\n",fact); } 请思考:fact=0;结果如何?
#include <stdio.h> void main( ) { int i,fact=1; 练习4:用for语句计算fact=10! 再请思考:改为求10的阶乘,程序结果又如何? #include <stdio.h> void main( ) { int i,fact=1; for(i=1;i<=10;i++) fact=fact*i; /* fact*=i */; printf(“fact=%d\n",fact); }
fact=fact*i; /* fact*=i */; printf(“fact=%f\n",fact); } 练习4:用for语句计算fact=10! void main( ) { int i; float fact=1.0; for(i=1;i<=10;i++) fact=fact*i; /* fact*=i */; printf(“fact=%f\n",fact); } 该程序是求阶乘的正确形式
课堂练习:用for语句计算任意一个正整数n的阶乘fact=n! #include <stdio.h> void main( ) { int i,n; float fact=1.0; printf(“please input n(n>=0):\n"); scanf(“%d”,&n); for(i=1;i<=n;i++) fact=fact*i; /* fact*=i */; printf(“fact=%f\n",fact); } 再请思考:
4.5.1 循环结构(for) 有关for语句的几点说明: 1)for语句的一般形式中的“表达式1”可以省略,此时应在for语句之前给循环变量赋初值。注意省略表达式1时,其后的分号不能省略。 例如:i=1; for(; i<=n; i++) t=t*i; 执行时,跳过“求解表达式1”这一步,其他不变。
4.5.1 循环结构(for) 有关for语句的几点说明: 2)如果表达式2省略,即不判断循环条件,循环无终止地进行下去。也就是认为表达式2 始终为真,在形式上构成死循环。 例如: for(i=1; ; i++) t=t*i; 表达式1是一个赋值表达式,表达式2省略。
3)表达式3也可以省略,但此时程序设计者应另外设法保证循环能正常结束,表达式3的功能放到循环体内。例如: 4.5.1 循环结构(for) 有关for语句的几点说明: 3)表达式3也可以省略,但此时程序设计者应另外设法保证循环能正常结束,表达式3的功能放到循环体内。例如: for(i=1;i<=n;) { t=t*i; i++; }
4)可以省略表达式1和表达式3,只有表达式2,即只给循环条件,如: for(;i<=n;) { t=t*i; i++; }
即不设初值,不判断条件(认为表达式2为真值),循环变量不增值。循环为“死循环”。 4.5.1 循环结构(for) 有关for语句的几点说明: 5)3个表达式都可省略,例如: for(;;) 语句; 即不设初值,不判断条件(认为表达式2为真值),循环变量不增值。循环为“死循环”。
Break 退出循环体 回顾练习3:用for语句计算sum=1+2+3+...+99+100 #include <stdio.h> void main( ) { int i,sum=0; for(i=1;i<=100;i++) sum=sum+i; printf("sum=%d\n",sum); } #include <stdio.h> void main( ) { int i,sum=0; i=1; for(; ;) { if (i>100) break; sum+=i++; } printf("sum=%d\n",sum); Break 退出循环体
6)表达式1和表达式3可以是一个简单的表达式,也可以是逗号表达式,即包含一个以上的简单表达式,中间用逗号间隔。 例如: 4.5.1 循环结构(for) 有关for语句的几点说明: 6)表达式1和表达式3可以是一个简单的表达式,也可以是逗号表达式,即包含一个以上的简单表达式,中间用逗号间隔。 例如: for(t=1, i=1;i<=n; t=t*i, i++); 等价于: t=1; for(i=1;i<=n;i++) t=t*i; 更为简洁 此分号表示循环体为空语句,并非表示for语句结束
课堂练习:阅读下列程序,说明其功能。 #include <stdio.h> void main( ) { int n=0; printf(“input a string:\n”); for(; getchar( )!=‘\n’ ; n++); printf(“%d”,n); } 程序的功能是:计算用户输入的字符数,当输入是回车符时统计结束
课堂练习:编写一个程序,计算半径为0.5,1.5,2.5,3.5,4.5,5.5mm的圆的面积 #define PI 3.1415926 main() { float r,s; for(r=0.5;r<=5.5;r++) { s=PI*r*r; printf(“r=%f,s=%f\n”,r,s); } 循环变量还可以是实型
作业(P.102.) 一:1、2 二:1、2、4、5 四:1 五:1、2 上机实验题: 1. 任意输入n个整数,输出其中的每个偶数并最 后输出奇数之和和偶数之和。 2. 用for循环语句编程求
其他循环语句 问题的提出 从终端键盘上输入若干个正整数,编程求这些正整数的和,最后以输入0作为结束条件。 虽然仍然是重复操作和重复计算,但是由于次数未知,所以不便于用for循环语句实现,而用其他循环语句。
4.5.2 用while语句实现循环 while(表达式) 一般形式: 此处的表达式是循环条件 循环体语句; 流程图: 假(0) 真(非0) 问:该流程图和教材P.80.的图4-18一样吗?
4.5.2 用while语句实现循环 while(表达式) 一般形式: 循环体语句; 流程图: N-S图: 当条件成立 语句组 假(0) 真(非0)
练习1:用while语句实现“在屏幕上输出整数1~20” #include<stdio.h> main( ) { int i=1; while(i<=20) { printf(〝%d 〞,i); i=i+1; } 当i≤20 输出I i=i+1 初始化:i=1
特点:先判断表达式,后决定是否执行循环体 说明: 先判断后执行,循环体有可能一次也不执行 循环体可为任意类型语句 下列情况,退出while循环 条件表达式不成立(为零) 循环体内遇break,return,goto 无限循环: while(1) 循环体语句; 表达式 循环体语句 假(0) 真(非0)
一般情况下,while型循环最适合于: 知道控制循环的条件为某个关系表达式或逻辑表达式的值,而且该表达式的值会在循环中被改变的情况 如上例中的while(i<=20)中的 i<=20
从终端键盘上输入若干个正整数,编程求这些正整数的和,最后以输入0作为结束条件。 次数未知,但是循环体中的某个语句可以改变循环条件的值。 #include<stdio.h> main() { int n,sum=0; scanf(“%d”,&n); while(n>0) { sum+=n; } printf(〝sum=%d 〞,sum); sum=sum+n 输入数 n 初始化:sum=0 当 n>0 打印输出sum
例: 编程序求:s=1+2+3+……+100 sum=sum+i i=i+1 初始化:sum=0 i=1 当i≦100 打印输出sum
请思考:该问题可以用for循环语句实现吗? 例:编程序求:s=1+2+3+……+100 请思考:该问题可以用for循环语句实现吗? sum=sum+i i=i+1 初始化:sum=0 i=1 当i≦100 打印输出sum #include <stdio.h> main() { int i,sum=0; i=1; while(i<=100) { sum+=i; i++; } printf("%d",sum); 循环条件 循环初值 循环终值 循环体语句 循环变量增值
练习2:编写程序输出1~10的平方 1*1=1 #include <stdio.h> 2*2=4 main() 3*3=9 4*4=16 5*5=25 6*6=36 7*7=49 8*8=64 9*9=81 10*10=100 #include <stdio.h> main() { int i=1; while(i<=10) { printf("%d*%d=%d\n",i,i,i*i); i++; } 请思考:该问题可以用for循环语句实现吗?
练习3:输入20个整数,求它们的和以及平均值。 #include<stdio.h> main( ) { int i=0,sum=0,x; float ave; while(i<20) scanf("%d",&x); sum=sum+x; i++; } ave=sum/20.0; printf("sum=%d average=%f\n",sum,ave); 请思考:循环条件可以改为i<=20吗? 重复操作,输入一个数并累加,因此用循环 四个 思考:声明几个变量? i为循环控制变量 请思考:该问题可以用for循环语句实现吗?
例4-11 求n!,n由键盘输入(P.86.) 4.5.2 循环结构(while) 编程分析:求阶乘是一个累乘问题: n!=n*(n-1)*(n-2)*..2*1。 根据其特点 1!=1,2!=1*2,3!=2!*3…….n!=(n-1)!*n。 用变量fact代表阶乘值,fact的初值为1;变量i为循环控制变量,i从1变到n,每一步令fact=fact*i,则最终fact中的值就是n!。
请思考:该问题可以用for循环语句实现吗? 4.5.2 循环结构(while) 请思考:该问题可以用for循环语句实现吗? void main() { int n,i; long fact; printf("please input n(n>=0):"); scanf("%d",&n); fact=1; i=1; /*给变量fact、i赋初值*/ while(i<=n) { fact*=i; i++; } printf("%d!=%f\n",n,jct); } 程序中控制循环结束的变量i必须在循环体中被改变,否则,循环将无限进行下去,成为死循环。 变量fact可以定义为float类型吗? 请思考与讨论: 如果程序中变量fact定义为整数,程序运行会出现什么情况?
分析:其实是一个求累加和的问题: 只要把每项加数求出来就可以了 每项加数中的分母有规律:差为2 每项的符号不同 最后的累加和乘以4即可。 4.5 循环结构 例4-12 利用格里高利公式求: 直到最后一项的绝对值小于等于10-6为止。 分析:其实是一个求累加和的问题: 只要把每项加数求出来就可以了 每项加数中的分母有规律:差为2 每项的符号不同 最后的累加和乘以4即可。
4.5 循环结构 请思考:该问题可以用for循环语句实现吗? 请注意头文件 例4-12 利用格里高利公式求: 直到最后一项的绝对值小于10-6为止。 #include <math.h> void main() { float pi,t,n; int sign=1; pi=0.0; n=1.0; t=1.0; while (fabs(t) >= 1e-6) { t=sign/n; pi+=t; n+=2; sign=-sign; } pi=pi*4; printf("pi = %f\n",pi); } 请思考:该问题可以用for循环语句实现吗? 思考与讨论: 1)程序中控制循环结束的变量 t 是多项式中的通项。 2)如果程序中将变量n定义为整数,程序运行会出现什么情况? 请注意头文件
while循环语句小结: (1)循环次数的控制要正确; 可以通过循环变量控制:i<=20等 可以通过一些特殊条件来终止循环: while(getchar()!=′\n′) m++; (2)循环体包含一个以上的语句时,一定要用花括号括起来,否则,可能与程序要求不符。 (3)循环体内要有使循环趋向结束的语句,否则,引起死循环。
练习4:一个程序,计算半径为0.5,1.5,2.5,3.5,4.5,5.5mm的圆的面积 分析:此题要求计算6个不同半径的圆的面积。可以每次计算一个圆的面积,共需要计算6次。 规律:半径是有规律变化的,即从0.5mm 开始按1mm的规律递增。 解决:用while语句语句处理循环 请自己写出程序
4.5.3 用Do-while语句实现循环 一般形式: 执行流程: N-S图: do 请注意N-S图的表示形式 循环体语句; 表达式 假(0) 真(非0) 当表达式为真 循环体语句 执行过程:先执行循环体语句一次,再计算并判别表达式的值,若为真(非0)则继续循环,否则终止循环。
4.5.3 循环结构(do-while) 练习: 编程序求:s=1+2+3+……+100 真 假 s=0 i=1 s=0, i=1 i=i+1 i≦100? 真 假 s=s+i i=i+1 s=0, i=1 当i≦100 输出打印s
4.5.3 循环结构(do-while) 练习: 编程序求:s=1+2+3+……+100 请思考:该问题可以用for循环语句实现吗? void main() { int s=0,i=1; do { s=s+i; i++; } while (i<=100); printf("S=%d",s); } s=s+I i=i+1 s=0, i=1 当i≦100 输出打印s 请思考:该问题可以用while循环语句实现吗?
例4-13 计算 直到最后一项的绝对值小于1e-7时为止。 4.5.3 循环结构(do-while) 例4-13 计算 直到最后一项的绝对值小于1e-7时为止。 编程分析:使用递推方法,多项式的每一项与一个变量n对应,n的值依次为1,3,5,7,...,从多项式的前一项算后一项,只需将前一项乘一个因子:(-x2)/((n-1)*n)。用s表示多项式的值,用t表示每一项的值,程序如下: 所谓递推:就是对于要求解的值,由一个给定的初值,经过某种方法可以求得下一个值(新值)
4.5.3 循环结构(do-while) P.88. 请思考:该问题可以用for循环语句实现吗? 例4-13 计算 例4-13 计算 直到最后一项的绝对值小于1e-7时为止。 请思考:该问题可以用for循环语句实现吗? #include <math.h> void main() { double s,t,x; int n; printf("please input x:"); scanf("%lf",&x); t=x; n=1; s=x; do { n=n+2; t=-t*x*x/(n-1)/n; /* 计算通项 */ s=s+t; /* 累加求和 */ }while(fabs(t)>=1e-7); /* 当累加项的值大于1e-7继续循环 */ printf("sin(%f)=%lf",x,s); }
do-while语句特点:先执行循环体,后判断表达式 说明: 至少执行一次循环体 do~while可转化成while结构 假(0) 真(非0) While循环 循环体语句 表达式 假(0) 真(非0) Do-While循环
while和do~while循环的比较 输入1和11,分析输出结果 输入1时,结果为? 输入1时,结果均为55 #include <stdio.h> main( ) { int i,sum=0; scanf("%d",&i); do { sum+=i; i++; }while(i<=10); printf("%d",sum); } # include <stdio.h> main() { int i,sum=0; scanf("%d",&i); while(i<=10) { sum+=i; i++; }//while printf("%d",sum); } 结论:如果循环至少要执行一次, while和do-while语句可以相互替换。 输入11时,do-while循环结果为11,while循环结果为0 输入11时,结果为?
4.5.4 三种循环语句比较
4.5.4 三种循环语句比较 循环次数已知时一般用for循环,未知时用while和do-while 说明: 1)3种循环中for语句功能最强大,使用最多,一般情况的循环都可使用for语句实现。for语句与while语句的等价代换形式如下: for(<表达式1>;<表达式2>;<表达式3>) 语句; 2)当循环体至少执行一次时,用do...while语句与while语句等价。如果循环体可能一次也不执行,则只能使用while语句或for语句。 循环次数已知时一般用for循环,未知时用while和do-while 表达式1; while (表达式2) { <语句>; 表达式3;}
结 论 可以采用for语句的程序也完全可以使用while或 do-while语句来书写,程序员完全可以按照自己的习惯和爱好加以选择。 结 论 可以采用for语句的程序也完全可以使用while或 do-while语句来书写,程序员完全可以按照自己的习惯和爱好加以选择。 一般采用for语句的循环更为清晰,特别是每次有固定增量的情况。
#include <stdio.h> void main() { int i; for(i=1;i<=10;i++) 问题的提出:请设计程序输出下列图形: ********** #include <stdio.h> void main() { int i; for(i=1;i<=10;i++) printf("*"); printf("\n"); }
4.5 循环结构 4.5.5 循环的嵌套——多重循环结构 #include <stdio.h> ********** 一个循环内完整地包含另一个循环结构,则称为多重循环 。嵌套一层称为二重循环,嵌套二层称为三重循环 4.5.5 循环的嵌套——多重循环结构 问题的提出:请设计程序输出下列图形: #include <stdio.h> main() { int i,j; for(i=1;i<=4;i++) // 外循环控制打印行数 { for(j=1;j<=10;j++) // 内循环控制每行打印个数 printf("*"); printf("\n"); } ********** 再请问:第二个for后再加一层{ }又是什么结果? 请问:该printf函数去掉是什么结果?
#include <stdio.h> main() { int i,j; for(i=1;i<=4;i++) ********** 循环的嵌套(图解) i≦4 printf 假(0) 真(非0) i=1 j++ j=1 j ≦ 10 i++ 换行 外循环 内循环 #include <stdio.h> main() { int i,j; for(i=1;i<=4;i++) { for(j=1;j<=10;j++) printf("*"); printf("\n"); }
4.5 循环结构 ********** #include <stdio.h> main() { int i,j; 请思考:内循环可以变为这种形式吗? ********** #include <stdio.h> main() { int i,j; for(i=1;i<=4;i++) /* 外循环控制打印行数 */ { for(j=10;j>=1;j--) /* 内循环控制每行打印个数*/ printf("*"); printf("\n"); } 结论:完全可以。只要循环控制变量在循环体中不参加运算或操作就可以 外循环也可以改变吗?
4.5 循环结构 补充例题:打印如下形式的九九乘法表. main() { int x,y; for(x=1;x<=9;x++) { for(y=1;y<=9;y++) printf("%d*%d=%2d ",x,y,x*y); printf("\n"); } }
上机补充题1:打印如下形式的九九乘法表. 上机补充题2:打印如下形式的九九乘法表.
#include <stdio.h> main() { int i,j; for(i=1;i<=10;i++) 请设计程序输出下列图形: * ** *** **** ***** ****** ******* ******** ********* ********** #include <stdio.h> main() { int i,j; for(i=1;i<=10;i++) { for(j=1;j<=i;j++) printf("*"); printf("\n"); }
#include <stdio.h> main() { int i,j; for(i=10;i>=1;i--) 请设计程序输出下列图形: ********** ********* ******** ******* ****** ***** **** *** ** * #include <stdio.h> main() { int i,j; for(i=10;i>=1;i--) for(j=1;j<=i;j++) printf("*"); printf("\n"); }
#include <stdio.h> main() { int i,j; for(i=1;i<=10;i++) 请设计程序输出下列图形:还有别的方法吗? ********** ********* ******** ******* ****** ***** **** *** ** * #include <stdio.h> main() { int i,j; for(i=1;i<=10;i++) { for(j=1;j<=10-i+1;j++) printf("*"); printf("\n"); }
#include <stdio.h> main() { int i,j; for(i=4;i>=1;i--) 再请设计程序输出下列图形: #include <stdio.h> main() { int i,j; for(i=4;i>=1;i--) { for (j=1;j<=i;j++) printf(“ ”); for (j=1;j<=10;j++) printf("*"); printf("\n"); } *********** 内层第一个并列的for输出空格数
#include <stdio.h> main() { int i,j; for(i=1;i<=4;i++) 再请设计程序输出下列图形: #include <stdio.h> main() { int i,j; for(i=1;i<=4;i++) { for (j=1;j<=i;j++) printf(“ ”); for (j=1;j<=10;j++) printf("*"); printf("\n"); } *********** 内层第一个并列的for输出空格数
完全正确 从第5行开始从最左边开始打印 完全正确 例4-16 打印由数字组成的如下所示金字塔图案 编程分析:打印图案一般可由多重循环实现,外循环用来控制打印的行数,内循环控制每行的空格数和字符个数。 程序如下: 1 222 33333 4444444 555555555 66666666666 7777777777777 888888888888888 99999999999999999 完全正确 void main() { int i,k,j; for(i=1;i<=9;i++) /* 外循环控制打印行数*/ { for (k=1;k<=10-i;k++) /* 每行起始打印位置*/ printf(" "); for (j=1;j<=2*i-1;j++) /* 内循环控制打印个数*/ printf(“%c”,48+i); /* 0的ASICC码为48*/ printf(“\n”); /*换行*/ } 思考与讨论: 1)程序如果将程序中的数值“10”改 为“20”,程序的输出结果有什么不同?如果改为“5”,输出结果如何? 2)能否将语句“printf("%c",48+i);”改为“printf("%c",'0'+i);”? 改为: printf(“%d”, i); 如何?
课堂练习:输出打印图形 #include <stdio.h> void main() { int i,k,j; for(i=1;i<=9;i++) /* 外循环控制打印行数 */ { for (k=1;k<=10-i;k++) /* 每行起始打印位置 */ printf(" "); for (j=1;j<=i;j++) /* 内循环控制打印个数 */ printf("* "); printf("\n"); /* 换行 */ }
实验选做题:请设计程序输出下列图形: #include <stdio.h> void main() { int i,k,j; for(i=1;i<=9;i++) /* 外循环控制打印行数 */ { for (k=1;k<=10-i;k++) /* 每行起始打印位置 */ printf(" "); for (j=1;j<=i;j++) /* 内循环控制打印个数 */ printf("* "); printf("\n"); } /* 换行 */ for(i=8;i>=1;i--) /* 外循环控制打印行数 */ printf("* "); /* 打印内容 数字1的Ascii码为49 */ printf("\n"); /* 换行 */ }
4.5 循环结构 4.5.5 循环的嵌套——多重循环结构 一个循环内完整地包含另一个循环结构,则称为多重循环 嵌套一层称为二重循环,嵌套二层称为三重循环 do { … { …} while( ) … }while( ); (1) while( ) { … while( ) {…} … } (3) for( ) { ……… for( ) { …… } …… } (4) while( ) { … for( ) {…} }
4.5 循环结构 (5) for( ) {… while( ) {…} … } (6) do{ … for( ) {…} }while(); { … }while( ); } (8) for( ) { ……… do { …… } while( ); …… }
课堂练习:编程输出下列图形 main() { int i,j; for(i=0;i<=9;i++) { for(j=1;j<=i;j++) printf(“%d”,j); printf(“\n”); }
课堂练习2:编程输出下列图形 #include<stdio.h> void main( ) { int i,j; for(i=1;i<10;i++) for(j=1;j<10;j++) printf ((j==9)?"%4d\n":"%4d",i*j); }
循环的嵌套(图解) for(i=1;i<10;i++) for(j=1;j<10;j++) printf 假(0) 真(非0) i=1 j++ j=1 j<10 i++ for(i=1;i<10;i++) for(j=1;j<10;j++) printf((j==9)?"%4d\n":"%4d",i*j); 内循环 外循环 循环的嵌套(图解)
请打印1到1000中能同时被3和5整除的所有数。 #include <stdio.h> void main() { int k; for(k=1;k<=1000;k++) if(k % 3 = =0 && k %5= =0) printf(“%d ” ,k); }
4.6 其它控制语句 请打印1到1000中能同时被3和5整除的前10个数。 #include <stdio.h> void main() { int k,n=0; for(k=1;k<=1000;k++) if(k % 3 ==0 && k %5==0) { printf(“%d ” ,k); n++ ; if(n==10) break; } 提前退出循环
4.6 其它控制语句 4.6.1 break语句 作用范围:switch 语句或循环语句 功能:跳出switch语句或跳出本层循环, 转去执行后面的程序。 break语句的一般形式为: break; 注意: break语句用于循环体中,一般与if语句联合使用 说明: break只能终止并跳出最近一层的结构 break不能用于循环语句和switch语句之外的任何其它语句之中
表达式2 …… break; …... 假(0) 真(非0) for 表达式1 表达式3 表达式 …… break; 假(0) 真(非0) while do …… break; …... 表达式 假(0) 真(非0) while
例 break举例:输出圆面积,面积大于100时停止 #define PI 3.14159 main() { int r; float area; for(r=1;r<=10;r++) { area=PI*r*r; if(area>100) break; printf("r=%d,area=%.2f\n",r,area); }
例:输入任意10个整数,求其和 main() { int i,j,sum=0; for(i=0;i<10;i++) { scanf(“%d”,&j); sum=sum+j; } printf(“sum= %d”,sum);
例:输入任意10个整数,求其中正数的和 main() { int i,j,sum=0; for(i=0;i<10;i++) { scanf(“%d”,&j); if(j<0) continue; sum=sum+j; } printf(“正数的和为: %d”,sum);
4.6 其它控制语句 4.6.2 continue语句 作用范围:只能用在循环体中 功能:结束本次循环,转入下一次循环条件的判断与 执行。 注意:本语句只结束本层本次的循环,并不跳出循环。
例:输入任意10个整数,求其中正数的和 main() { int i,j,sum=0; for(i=0;i<10;i++) { scanf(“%d”,&j); if(j<0) continue; sum=sum+j; } printf(“正数的和为: %d”,sum); 可以不用continue吗?
4.6 其它控制语句 例4-18 计算半径为1到15的圆的面积,仅打印出超过50的圆面积。 void main() { int r; float area; for(r=1;r<=15;r++) { area=3.141593*r*r; if(area<50.0) continue; printf(" square=%f\n",area); } }
continue语句 功能:结束本次循环,跳过循环体中尚未执行的语句,进行下一次是否执行循环体的判断 仅用于循环语句中 表达式2 …… …... 假(0) 真(非0) for 表达式1 表达式3 表达式 …… continue; 假(0) 真(非0) while 真(非0) do …… continue; …... 表达式 假(0) while
4.6 其它控制语句 使用continue 不用continue main() { int n; for(n=7;n<=100;n++) { if (n%7!=0) continue; printf("%3d ",n); } } main() { int n; for(n=7;n<=100;n++) { if (n%7!=0) printf("%3d ",n); } }
补充题: 把100~200之间的不能被3整除的整数输出 main( ) { int n; for(n=100;n<=200;n++) { if(n%3!=0) printf(“%d ”,n); } main( ) { int n; for(n=100;n<=200;n++) { if(n%3==0) continue; printf(“%d ”,n); } 如果改为:
break与continue的区别 4.6 其它控制语句 break:语句只能用在switch 语句或循环语句中, 其作用是跳出switch语句或跳出本层循环,转去执行后面的程序。 continue:结束本次循环,即不再执行循环体中continue 语句之后的语句,转入下一次循环条件的判断与执行。应注意的是, 本语句只结束本层本次的循环,并不跳出循环。
比较break和continue main() main() { int i=0; { int i=0; while(i++<9) 输出为 1 2 3 4 比较break和continue main() { int i=0; while(i++<9) { if(i==5) break; printf(“%d\n”,i); } main() { int i=0; while(i++<9) { if(i==5) continue; printf(“%d\n”,i); } 输出为 1 2 3 4 6 7 8 9
4.6 其它控制语句 4.6.3 goto语句(无条件转移语句) 一般格式: goto 语句标号; 4.6 其它控制语句 4.6.3 goto语句(无条件转移语句) 一般格式: goto 语句标号; 语句标号符合标识符书写规则,起标识语句的作用。 如: label: i++; ………… goto label; 注意:C语言不限制程序中使用标号的次数,但不得重名。
4.6 其它控制语句 例4-20 统计从键盘输入一行字符的个数 #include <stdio.h“> void main() 能否用循环控制结构语句? 例4-20 统计从键盘输入一行字符的个数 #include <stdio.h“> void main() { int n=0; printf("input a string\n"); loop:if(getchar( )!='\n') { n++; goto loop;} printf("%d",n); } 形成了一个死循环 #include <stdio.h> void main() { int n=0; printf("inputa string\n"); while (getchar()!='\n') n++; printf("%d",n); } 思考:去掉loop后的复合语句括号{ },结果如何? 注意:在结构化程序设计中一般不主张使用goto语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。
除了1和自身以外不能被其他数整除的数就是素数 P.97. 4.7 控制结构应用程序举例 例4-21 判断一个给定的整数m是否为素数? 什么是素数? 除了1和自身以外不能被其他数整除的数就是素数 如2,3,5,7,11,13,17,………. 如何判别? 从2~ m-1 不能被整除就是素数,否则不是素数。 因为m=sqrt(m)*sqrt(m),所以从2~sqrt(m)就可以了。
输入m k=sqrt(m) for i=2; i≦k;i++ i>k? Lt4-20.c m能被i整除? 真 假 退出循环 i>k? 输出m是素数 输出m不是素数 #include "math.h" void main() { int m,i,k; scanf("%d",&m); k=sqrt(m); for(i=2;i<=k;i++) if(m%i==0) break; if(i>k) printf(“m是素数\n"); else printf(“m不是素数\n"); } Lt4-20.c 请与P.98.图4-22比较
思考:求100~200之间的所有素数 只要在上述算法的基础上再增加一重关于m的循环即可。由于大于2 的所有偶数一定不是素数,所以只要判断100~200之间的奇数即可。
偶数一定不是素数,所以从101开始,每次递增2 输入m k=sqrt(m) for i=2; i≦k;i++ i>k? m能被i整除? 真 假 退出循环 i>k? 输出m是素数 输出m不是素数 for m=101; m≦200;m=m+2 k=sqrt(m) for i=2; i≦k;i++ m能被i整除? 真 假 退出循环 i>k? 输出m是素数 输出m不是素数 求出100~200之间的所有素数 判断m是否素数
100-200之间的素数.c #include "math.h" void main() { int m,i,k; scanf("%d",&m); k=sqrt(m); for(i=2;i<=k;i++) if(m%i==0) break; if(i>k) printf(“m是素数\n"); else printf(“m不是素数\n"); } #include "math.h" void main() { int m,i,k,n=0; /*n为计数*/ for(m=101;m<=200;m+=2) { k=sqrt(m); for(i=2;i<=k;i++) if(m%i==0) break; if(i>k) { printf("%d ",m);n++; if (n%10==0) printf(“\n”); } /*每行打印10个素数 } printf("\n"); 100-200之间的素数.c
例4-22 编一程序验证哥德巴赫猜想:一个大于等于6的偶数可以表示为两个素数之和。 如6=3+3 8=3+5 12=5+7 P.98. 例4-22 编一程序验证哥德巴赫猜想:一个大于等于6的偶数可以表示为两个素数之和。 如6=3+3 8=3+5 12=5+7 P.98. 编程分析:设n为大于等于6的任一偶数,将其分解为n1和n2两个数,使用n1+n2=n,分别判断n1和n2是否为素数,若都是,则为一组解。若n1不是素数,就不必再检查n2是否素数。先从n1=3开始,直到n1=n/2为止。算法流程图如图4-24所示。 N1≤N/2 图4-24 验证哥德巴赫猜想的N-S流程图
大于2的偶数都不是素数,为使n1不取偶数,n1从3开始每次加2即可 验证哥德巴赫猜想程序: N1≤N/2 此break跳出内循环中的第一个for循环。 #include "math.h" void main() { int n,n1,n2,j,k; printf("Enter a number n=?\n"); scanf("%d" ,&n); for(n1=3;n1<=n/2;n1+=2) { k=sqrt(n1); /*判断被加数是否素数*/ for (j=2;j<=k;j++) if(n1%j==0) break; if(j<=k) continue; n2=n-n1; /*求得加数*/ k=sqrt(n2); /*判断加数是否素数*/ if(n2%j==0) break; if(j>k) printf("%d=%d+%d\n",n,n1,n2); }/*for*/ } 此continue跳出到外层for循环的下一次,即直接进入到下一个n1值 思考与讨论: 程序中对n1的循环为什么要从3开始? 此break跳出内循环中的第二个for循环,进入到外循环的下一个n1值继续循环。 Lt4-21.c
验证哥德巴赫猜想程序: 程序运行,输入8,结果是? #include “include.h" void main() N1≤N/2 程序运行,输入8,结果是? #include “include.h" void main() { int n,n1,n2,j,k; printf("Enter a number n=?\n"); scanf("%d",&n); for(n1=3;n1<=n/2;n1+=2) { k=sqrt(n1); /*判断被加数是否素数*/ for (j=2;j<=k;j++) if(n1%j==0) break; if(j<=k) continue; n2=n-n1; /*求得加数*/ k=sqrt(n2); /*判断加数是否素数*/ if(n2%j==0) break; if(j>k) printf("%d=%d+%d\n",n,n1,n2); }/*for*/ } 程序运行,输入10,结果又是什么? 程序运行,输入20,结果又是什么?
穷举法(枚举法):一一列举各种可能的情况,并判断哪一种可能是符合要求的解。 4.7 控制结构应用程序举例 P.99. 穷举法(枚举法):一一列举各种可能的情况,并判断哪一种可能是符合要求的解。 例4-22 将一张面值为100元的人民币等值换成100张5元、1元和0.5元的零钞,要求每种零钞不少于1张,问有哪几种组合? 分析:如果用x、y、z分别代表5元、1元、和0.5元的零钞的张数,根据题意可以得到下列方程: 三个未知数,两个方程 x+y+z=100 5x+y+0.5z=100 从数学上无法通过解析求解,但是可以利用计算机用枚举的方法方便的求出各种可能的解
#include <stdio.h> void main() { int x,y,z,n;/*n用于计数*/ printf( “ 5元 1元 0.5元\n”); n=0; for(x=1; x<=100;x++) for(y=1;y<=100;y++) for(z=1;z<=100;z++) if(x+y+z==100 && 5*x+y+0.5*z==100) { printf("%d %d %d\n",x,y,z); n++; } printf(" Total %d",n); Lt4-22.c
#include <stdio.h> void main() { int x,y,z,n; ;/*n用于计数*/ printf( “ 5元 1元 0.5元\n”); n=0; for(x=1; x<=100;x++) for(y=1;y<=100;y++) for(z=1;z<=100;z++) if(x+y+z==100 && 5*x+y+0.5*z==100) { printf("%d %d %d\n",x,y,z); n++; } printf(" Total %d",n); 该算法设计效率低,x最大取值应小于20,因每种面值不少于1张,因此y最大取值应为100-x,同时在x,y取定值后,z的值便确定了:z=100-x-y,所以本问题的算法使用二重循环即可实现
for(y=1;y<=100-x;y++) { z=100-x-y; if(5*x+y+0.5*z==100) #include <stdio.h> void main() { int x, y, z,n; printf( “ 5 元 1 元 0.5 元\n”); n=0; /*统计共有多少个解*/ for(x=1; x<=20;x++) for(y=1;y<=100-x;y++) { z=100-x-y; if(5*x+y+0.5*z==100) { printf(" %5d %5d %5d\n",x,y,z); n++; } } printf(" Total %d",n); 实际上最大到18 结果完全一样 Lt4-22-2.c
4.7 控制结构应用程序举例 4.7.3 迭代法(递推法): 对于要求解的值,由一给定的初值,通过某一算法(迭代公式)可求得新值,通常该新值比初值更接近要求解的值,再由新值按照同样的算法又可求得另一个新值,这样经过有限次的迭代最终求得其解。
4.7 控制结构应用程序举例 P.100. 例4-24:用迭代法求某个数的平方根。 已知求平方根 的迭代公式为: 编程分析:设平方根的解为x,可假定一个初值x0=a/2(估计值),根据迭代公式得到一个新的值x1,这个新值x1比初值x0更接近要求的值x;再以新值作为初值,即:x1→x0,重新按原来的方法求x1,重复这一过程直到|x1-x0|<ε(某一给定的精度)。此时可将x1作为问题的解。
Lt4-23.c #include <math.h> void main() { float x, x0, x1, a; printf("Enter a number a=?\n"); scanf("%f",&a); if (fabs(a)<0.000001) x=0.0; /* */ else if(a<0) printf("Data Error\n"); { x0 = a / 2.0; /* 取迭代初值 */ x1 = 0.5 * (x0 + a / x0); while (fabs(x1 - x0) > 0.00001) { x0 = x1; /* 为下一次迭代作准备 */ x1 = 0.5 * (x0 + a / x0); } x = x1; } printf("%f \'s sqrt is:%f\n", a,x); Lt4-23.c
1,1,2,3,5,8,13,21,…… 补充例1:求fibonacci数列的前30个数 特点:第1、2两个数为1、1。从第三个数开始,该数是其前面两个数之和。 F1=1 (n=1) F2=1 (n=2) Fn=Fn-1+Fn-2 (n≥3) 迭代问题: 不断用变量的新值取代旧值,或由旧值递推出变量新值的过程
printf("%ld\t%ld\t",f1,f2); for(i=0;i<28;i++) { f=f1+f2; main() { long f,f1=1,f2=1; int i; printf("%ld\t%ld\t",f1,f2); for(i=0;i<28;i++) { f=f1+f2; printf("%ld\t",f); f1=f2; f2=f; } printf(“\n”); } \t为水平制表 Fibnacci.c
if (i%2==0) /*计数,每行输出4个数*/ printf("\n"); f1=f1+f2; f2=f1+f2; } main() { long f1=1,f2=1; int i; for(i=1;i<=15;i++) { printf("%12ld %12ld",f1,f2); if (i%2==0) /*计数,每行输出4个数*/ printf("\n"); f1=f1+f2; f2=f1+f2; } 方法2:只设两个变量f1、f2
补充例2:欧几里德算法 u v u v 如 20 6 编写程序:用辗转相除法求两个整数的最大公约数。 6 2 2 0 如 20 6 6 2 2 0 所以2是20和6的最大公约数 编写程序:用辗转相除法求两个整数的最大公约数。 算法: 假设两个整数分别是u和v,另一个变量为r 如果v!=0,则转向第2步,否则转到第3步 给变量u和v重新赋值: r=u%v ;u=v ; v=r ;转到第1步 3. 输出最大公约数,即为当前u的值。 u v 又如17 3 3 2 2 1 1 0 所以1是17 和3 的最大公约数
#include <stdio.h> void main() { int u,v,r,a,b; printf("请输入任意的两个数\n"); scanf("%d%d",&u,&v); /*假设u>v>0*/ a=u; b=v; while(v!=0) { r=u%v; u=v; v=r; } printf(“%d 和 %d 的最大公约数是 %d\n",a,b,u); 可以改为: while(v)
本章小结 程序可分为三种最基本的控制结构:顺序结构,分支结构以及循环结构。 (1)C语言提供了多种形式的条件语句以构成分支结构。 if语句主要用于单向选择。 if-else语句主要用于双向选择。 if-else-if语和switch语句用于多向选择。 (2)C语言提供了三种循环语句。 For语句用于给定循环变量初值,步长增量以及循环次数的循环结构。循环次数及控制条件要在循环过程中确定的可用 while或do-while语句。 三种循环语句可以相互嵌套组成多重循环。循环之间可以并列但不能交叉。可用转移语句把流程转出循环体外,但不能从外面转向循环体内。
作业(P.102.) 一:3、4 二:3、5、6 三:1、2、3 四:1 五:3、4、5、6、7、8 上机作业(实验教程): 一:仔细阅读实验4的一、二、三、内容(P.30. ~P.34.); 二:仔细阅读实验4的【实例2-4-1】、 【实例2-4-2】; 三:实验内容(P.38.)的3)、4)、7)、9)、10)、11);
补充题:分别用传统流程图和N—S流程图表示实现下列功能的算法。 (1)打印输出1-100之间能够同时被3和5整除的数据。 (2)输入3个数据,按由大到小的顺序打印输出。
printf(“i=%d\n”,i++); printf(“i=%d”,i); } void main() { int i=-5; while(i) printf(“i=%d\n”,i++); printf(“i=%d”,i); } 输出结果为: i=-5 i=-4 i=-3 i=-2 i=-1 i=0 改为int i=5; 输出结果为:? 死循环
表达式 循环体语句 假(0) 真(非0)
printf(“i=%d\n”,i++); printf(“i=%d”,i); } void main() { int i=0; while(!i) printf(“i=%d\n”,i++); printf(“i=%d”,i); } 输出结果为: i=0 i=1 改为int i=1; 输出结果为: i=1