C语言教学课件:第八部分 基本程序设计技术1_第1页
C语言教学课件:第八部分 基本程序设计技术1_第2页
C语言教学课件:第八部分 基本程序设计技术1_第3页
C语言教学课件:第八部分 基本程序设计技术1_第4页
C语言教学课件:第八部分 基本程序设计技术1_第5页
已阅读5页,还剩71页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1学习程序设计需要注意规律性的东西同一个问题可以采用不同的解决方案不同的解决方案具有不同的效率2第八部分 基本程序设计技术3本部分内容基本程序设计技术循环程序设计解决问题的一些思路数据的精度程序调试4循环的常用机制+, - 将变量值加/减 1。有前/后置写法: +x x+ 将x的值加1 -x x- 将x的值减1建议不要写难以理解的语句,如:Sum=(+a)+(+a);Printf(“%d%d%d”,a,a+,a+);1. 增/减量运算5形式:表达式1,表达式2 ,表达式3,表达式4执行时先求值表达式1,后求值表达式2,以表达式n的值作为整个逗号表达式的值。优先级最低(低于赋值),从左向右结合。

2、主要用在 for 头部做变量初置和更新,用于做多个变量赋值或更新。例(求平方和):for(sum=0, n=1; n=100; +n) sum = sum + n * n;2. 逗号运算符( a=3*5,a*4), a+520 x=(a=3,6*a) a=3 x=1863.实现二元运算符操作的赋值运算符常见“sum = sum + n*n;”形式的赋值。C语言为许多二元运算符提供了(复合)赋值符。算术运算符都有对应赋值运算符:+=-=*=/=%= x+=y; 相当于 x=x+(y); x*=y+z; ? 赋值运算符使用的例子(求平方和):for (sum = 0, n = 1; n = 100

3、; n+) sum += n * n;这些运算符的优先级与结合方式与普通赋值相同。sum =sum+n * n74.循环中的几种变量循环中常出现几类变量,注意这些有助于对循环的思考和分析。1)循环控制变量(循环变量):循环前设初值,循环中递增/递减,达到/超过界限时循环结束。它们控制循环的进行/结束。 for中常有这类变量。for(n = 0; n = 0; -n) . .for(n = 2; n 52; n += 4) .这种循环是固定次数的循环。82)累积变量:循环中常用 += 或 *= 等更新。初值常用运算的单位元(加用0;乘用1为初值)。循环结束时变量终值被作为循环计算结果。 sum+

4、=t;3)递推变量:对变量x1、x2、x3,循环体可能有序列:x1 = x2;x2 = x3;x3 = . x1 . x2 .;9写循环时要考虑和解决问题:循环涉及到哪些变量,需引进哪些临时性变量?循环如何开始?循环开始前给变量什么初值?循环中变量的值如何改变?什么情况下继续(或终止)循环?循环终止后如何得到所需结果?用哪种结构实现循环,等等。工作方式:分析问题,发掘线索,最终完成程序。10只有在程序设计实践中才能学好程序设计:1)模仿好的范例;2)自己动手动脑,反复实践从问题出发做出程序的整个过程;3)多想想自己的程序做得怎么样,能不能做得更好。只有学会怎样做好小程序,才能做出大程序11学习

5、程序设计需要注意规律性的东西。三种流程模式是重要总结。本节主要内容:循环程序设计进阶顺序模式最简单选择模式:要确定判断条件及不同情况下的动作难点是实现重复执行的循环。8.1 循环程序设计 12138.1循环程序设计写循环首先要发现循环。重复动作的常见实例:一批类似数据做同样加工处理累积一批可按规律算出的数据(累加等)反复从一个结果算出下一结果(递推)若重复次数很多,就应该考虑用循环如果重复次数无法确定,就必须用循环描述14例1:求13到315所有数的平方根之和。可以一个个地加,但更方便的方法是写循环。需要一个变量保存部分和,逐步把各平方根加上去;需要一个变量保存变动轨迹,从初值开始每次修改。f

6、or(sum = 0.0, n = 13; n = 13; -n) sum += sqrt(n);典型for循环。假定已有总和变量sum和循环变量n:向上循环向下循环循环变量累积变量基本的循环方式有两种:向上循环和向下循环15while语句重写上例double sum;int n;Sum = 0.0; n = 13;while (n = 315) sum += sqrt(n); +n; 16注意:因为浮点计算存在误差,一般不用浮点数控制循环,尤其是增量为小数或包含小数时。例:求从0到100每隔0.2的数的平方根之和: double sum, x;for (sum=0.0, x=0.2; x=1

7、00.0; x+=0.2) sum += sqrt(x);由于浮点计算误差,不能保证循环500次。应写:int n; double sum;for (sum = 0.0, n = 1; n = 500; +n) sum += sqrt(0.2*n);17例2:打印出 1 到 200 间的完全平方数。方法一:逐个检查,遇平方数打印。循环框架:for (n = 1; n = 200; n+) if (n是完全平方数) 打印 n;n 是完全平方数判断:m可以从1开始,递增,直至m*m大于n:for (m = 1; m * m = n; m+) if (m * m = n) 打印 n;18完整程序:#

8、includeint main() int m, n;for(n=1; n = 200; n+)for (m = 1; m * m = n; m+) if (m * m = n) printf(%d , n);printf(n); return 0;内层循环结束的两种情况:1,找到m使m*m=n(n是完全平方数)2,试探了所有可能,但都不成功(n不是)共循环了多少次?19具有累计循环次数功能的完整程序:#include int main () int m, n, count = 0; for (n = 1; n = 200; n+) for (m = 1; m * m = n; m+) cou

9、nt+; if (m * m = n) printf(%d , n); printf(“%dn“, count); return 0;共循环了1799次20方法二:需要打印的一定是从1开始连续几个整数的平方,可从1开始打印到平方大于200为止。for (n = 1; n * n = 200; +n) printf(%d , n * n); /*注意打印什么*/ 方法三:还可以考虑利用递推公式:方法一产生所有备选数据(1到200的整数),检查排除不合格的。生成与检查是解决问题的常用方法。方法二是针对具体问题的特定方法。21#includeint main() int termn, n; for

10、(n = 1,termn = 1;termn = 200; n+) printf(%4d,termn); termn += 2*n+1; return 0;利用递推公式求平方数程序:方法与启发方法1产生所有备选数据(1到200之间的整数),检查排除不合格的。启发生成与检查是解决问题的常用方法。方法2换个角度思考问题,是针对具体问题的特定方法。方法3递推,用数学模型解决问题启发同一个问题,可以有不同的解决方法应尝试切换思考角度和寻找更有效的方法不同的方法有不同的效率22例3:判断整数是否为素数int isprime(int),返回0/1值n是素数当且仅当n没有真因子若n % m = 0,则m是n

11、的因子若n % m = 0,且 m n 23素数判断函数定义int isprime (int n) /* n是否素数 */ int m = 2; for ( ; m * m = n; m+) if (n % m = 0) return 0; /只要发现能被一个数整除,就返回 return 1; /* 没有因子,是素数 */24分析从循环中退出途径只要发现一个因子,就可做结论,return 0;没有发现因子,循环条件不符合。函数存在的问题对1给出“是素数” ;负数也会给出不合理结果。应该在循环前处理特殊情况if (n = 1) return 0;负数问题?如果需要可以另外考虑处理。2526例4,

12、艰难旅程(浮点误差)。乌龟要去环球。第1秒爬1米,第2秒爬1/2米,第3秒爬1/3米,第4秒爬1/4米,。问一小时能爬出多远?爬20米需多少秒? 一小时能爬多远算法:1.d(距离,实数); s(时间,整数);2.d=0.0;s=1;3. d=d+1/(实数)s; s=s+1; 4.如果s=3600,转3;5.否则,输出d;爬20m需多少秒算法:d(距离,实数); s(时间,整数);d=0.0,s=1;d=d+1/(实数)s;s=s+1; 如果d20.0,转3;否则,输出s-1;根据数学,乌龟能完成环球,可以爬得任意远。这里想比较float和double的计算误差情况。s-1?27n秒能爬x米的

13、函数float distfun(long n) float distfun(long n) long i; float x = 0.0; for (i = 1; i = n; +i) x += 1/(float)i; return x;例4,艰难旅程(浮点误差)。乌龟要去环球。第1秒爬1米,第2秒爬1/2米,第3秒爬1/3米,第4秒爬1/4米,。问一小时能爬出多远?爬20米需多少秒? 28例4,艰难旅程(浮点误差)。乌龟要去环球。第1秒爬1米,第2秒爬1/2米,第3秒爬1/3米,第4秒爬1/4米,。问一小时能爬出多远?爬20米需多少秒? 20米需要多少时间。写出下面函数:long scndsf

14、un(float d) /*d为爬行距离*/ long i; float x = 0.0; for (i = 1; x d; +i) x += 1/(float)i; return i - 1;d=2029写下面语句,执行时总也不输出:printf(%lds, %fmn,scndsfun(20.),20.);修改为如下语句:for (x = 10.0; x = 20.0; x += 1.0) printf(%lds, %fmn,scndsfun(x),x);原因?程序输出: 12367s, 10.000000m 33617s, 11.000000m 91328s, 12.000000m 248

15、695s, 13.000000m 662167s, 14.000000m1673859s, 15.000000m而后又没反应了。30void test_float () long i; float sum = 0.0, sum0 = -1.0; for (i = 1; sum != sum0; +i) sum0 = sum; sum += 1 / (float)i; printf (float: %ld terms at %fn, i-1, sum); 对乌龟活动的模拟受到数据表示精度和范围的限制。 考虑如下的测试函数:程序很快就输出了一行:float: 2097152 terms at 15

16、.403683判断爬的距离不再发生变化。Float精度限制,差值无法测试到记住上一次的值解决方案:改用双精度浮点数如果把测试程序改用double类型变量保存距离则可以得出如下结果(15m以后) 1835421s, 15m 2989191s, 16m 13562027s, 17m 36865412s, 18m111210581s, 19m272400600s, 20m采用float表示所爬的里程,d为目标里程long scndsfun(float d) long i; /秒数 double x = 0.0; /已经爬里程 for (i = 1; x = 1E-6) x1 = x2; x2 = (

17、2.0*x1 + x / (x1*x1) / 3.0; return x2;程序的一个缺点是同一表达式写了两次。调用语句:y=cbrt(200);或x=200;y=cbrt(x);36do-while没有for和while语句用得多。用do-while结构重写求立方根函数:double cbrt(double x) double x1, x2 = x;if (x = 0.0) return 0.0; /*处理特殊情况*/ do x1 = x2; x2 = (2.0*x1 + x / (x1*x1) / 3.0; while (fabs(x2 - x1) / x1) = 1E-6); retur

18、n x2;37循环中的递推变量:对变量x1、x2、x3,循环体可能有序列:x1 = x2;x2 = x3;x3 = . x1 . x2 .;例如上面cbrt里的变量x1和x2。38例6:定义函数,利用公式求 近似值。设为double dsin(double x)。sum = 0.0; 对n为0计算t;while (需要继续) sum = sum + t; 计算下一个t;循环结束条件:例如用项绝对值小于 。方法:循环累加,n 趋向无穷的过程中项值趋于0,而累加值趋向函数值。保存累加和的变量sum ,循环中求出的项值用 t 保存。39问题:t 的值如何计算?第一个 t就是 x;分析可以发现项值的递

19、推公式:double dsin(double x) double s=0.0, t=x; int n=0; /*n=0,t=x*/ while (t=1E-6|t=-1E-6) s+=t; n=n+1; t=-t*x*x/(2*n)/(2*n+1); return s;Sin(x)=x-x3/3!+x5/5!-x7/7!+.8.2 循环中的重要问题 40nmisprime(m)isprime(n-m)验证结果6311OK58311OK510311OK511OK712310NO511OK7nmisprime(m)isprime(n-m)验证结果14311OK510711OK916311OK510

20、OK710NO9.存在多余判断for (n = 6; n = 200; n += 2) for (m = 3; m = n / 2; m+= 2) if (isprime(m) & isprime(n - m) printf(%d=%d+%dn, n, m, n-m);如何从循环中退出?例:对6200的各偶数验证哥德巴赫猜想(一个偶数可以拆成两个素数),可以利用前面写的素数判断函数isprime41用return退出?前面写isprime时借助return退出了函数,效果上等于结束了循环。但是在这个程序里,程序判断完某个偶数n已经由某个素数对组成以后,还需要对下一个偶数进行相同判断。此时并不能

21、用return结束函数的执行,怎么办?42解决方案1增加对循环的控制,把发现素数分解作为条件加入。引入整型变量found,值0表示未发现素数对。发现时将found赋1。内循环开始时found置0。应修改内层循环条件。修改后循环是for (n = 6; n = 200; n += 2) for (found = 0, m = 3; m = n / 2 & !found; m += 2) if (isprime(m) & isprime(n-m) printf(%d=%d+%dn, n, m, n - m); found = 1; 非常常用的机制,设置变量用于表示某一项任务的完成状态或某个对象的状

22、态FoundNOTFound43执行过程示意nmfoundisprime(m)isprime(n-m)found6301115183011151103011151230100501117114301115116301115118FoundNOTFound44解决方案2:用break语句语法break;功能及说明break只能用在循环语句及switch语句里。用在循环语句里,从break所处的循环层次的复合语句内跳出(跳到外层),程序中被跳出的循环语句体之后的第一条语句处继续执行。用在switch语句的语句体中时,从该语句体中跳出,执行跳到switch的语句体之后继续执行。45例,用break实

23、现跳出for (n = 6; n = 200; n += 2) for (m = 3; m = n / 2; m += 2) if (isprime(m) & isprime(n - m) printf(%d = %d+%dn, n, m, n - m); break; printf(“finished”);问题:执行完break以后执行哪条语句或计算?expr2循环体真(非0)for计算expr1计算expr3假(0)46执行过程示意nmisprime(m)isprime(n-m)验证结果6311TRUE8311TRUE10311TRUE12310FALSE511TRUE14311TRUE1

24、6311TRUE1847利用break重写前面求立方根的函数double cbrt (double x) double x1, x2 = x; if (x = 0.0) return 0.0; while (1) x1 = x2; x2 = (2.0 * x1 + x / (x1 * x1) / 3.0; if (fabs(x2 - x1) / x1) 1E-6) break; return x2;本例中,因循环结束后函数不需要再做别的事情,因此也可以在 break 处直接写 return x2。解决了同一表达式计算写了两段相类似的计算代码的问题48“常量”是标识符形式,在程序里代表同一常数的

25、东西。用enum定义(枚举)可方便地定义一组符号常量:enum NUM = 10, LEN = 20; 枚举常量的作用?用于循环示例例:#include enum START = 0, END = 300, STEP = 20;int main (void) int c; for (c = START; c = END; c += STEP) printf(C = %d, F = %fn, c, c * 5.0/9.0 + 32.0); return 0; /*这样的程序更容易修改*/另一种形式:define START 0define END 300# define STEP 2049符号形

26、式表示能帮人理解程序意义。程序里两个0可能代表不同意义,数值形式没有任何区分。采用符号常量可提高可读性。将所需常数定义为符号常量,在程序中统一使用是很好的方法。使程序更容易修改(修改时不必浏览整个程序)。对大程序的作用更明显。课本第五章继续介绍其他常量定义方式。enum的详细讨论在第九章,目前作为一种定义符号整型常量的机制。 说明:508.3 程序设计举例518.3 程序设计实例例1:简单交互式计算器。假定它可以输入并计算:128+365254+143810313+524 输入一行算一个结果。直至用户要求结束。基本思想: while (还有输入) 取得数据 计算并输出用scanf读数据,用文件

27、结束或非数字表示输入结束。52#include int main () int left, right; printf(Small calculator.n); printf(Any no-digit character to stop.n); while(scanf(%d, &left) = 1) if(getchar()!=+ | scanf(%d,&right)!=1) printf(Fmt error. Enter: nnn+mmmn ); while (getchar() != n); /丢掉本行剩余字符 ; continue; printf(%d+%d=%dn,left,right

28、,left+right); return 0;53例2:单词计算问题 英文正文文件可看成是字符序列,空白字符把序列分隔为一个个“单词”。要求写程序统计文件中的单词个数。 空白字符包括:空格 、制表符t、换行符n54问题分析需要一个计数器,遇到一个词将计数器加一。考虑用函数getchar读字符。程序主要部分的框架:while (文件未结束) 遇到一个词时计数器加一;打印统计信息;用getchar输入,很容易判断文件结束。问题是如何确定“遇到了一个词”。55初始思路: #include int main () int c = , count = 0; while (c != EOF) while

29、(c = getchar() != EOF & (c = | c = t | c = n) ; if (c = EOF) break; +count; while (c = getchar() != EOF & c != & c != t & c != n) ; printf(word count: %dn, count); return 0;读完新词略过空白字符出现新词56如何判断一个单词?若读的字符是单词首字符,则计数器加一。读入字符过程中需要区分是否空白。问题:非空白字符未必是词的开始,是否新词要看前一字符是否空白。可见:不能孤立地处理情况,要参考前面情况。必须做情况记录,以便后面参考。

30、57前后关系分两种情况:1)读到空白,随后遇非空白字符就是新词;2)读到非空白,随后不会遇到新词。可看作处理过程的不同状态,两种状态:1)读在词外(遇到非空白是新词); 2)读在词内。在读入字符的过程中读入状态也不断转换。典型,可以用有限状态转换系统(自动机)描述。58分析前后关系OUTIN读到空格字符状态不变读到非空格字符转入词内状态读到空格字符转入词外状态读到非空格字符状态不变两种读入状态:OUT: 当前位置在词外IN: 当前位置在词内在从OUT转换到IN时,遇到新词,计数。状态图转换图59状态的表示60可以用一个变量来记录状态,设state令变量取值为IN和OUT;只要求这两个值不同;不

31、会同时处在两种状态;可以将IN, OUT定义成枚举值。if (c = | c = t | c = n) if (state = IN) state = OUT; else /* state为OUT */ state = OUT;else /* 不是空格 */ if (state = IN) state = IN; else /* state为OUT */ state = IN; +count; /* 许多地方可以简化 */61当前读状态变换的表示当前读到一个字符c时,处理可描述为:#include enum IN = 1, OUT = 0;int main (void) int c, coun

32、t = 0, state = OUT; while (c = getchar() != EOF) if (c = | c = t | c = n) /如果是空白字符 state = OUT; /在词外 else if (state = OUT) /如果碰到非空白字符 /且读状态为在词外,则说明碰到新词 state = IN; /将状态修改为词内 +count; /记数 printf(word count: %dn, count); return 0;62例2:单词计算问题英文正文文件可看成是字符序列,空白字符(空格 、制表符t、换行符n)把序列分隔为一个个“单词”。要求写程序统计文件中的单词个

33、数。思考中文的词可以用这种办法找出来吗?拓展学习学习中文分词知识及算法638.4 程序测试和排错64分析问题编制程序编译连接调试运行完成调试中发现程序编写有错误,修改源程序编译时发现程序有语法错误,修改源程序连接时发现程序有错误,修改源程序调试运行时发现问题分析设计有错误,重新分析问题程序开发过程658.4 程序的调试与排错写好一个程序后,需要:通过加工(编译和连接)产生可执行程序运行它,提供数据进行试验,确认它确实满足要求试验中常常会发现错误,需要设法排除测试(testing):在完成一个程序或一部分程序,通过编译后试验性运行,仔细检查运行效果,设法确认该程序确实完成了所期望的工作。反过来说

34、:测试就是设法用一些特别选出的数据去挖掘出程序里的错误排错(debugging):在发现程序有错时,设法确认产生错误的根源,修改程序,排除这些错误的工作过程66测试的基本方法测试时考虑的基本问题是提供什么样的数据,才可能最大限度地把程序中的缺陷和错误挖出来。有两类确定测试数据的基本方式:根据程序结构确定测试数据。这相当于把程序打开,根据其内部结构考虑如何检查它,设法发现其中的问题。这种方式称为白箱测试根据程序所解决的问题去确定测试过程和数据,不考虑程序内部如何解决问题。这相当于把程序看作解决问题的“黑箱”,因此称为黑箱测试67白箱测试考察程序内部结构及由此产生的执行流,选择数据使程序在试验运行中能通过“所有”可能出现的执行流程 复合结构只有一条执行流“if (条件) 语句1 else语句2” 有两条可能执行流:条件成立时执行语句1;不成立时执行语句2。应设法提供测试数据,检验程序在这两种情况下都能正确工作从本质上说“while (条件) 循环体”可能产生无穷多条执行流:循环体不执行,执行1次,执行2次,。无法穷尽检查。常用方法是选择测试数据,检查循环的一些典型情况,包括循环体执行0次、1次、2次的情况,以及一些其他情况其他结构可类似分析。要考虑程序中结构嵌套产生的组合流程68黑箱测试考虑的不是程序

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论