基本程序设计技术_第1页
基本程序设计技术_第2页
基本程序设计技术_第3页
基本程序设计技术_第4页
基本程序设计技术_第5页
已阅读5页,还剩104页未读 继续免费阅读

下载本文档

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

文档简介

1、关于基本程序设计技术第一张,PPT共一百零九页,创作于2022年6月第四章基本程序设计技术第二张,PPT共一百零九页,创作于2022年6月学习程序设计需要注意规律性的东西。三种流程模式是重要总结。本章还讨论:基本输入和输出递归的程序设计其他控制结构顺序模式最简单选择模式:要确定判断条件及不同情况下的动作开始的难点在实现重复执行的循环。重复执行比较复杂,牵涉问题多,是本章重点第三张,PPT共一百零九页,创作于2022年6月4.1循环程序设计写循环首先要发现循环。注意计算中的重复性动作,引进循环可能统一描述和处理。重复动作的常见实例:一批类似数据做同样加工处理累积一批可按规律算出的数据(累加等)反

2、复从一个结果算出下一结果(递推)若重复次数很多,就应该考虑用循环如果重复次数无法确定,就必须用循环描述第四张,PPT共一百零九页,创作于2022年6月例:求13到315所有数的平方根之和。可以一个个地加,但更方便的方法是写循环。需要一个变量保存部分和,逐步把各平方根加上去;需要一个变量保存变动轨迹,从初值开始每次修改。典型for循环。假定已有总和变量sum和循环变量n:for (sum = 0.0, n = 13; n = 13; -n) sum += sqrt(n);这里的两个循环等效。一般采用向上循环第五张,PPT共一百零九页,创作于2022年6月可以用while语句重写,两种结构功能上等

3、效。例,求 13, 315 间每隔7的各整数的平方根之和。 一般不用浮点数控制循环,尤其是增量为小数或包含小数时。例:求从0到100每隔0.2的数的平方根之和: double sum, x;for (sum=0.0, x=0.2; x=100.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);第六张,PPT共一百零九页,创作于2022年6月例1:打印出 1 到 200 间的完全平方数。方法一:逐个检查

4、,遇平方数打印。重复做,每次检查一个数。循环框架: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;第七张,PPT共一百零九页,创作于2022年6月综合起来可得到完整程序:#include int main () int m, n; for (n = 1; n = 200; n+) for (m

5、 = 1; m * m = n; m+) if (m * m = n) printf(%d , n); printf(n); /* 最后输出一个换行符 */ return 0;内层循环结束:1,找到m使m*m=n(n是完全平方数)2,试探了所有可能,但都不成功(n不是)第八张,PPT共一百零九页,创作于2022年6月方法二:需要打印的一定是从1开始连续几个整数的平方,可从1开始打印到平方大于200为止。for (n = 1; n * n = 200; +n) printf(%d , n * n); /*注意打印什么*/ 还可以考虑利用递推公式:方法一:产生所有备选数据(1到200的整数),检查

6、排除不合格的。生成与检查是解决问题的常用方法。方法二是针对具体问题的特定方法。第九张,PPT共一百零九页,创作于2022年6月例 2:写函数判断整数是否为素数(谓词)。类型特征可用 int isprime(int),返回0/1值n是素数则它没有真因子,m是n的因子用(n%m = 0)描述,若 mn 就够了。函数定义:int isprime (int n) /* n是否素数 */ int m = 2; for ( ; m * m = n; m+) if (n % m = 0) return 0; return 1; /* 没有因子,是素数 */第十张,PPT共一百零九页,创作于2022年6月从循

7、环中退出:isprime发现一个因子就可做结论。return使函数结束,也导致循环结束。函数不完善,对1给出“是素数” ,负数也会给出不合理结果。应该在循环前处理特殊情况:if (n = 1) return 0;负数问题?如果需要可以另外考虑处理。第十一张,PPT共一百零九页,创作于2022年6月例3,艰难旅程(浮点误差)。乌龟要去环球。第1秒爬1米,第2秒爬1/2米,第3秒爬1/3米,第4秒爬1/4米,。问一小时能爬出多远?爬20米需多少秒? 根据数学,乌龟能完成环球,可以爬得任意远。这里想比较float和double的计算误差情况。这里只考虑20米需要多少时间。写出下面函数:long sc

8、ndsf (float d) long i; float x = 0.0; for (i = 1; x d; +i) x += 1/(float)i; return i - 1;第十二张,PPT共一百零九页,创作于2022年6月写下面语句,执行时总也不输出:printf(%lds, %fmn,scndsf(20.),20.);修改为如下语句:for (x = 10.0; x = 1E-6) x1 = x2; x2 = (2.0*x1 + x / (x1*x1) / 3.0;return x2;这个程序的一个缺点是同一表达式写了两次。书上利用C的特点给了一种简化写法,供参考学习了其他结构后有改进

9、的写法第十七张,PPT共一百零九页,创作于2022年6月例5:定义函数,利用公式求 近似值。设为double dsin(double x)。方法:循环累加,n 趋向无穷的过程中项值趋于0,而累加值趋向函数值。需要用循环。保存累加和的变量sum ,循环中求出的项值用 t 保存。sum = 0.0; 对n为0计算t;while (需要继续) sum = sum + t; 计算下一个t;循环结束条件:例如用项绝对值小于 。第十八张,PPT共一百零九页,创作于2022年6月问题:t 的值如何计算?第一个 t就是 x;分析可以发现项值的递推公式:double dsin (double x) double

10、 s = 0.0, t = x; int n = 0; while (t = 1E-6 | t = -1E-6) s += t; n = n + 1; t = -t*x*x / (2*n) / (2*n + 1); return s;第十九张,PPT共一百零九页,创作于2022年6月一些情况:实参值很小时,循环将很快结束。实参绝对值很大时循环可能做许多次,项的绝对值可能达到很大,n 值也可能超出整数的表示范围可考虑用fmod把参数归约到0,2 之内启示:理解问题非常重要。发现了项的递推性质能节省许多计算(否则就要再写一个嵌套循环完成项的计算)级数收敛性质能帮人认识情况,改进计算方法写程序时必须

11、仔细考虑问题本身的性质第二十张,PPT共一百零九页,创作于2022年6月4.2 循环中的问题从循环中退出有时需要从正在执行的循环中退出。对6200的各偶数验证哥德巴赫猜想,利用isprime: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);问题:有多种分解时将产生多对输出。如对10:10=3+7 10=5+5前面写isprime时借助return退出了循环第二十一张,PPT共一百零九页,创作于2022年6月希望每个偶数

12、只输出一行。怎样在发现素数对后停止?增加对循环的控制:把发现素数分解作为条件加入。引入整型变量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; 这种需求很常见。C语言引进了专门的控制语句第二十二张,PPT共一百零九页,创作于2022年6月break语句,形式:br

13、eak;break只能用在循环语句(及switch语句)里,使最内层(循环可嵌套)循环语句(或switch)立即停止,执行从被终止循环(或switch)之后继续。可用break解决循环中退出问题,放在条件下。完成前例的程序段: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; 第二十三张,PPT共一百零九页,创作于2022年6月利用break重写前面求立方根的函数:double cbrt (

14、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。第二十四张,PPT共一百零九页,创作于2022年6月循环中的几种变量循环中常出现几类变量,注意这些有助于对循环的思考和分析。也是写循环程序的经验总结注意:分类不是绝对的,不同类别没有截然界限1)循环控制变量(循环变量):循环前设初值,循环中递增/递

15、减,达到/超过界限时循环结束。它们控制循环的进行/结束。 for中常有这类变量。for(n = 0; n = 0; -n) . .for(n = 2; n 52; n += 4) .这种循环是固定次数的循环。这种循环可能展开第二十五张,PPT共一百零九页,创作于2022年6月2)累积变量:循环中常用 += 或 *= 等更新。初值常用运算的单位元(加用0;乘用1为初值)。循环结束时变量终值被作为循环计算结果。3)递推变量:前两类变量的推广。几个协同工作的变量,每次由几个变量推出一个新值,其余依次更新。对变量x1、x2、x3,循环体可能有序列:x1 = x2;x2 = x3;x3 = . x1 .

16、 x2 .;例如上面cbrt里的变量x1和x2。第二十六张,PPT共一百零九页,创作于2022年6月写循环时要考虑和解决问题列表:循环涉及到哪些变量,需引进哪些临时性变量?循环如何开始?循环开始前给变量什么初值?循环中变量的值如何改变?什么情况下继续(或终止)循环?循环终止后如何得到所需结果?用哪种结构实现循环,等等。工作方式:分析问题,发掘线索,最终完成程序。程序设计不是教条,典型问题也无标准答案。并非最简单的问题总有多种解决方法,往往各有长短。 “正确”程序常有优劣之分。第二十七张,PPT共一百零九页,创作于2022年6月4.3循环与递归程序中有循环可能导致很长的计算。没有循环结构也能描述

17、这类计算。C语言允许递归,可在函数内调用自身,程序常常更简单清晰。例:定义计算整数阶乘的函数:12(n-1)n乘的次数依赖于n,定义时不知道,每次用可能不同。程序的典型情况:计算“次数”依赖某些参数的值。省略号不科学。严格定义需用递归形式。第二十八张,PPT共一百零九页,创作于2022年6月注意递归定义的形式。这也提出了一种计算方法。如果语言允许递归定义函数,就可以直接翻译为程序。C允许递归定义:在函数定义内调用被定义函数本身。类型特征可定为: int fact(int)阶乘值增长极快(数学),更合适的类型特征:long fact(long)第二十九张,PPT共一百零九页,创作于2022年6月

18、递归的函数定义:long fact (long n) return n = 0 ? 1 : n * fact(n-1);也可以用循环定义:long fact1(long n) long i, f = 1; for (i = 2; i = n; +i) f *= i; return f;比递归定义长,需要引进多个局部变量。第三十张,PPT共一百零九页,创作于2022年6月fact实现的计算过程很不简单。计算中fact被递归调用的次数由实参确定。考虑负参数值处理。可改为:n=1 ? 1 : .第三十一张,PPT共一百零九页,创作于2022年6月递归定义导致的计算过程参数不同fact递归调用次数(步

19、数)不同。定义只有一个语句,可能要许多步才能完成。包含递归(和循环)的程序产生的计算过程和性质更复杂,能完成更复杂工作,理解和书写也更困难。递归的函数定义需要条件表达式或if,必须区分:直接给出结果的情况。是递归的基础需要递归处理的情况。其中把对较复杂情况的计算归结为对更简单情况的计算基本运算/关系判断/条件表达式,加函数定义和递归定义构成了一个(理论上)“足够强的”的程序语言。第三十二张,PPT共一百零九页,创作于2022年6月程序实例Fibonacci(斐波那契)序列的递归定义:Fibonacci 序列增长很快,返回值选long。递归定义:long fib (int n) return n

20、2 ? 1 : fib(n-1) + fib(n-2);负参数值定义为 1。这是“合理”处置。问题分析:这个程序好不好?一方面,很好!程序与数学定义的关系很清晰,正确性容易确认,定义易读易理解。第三十三张,PPT共一百零九页,创作于2022年6月但这个定义有一个本质性缺点。示意图:第三十四张,PPT共一百零九页,创作于2022年6月存在大量重复计算,参数越大重复计算越多。有关系吗?随着参数增大,计算中重复增长迅速,最快的微机上一分钟大约可以算出fib(45)参数加1,fib多用近一倍时间(指数增长)。最快的微机一小时算不出fib(55),算fib(100)要数万年计算需时间,复杂计算需要很长时

21、间。这是计算机的本质特征/弱点。说明它不万能,有些事清“不能”做。求Fibonacci 值有更好计算办法(下面介绍)。第三十五张,PPT共一百零九页,创作于2022年6月人们发现了许多实际问题,理论上说可用计算机解决(可写出计算它的程序),但对规模大的情况(“大的参数 n”),人类永远等不到计算完成。这时能说问题解决了吗?理解这个情况对于理解计算机是非常重要的。这里有一大类问题称为计算中的“难解问题”,其中有许多很实际的问题(规划、调度、优化等)。这方面的理论和实际技术的研究极为重要:计算复杂性,难解问题,“P = NP?”问题。另外,对于许多问题的实用的有效算法,有极大的理论价值和实际价值。

22、第三十六张,PPT共一百零九页,创作于2022年6月为计算过程计时统计程序/程序片段的计算时间有助于理解程序性质。许多语言或系统都提供了内部计时功能。有关函数在time.h,统计程序时间时程序头部应写:#include 在程序里计时,通常写表达式:clock()/CLOCKS_PER_SEC得到从程序开始到表达式求值时所经历的秒数。注意:有些老的C系统(如Turbe-C)用 CLK_TCK。第三十七张,PPT共一百零九页,创作于2022年6月确定计算fib(45)所需要的时间的程序:#include #include long fib (int n) return n=1 ? 1 : fib(

23、n-1)+fib(n-2);int main () /* 自己做其他试验 */ double x; x = clock() / CLK_TCK; fib(45); x = clock() / CLK_TCK - x; printf(Timing fib(45): %f.n, x); return 0;第三十八张,PPT共一百零九页,创作于2022年6月Fibonacci数的递推计算 易见 1)F1和F2是12)知道连续两个Fibonacci数,就可算出下一个递推计算方式:逐个前推,可用循环实现:long fib1 (int n) long a = 1, b = 1, c, i; if (n =

24、 1) return 1; for (c = a+b, i = 2; i n; +i) a = b; b = c; c = a + b; return c; /* 对吗? */第三十九张,PPT共一百零九页,创作于2022年6月循环结束时i等于n,这时c的值是Fn。要得到此结论,可设法证明:每次判断 i 的值时c正是 Fi。上面循环保证这种关系,可以通过归纳证明:for (c = a+b, i = 2; i n; +i) a = b; b = c; c = a + b;第一次判断时 i 的值是 2,c 的值2,正是 Fi(且 a 的值是Fi-1 ,b 的值是Fi-2 )若某次判断时 i 值是

25、k(小于n),循环体中的语句使a变成Fk-1 ,b变成Fk ,c变成Fk+1 。i 值增 1 使我们又有a为Fi-2 ,b变成Fi-1 ,c变成Fi 根据归纳法,每次判断 i 的值时c正是 Fi。第四十张,PPT共一百零九页,创作于2022年6月循环实现重复性计算,循环体可能执行多次。如何保证对各种数据都能正确完成计算?循环中变量不断变化。写循环要考虑变量间的关系,保证某些关系在循环中不变:循环的不变关系。写循环时最重要的就是想清循环中应维持变量间的什么关系才能保证循环结束时变量能处在所需状态。写完循环后应仔细检查是否满足要求。循环不变关系(循环不变量)是理解循环、写好循环的关键。这个方面有很

26、多研究,有许多理论结果。第四十一张,PPT共一百零九页,创作于2022年6月问题:用循环的函数比用递归定义的好吗?新函数在计算时间上有极大优越性。计算时间由循环次数确定。循环体执行次数大致为n。fib(100)只需约100次循环,几乎察觉不到所花费时间。新函数定义较复杂,有复杂的循环。要理解程序意义,确认函数对任何参数都算出Fibonacci值,需要借助“循环不变关系”的概念和细致分析。上面分析中没考虑数据表示范围,long类型一般无法表示fib(100)。注意:这个例子并不是说明递归比循环的效率低。完全可以写出计算fib的同样高效的递归定义的函数第四十二张,PPT共一百零九页,创作于2022

27、年6月求最大公约数(greatest common divisor,GCD):写函数 long gcd(long, long)方式1:k取初值1后递增,大于m或n之一时结束。如何得到所需结果? m和n可能有多个公约数,最后的k值不是m和n的公约数(它已大于两数之一)。解法1:逐个检查,直到找到能同时整除m和n的最大整数(生成与检查)。需辅助变量k记录检查值。简单方式:k顺序取值(初值/更新/结束),可用循环实现。需要记录循环中找到的公约数。第四十三张,PPT共一百零九页,创作于2022年6月只需记录已找到最大的公约数,用变量d,初值1(是公约数),遇到新公约数(一定更大)时记入d:if (m

28、% k = 0 & n % k = 0) d = k; /* k为新找到的公约数 */有了d及其初值,k可以从2开始循环。函数定义:long gcd (long m, long n) long d = 1, k = 2; for ( ; k = m & k = n; k+) if (m % k = 0 & n % k = 0) d = k; return d;参数互素时初值1会留下来,也正确。第四十四张,PPT共一百零九页,创作于2022年6月还有一些特殊情况需要处理:1)m和n都为0需特殊处理。例如令函数返回值0;2)若m和n中一个为0,gcd是另一个数。函数的返回值正确。也可直接判断处理;

29、3)m、n为负时函数返回1,可能不对。应在循环前加语句:if (m = 0 & n = 0) return 0;if (m 0) m = -m;if (n n ? n : m); m % k != 0 | n % k != 0; k-) ; /* 空循环体 */return k; /*循环结束时k是最大公约数 */ 本方法比前一方法简单一些。两种方法的共同点是重复测试。这类方法的缺点是效率较低,参数大时循环次数很多。第四十六张,PPT共一百零九页,创作于2022年6月解法2:求GCD有著名的欧几里德算法(欧氏算法,辗转相除法)。最大公约数的递归定义:函数定义(递归):假设第二个参数非0,且参数

30、都不小于0。与数学定义直接对应:long gcd1 (long m, long n) return m%n = 0 ? n : gcd1(n,m%n);对欧氏算法的研究保证了本函数能结束,对较大的数计算速度也很快,远远优于顺序检查。第四十七张,PPT共一百零九页,创作于2022年6月对特殊情况可另写一函数,其主体是对gcd1的调用:long gcd(long m, long n) if (m 0) m = -m; if (n %cn, from, to);void henoi(int n,char from,char to,char by) if (n = 1) moveone(from, t

31、o); else henoi(n-1, from, by, to); moveone(from, to); henoi(n-1, by, to, from); moveone定义为函数是为了方便。函数调用:henoi(6, a, b, c);第五十三张,PPT共一百零九页,创作于2022年6月4.4 基本输入输出(IO)IO通过标准库进行常用函数scanfgetcharputchar需要(同printf)第五十四张,PPT共一百零九页,创作于2022年6月格式输入函数scanf功能与printf对应。scanf从标准输入读数据,根据格式描述将实际输入转换到指定类型,转换结果赋给指定变量:sca

32、nf(格式描述串, &变量名, .)格式描述串与printf的类似,其中的转换描述(以%开头)说明输入形式和转换方式。其他参数(个数应与格式串中转换描述一致)指明接受输入的程序变量。形式是在变量名前面加 & 符号。注意:必须写 & 符号,不写将引起严重问题。第五十五张,PPT共一百零九页,创作于2022年6月简单示例:#include int main() int i, n; printf(Please input a number: ); scanf(%d, &n); printf(%d %dn, n, n * n); return 0; 程序执行后输出提示串:Please input a

33、number:等待人的输入。得到输入数据后输出并结束。程序的行为依赖于当时的输入(与前面程序不同)第五十六张,PPT共一百零九页,创作于2022年6月注意实数类型的转换描述与printf的差异。例:设有变量定义:int n; double x; float y;可以写语句:scanf(%d %lf %f, &n, &x, &y);常用的 scanf 转换描述:第五十七张,PPT共一百零九页,创作于2022年6月读数值时, sacnf格式串里的转换描述之间的空格并不必要。上面语句写成下面形式,效果一样。:scanf(%d%lf%f, &n, &x, &y);如果这里的转换描述之间没字符或只有空格

34、,输入的数据之间也只能有空白字符,不能有其他字符。格式串里一般不写转换描述之外的东西。如果写%d, %lf, %f就是要求用逗号分隔输入数据,若输入时不注意就会导致数据不能正常读入。建议不要这样写。scanf格式串的细节在第八章有详细介绍。第五十八张,PPT共一百零九页,创作于2022年6月缓冲式输入若程序要求从标准输入取得信息(如执行scanf),我们由键盘输入,在按Enter键后程序才能得到输入数据造成这种情况的原因是操作系统通常采用“缓冲式”输入方式,把来自键盘的输入临时保存在 “输入缓冲区”(操作系统管理下的一块内存区域)里,直至人按了Enter键,才把缓冲区里的数据送给程序,这时sc

35、anf等输入函数才能读到数据程序经常需要输入一批数据,通过一个循环处理。为此需要在循环中反复调用输入函数下面讨论这种循环输入中的控制问题第五十九张,PPT共一百零九页,创作于2022年6月通过计数器控制的输入循环如果事先知道需要输入的数据项数,就可以用计数器控制输入循环。如由各月降雨量统计一年总量:#include int main() double x, sum; int n; for (sum = 0, n = 0; n 12; +n) printf(Enter next data: ); scanf(%lf, &x); sum += x; printf(Annual Precipitat

36、ion: %fn, sum); return 0;第六十张,PPT共一百零九页,创作于2022年6月假定写程序时不清楚需要输入的数据的确切项数,就无法采用计数循环的简单方法。一种方式是用一个特殊 “结束标志” 控制循环。该“结束标志”应是一个特殊输入值,具有与输入数据同样的类型,但又不是正常输入数据。让程序在循环中不断检测得到的数据,一旦看到这个特殊数据,就知道用户要求结束了。采用这种技术,循环结束条件就是写程序的人与使用者之间的一种约定,当输入满足约定时程序就结束。用特殊结束值控制输入循环第六十一张,PPT共一百零九页,创作于2022年6月例,计算货物总值,每次输入单价和数量。可考虑用特殊值

37、通知程序数据已输入完,例如用单价为0。 #include int main() double price = 1.0, amount, sum = 0.0; while (price != 0) printf(Next data (price amount): ); scanf(%lf %lf, &price, &amount); sum += price * amount; printf(Total price: %fn, sum); return 0;这个程序中循环体的执行次数,完全由程序执行时外部提供的输入数据项数决定。第六十二张,PPT共一百零九页,创作于2022年6月上面两种方式可以

38、解决许多数据输入循环的控制问题,但有时也会遇到困难。例:假定现在要写程序,求一批输入数据的平均值。事先不知道可能输入哪些数据。任何数值都可能出现在需要求平均值的数据中。如果选0作为“结束标志”,而实际数据里有0,这个程序就不能正确处理了(任何选择都有问题)。这种情况具有普遍性,要解决这类问题,就需要进一步理解scanf的功能。第六十三张,PPT共一百零九页,创作于2022年6月深入理解scanfscanf的返回值是int,它顺序处理格式串:根据格式串要求完成输入、转换和对变量的赋值工作正常结束时返回所完成的数据转换项数如果一开始就遇到文件结束,就返回一个特殊符号常量 EOF(是一个int值,后

39、面再介绍)如果没处理完整个格式串就失败时,返回已完成的数据转换项数scanf 用输入数据与正在处理的转换描述比较,如果相符就完成一项转换。例如:若转换描述是 %d,输入得到的是一串数字,就把它们转换为一个整数如果实际输入与转换描述不匹配,转换失败第六十四张,PPT共一百零九页,创作于2022年6月scanf要求三方面一致:格式串中转换描述、对应参数的类型、运行中提供的数据形式。假如格式串要求做整数转换,赋给整型变量。若实际输入不是一串数字,scanf也无法正常完成工作在格式串要求读整数或者浮点数,scanf会跳过遇到的空白字符,从下一非空白字符开始处理下面函数调用可能产生三种返回值:scanf

40、(%lf, &x)返回1表示成功读入一项数据,并存入了 x返回0表示读入数据失败返回EOF值表示遇到文件结束应该通过这种性质控制循环第六十五张,PPT共一百零九页,创作于2022年6月例:读入一些圆盘半径,算出各圆盘的面积并输出。不知圆盘数,可利用scanf的返回值控制循环结束#include void pc_area (double r) /* 定义略 */int main () double x; while (scanf(%lf, &x) = 1) if (x 0) printf(Input error: %fn, x); else pc_area(x); return 0; /* 什么

41、情况下循环结束? */只要scanf的返回值不是1,循环就结束第六十六张,PPT共一百零九页,创作于2022年6月遇到文件结束或错误数据时 scanf 不返回1。如果上面程序遇到输入字母m,转换失败就会导致循环结束。更好的方式是利用标准库定义的符号常量EOF。如果把标准输入定向到某个文件,在读完文件里所有数据后scanf就会返回EOF值。EOF 是什么?一般的C系统把EOF定义为-1,它一定不是正数,不会与scanf的其他返回值混淆。默认情况下,标准输入从键盘得到数据。许多系统里可以用Ctrl-Z或Ctrl-D组合键送入文件结束信息。前面程序运行时,如果按了这种组合键,scanf 就会返回EO

42、F并导致循环结束。第六十七张,PPT共一百零九页,创作于2022年6月例:统计一批输入数据的个数和最小值/最大值/平均值循环读入数据,并完成其他工作。两个变量记录已知的最小/最大值。读数据中考虑更新,使其保存已读数据的最小最大值(循环不变性质)。两个变量记录数据个数,记录已读入数据之和。循环中要正确更新(循环不变性质)。问题:保存最大值和最小值的变量的初始值?下面程序假定最少有一个输入数据,用读入的第一个数据作为最大和最小变量的初始值。第六十八张,PPT共一百零九页,创作于2022年6月#include int main () double sum = 0.0, biggest, smalle

43、st, x; int count = 1; scanf(%lf, &sum); biggest = smallest = sum; while (scanf(%lf, &x) = 1) sum += x; count+; if (x biggest) biggest = x; if (x smallest) smallest = x; /* 输出结果,略 */ return 0;/* 要求至少有一个输入数据 */第六十九张,PPT共一百零九页,创作于2022年6月关于输入循环的总结要输入一批数据时,可根据情况采用不同控制方式循环。主要的三种控制方式:1. 程序内部自主控制,根据程序内部情况决定

44、循环继续或终止,是否继续读入。这一技术的缺点是不够灵活,难以处理事先不清楚项数的输入数据。2. 从输入数据类型里选一个特殊值作为结束标志值,程序使用者可用它通知程序输入结束。这一技术的缺点是有时难以找到合适的结束标志值。3. 通过输入函数的返回值,控制循环的继续或结束。以后的程序实例中还会频繁使用它们。第七十张,PPT共一百零九页,创作于2022年6月字符IO函数getchar和putchargetchar是无参函数,从标准输入读一个字符,返回字符的编码值。getchar的类型特征:int getchar(void)典型使用(输入的字符赋给变量c):c = getchar();标准输入默认连到

45、键盘。没有输入数据时getchar等待,直到人输入字符(并换行)。返回类型int的问题下面解释。第七十一张,PPT共一百零九页,创作于2022年6月putchar把一字符送到标准输出:putchar(O); putchar(K);两字符送到标准输出,使字符显示在屏幕上。例:写程序把由输入的一个字符输出并换行:#include int main () int c; c = getchar(); putchar(c); putchar(n); return 0;/*执行情况?*/第七十二张,PPT共一百零九页,创作于2022年6月输入一系列字符假设要由标准输入得到的多个字符送到标准输出,需要反复读

46、入/输出字符,应该写循环:while (.) c = getchar(); putchar(c);本程序具有普遍性:putchar(c)是处理过程的代表,可根据需要换成其他程序片段。怎样描述循环条件?首先要问的是:希望在什么条件下结束循环?第七十三张,PPT共一百零九页,创作于2022年6月两种可能:1)程序内部确定,与实际输入无关。例如用计数器,读入若干个字符后结束。#include int main () /* 读10个字符,输出各个字符的编码 */ int c, n; for (n = 0; n 10; +n) c = getchar(); printf(%dn, c); return

47、0;第七十四张,PPT共一百零九页,创作于2022年6月2)根据实际输入决定。循环条件与输入有关,是编程者和使用者的协议,得到满足条件的输入时结束循环。例:输入读一行,输出各字符的编码:#include int main () int c; while (1) /*循环执行多少次由输入行包含多少字符确定*/ c = getchar(); if (c = n) break; printf(%d , c); return 0;也可要求遇到其他字符结束。何时结束是一种约定。第七十五张,PPT共一百零九页,创作于2022年6月处理任意的输入字符前面方法需要选一个字符作为表示结束的特殊字符。这个字符就不

48、能再作为输入中的正常字符了。要处理键盘能输入的所有字符,应怎样写结束条件?标准库定义了符号常量EOF(End Of File/文件结束),getchar遇文件结束返回EOF。如果标准输入定向到文件,getchar就会从文件读,文件读完时返回值EOF。由键盘输入文件结束:用Ctrl-Z送文件结束信息。EOF是什么?一般系统定义为-1(具体值并不重要)。程序里只需判断输入函数的返回值是否与EOF值相同。第七十六张,PPT共一百零九页,创作于2022年6月while (c = getchar() != EOF) . . /* 对输入的实际处理 */EOF的值不能与任何字符编码相同。若getchar返

49、回char,就无法给出EOF值。所以getchar返回int。总结:正常情况下getchar返回读入的字符,遇文件结束返回EOF值。应该用int变量接收getchar的返回值,以保证正确判断输入结束。如果用char变量,值超出char范围时结果无定义char ch;while (ch = getchar() != EOF) .注意:赋值操作有值,注意加括号。第七十七张,PPT共一百零九页,创作于2022年6月例:统计(由标准输入得到的)文件中的字符个数。#include int main () int c; long n = 0; while (c = getchar() != EOF) n+

50、; printf(%ldn, n); return 0;标准输入默认连接到键盘。程序执行到getchar等待输入,得到输入后处理。用Ctrl-Z发信息可使循环结束。缓冲式输入:键盘输入字符(行),按Enter键后才能送给程序。因为操作系统通常采用缓冲式输入方式。 第七十八张,PPT共一百零九页,创作于2022年6月从系统中的命名文件读入:设源程序是count.c,编译结果是count.exe。用命令行方式启动程序,将标准输入定向到文件(设被统计文件是abcd.txt):count = 1E-6); return x2;第八十四张,PPT共一百零九页,创作于2022年6月break语句已介绍。c

51、ontinue语句形式:continue;只能用在循环里,使最内层循环体的一次执行结束,进入下次循环。while/do-while的随后动作是条件判断;for的随后动作是变量更新。第八十五张,PPT共一百零九页,创作于2022年6月goto语句/转移语句/转跳语句最老的控制语句。现在已很少用。许多语言仍提供。goto语句与标号配合,实现函数体内的任意控制转移。标号可写在任何语句前面作为goto的目标。形式是:标号名:标号名是标识符。 goto语句的形式:goto 标号名;作用(语义):使控制转到标号处继续执行。break、continue是受限的goto,实现固定方式的控制转移。循环和分支也是

52、goto的包装。FORTRAN就有goto语句。第八十六张,PPT共一百零九页,创作于2022年6月无节制地用goto写程序,费解,常带有难发现的错误。1968年Dijkstra撰文“goto是有害的” 。六年大辩论的结果是结构程序设计革命,语言都引进“标准”控制结构,教育和实践中提倡结构化程序设计。对goto的认识:不用或尽量少用。大部分goto实际上是为构造条件或循环:1)向前转跳2)向后转跳label: . . goto label; . . . goto label;label: .用循环或条件重写的程序更清晰易读,不容易有错。随便使用goto是不良编程习惯。不合理的goto表明对问题

53、欠分析,没做好流程分解,函数抽象等,写的是不成熟的程序。第八十七张,PPT共一百零九页,创作于2022年6月开关语句(switch语句)多分支结构,根据一个整型值选择。形式:switch (整型表达式) case 整型常量表达式: 语句序列 . . default: 语句序列常量表达式常用整数/字符等。default部分可缺,语句序列可缺,可含多个语句。“case 整型常量表达式:”看作标号。语义:求值整型表达式,将值顺序与各整型常量表达式比较,遇到相等时转入执行;无匹配但有default则从default:处继续;没有default时结束。第八十八张,PPT共一百零九页,创作于2022年6月

54、例,按x的值确定分支,1和2分别处理,其他统一处理switch (x) case 1: . . break; case 2: . . break; default: . . break;各case标号值必须互不相同。习惯在各分支最后写break,包括最后分支。规定:如果分支最后无break,语句序列执行完后进入下一分支的语句序列。这导致一种代码共享。一般认为,除非几个分支的语句相同,否则不提倡共享。因为这导致程序段相互依赖,对程序修改不利。第八十九张,PPT共一百零九页,创作于2022年6月while (c = getchar() != EOF) switch (c) case : nb+;

55、break; case1: case2: case3: case4: case5: case6: case7: case8: case9: case0: nd+; break; case n: nl+; break; case : case : nc+; break; default: nn+; break;例:分别对空格/数字/行数/花括号/其他字符计数。第九十张,PPT共一百零九页,创作于2022年6月4.6 程序设计实例例:简单交互式计算器。假定它可以输入并计算:128+365254+143810313+524 输入一行算一个结果。直至用户要求结束。显然程序里应有一个基本循环: whil

56、e (还有输入) 取得数据 计算并输出可考虑用scanf读数据,用文件结束或非数字表示输入结束。第九十一张,PPT共一百零九页,创作于2022年6月#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

57、() != n) /丢掉本行剩余字符 ; continue; printf(%d+%d=%dn,left,right,left+right); return 0;第九十二张,PPT共一百零九页,创作于2022年6月“常量”是标识符形式,在程序里代表同一常数的东西。用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

58、(C = %d, F = %fn, c, c * 5.0/9.0 + 32.0); return 0; /*这样的程序更容易修改*/第九十三张,PPT共一百零九页,创作于2022年6月符号形式表示能帮人理解程序意义。程序里两个0可能代表不同意义,数值形式没有任何区分。采用符号常量可提高可读性。将所需常数定义为符号常量,在程序中统一使用是很好的方法。使程序更容易修改(修改时不必浏览整个程序)。对大程序的作用更明显。第五章介绍其他常量定义方式。enum的详细讨论在第九章,目前作为一种定义符号整型常量的机制。 第九十四张,PPT共一百零九页,创作于2022年6月单词计数问题正文文件可看成字符序列,空

59、白字符(空格 、制表符t、换行符n)把序列分隔为一个个“单词”。要求写程序统计文件中的单词个数。需要一个计数器,遇到一个词将计数器加一。考虑用函数getchar读字符。程序主要部分的框架:while (文件未结束) 遇到一个词时计数器加一;打印统计信息;用getchar输入,很容易判断文件结束。问题是如何确定“遇到了一个词”。第九十五张,PPT共一百零九页,创作于2022年6月若读的字符是单词首字符,则计数器加一。读入字符过程中需要区分是否空白。问题:非空白字符未必是词的开始,是否新词要看前一字符是否空白。可见:不能孤立地处理,要参考前面情况。必须做情况记录,以便后面参考。前后关系分两种情况:

60、1)读到空白,随后遇非空白字符就是新词;2)读到非空白,随后不会遇到新词。可看作处理过程的不同状态。两种状态:1)读在词外(遇到非空白是新词);2)读在词内。在读入字符的过程中读入状态也不断转换。典型,可以用有限状态转换系统(自动机)描述。第九十六张,PPT共一百零九页,创作于2022年6月IN/OUT(词里/词外)表示两种读入状态。读字符的动态过程可以用图示形象描述。在从OUT转换到IN时,遇到新词,计数。第九十七张,PPT共一百零九页,创作于2022年6月用变量state记录状态,令其值为IN/OUT,只要求这两个值不同(不会同时处在两种状态)。一个字符(假设存在变量c)的处理可描述为:i

温馨提示

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

评论

0/150

提交评论