基本程序设计技术课件.ppt
关于基本程序设计技术现在学习的是第1页,共109页第四章第四章基本程序设计技术基本程序设计技术现在学习的是第2页,共109页学习程序设计需要注意规律性的东西。三种流程模式是重要总结。本章还讨论:基本输入和输出 递归的程序设计 其他控制结构 顺序模式最简单 选择模式:要确定判断条件及不同情况下的动作 开始的难点在实现重复执行的循环。重复执行比较复杂,牵涉问题多,是本章重点现在学习的是第3页,共109页4.1循环程序设计写循环首先要发现循环。注意计算中的重复性动作,引进循环可能统一描述和处理。重复动作的常见实例:一批类似数据做同样加工处理 累积一批可按规律算出的数据(累加等)反复从一个结果算出下一结果(递推)若重复次数很多,就应该考虑用循环 如果重复次数无法确定,就必须用循环描述现在学习的是第4页,共109页例:求13到315所有数的平方根之和。可以一个个地加,但更方便的方法是写循环。需要一个变量保存部分和,逐步把各平方根加上去;需要一个变量保存变动轨迹,从初值开始每次修改。典型for循环。假定已有总和变量sum和循环变量n:for(sum=0.0,n=13;n=13;-n)sum+=sqrt(n);这里的两个循环等效。一般采用向上循环现在学习的是第5页,共109页可以用while语句重写,两种结构功能上等效。例,求 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);现在学习的是第6页,共109页例1:打印出 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;现在学习的是第7页,共109页综合起来可得到完整程序:#include int 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不是)现在学习的是第8页,共109页方法二:需要打印的一定是从1开始连续几个整数的平方,可从1开始打印到平方大于200为止。for(n=1;n*n=200;+n)printf(%d,n*n);/*注意打印什么*/还可以考虑利用递推公式:12111nnn方法一:产生所有备选数据(1到200的整数),检查排除不合格的。生成与检查是解决问题的常用方法。方法二是针对具体问题的特定方法。现在学习的是第9页,共109页例 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;/*没有因子,是素数*/现在学习的是第10页,共109页从循环中退出:isprime发现一个因子就可做结论。return使函数结束,也导致循环结束。函数不完善,对1给出“是素数”,负数也会给出不合理结果。应该在循环前处理特殊情况:if(n=1)return 0;负数问题?如果需要可以另外考虑处理。现在学习的是第11页,共109页例3,艰难旅程(浮点误差)。乌龟要去环球。第1秒爬1米,第2秒爬1/2米,第3秒爬1/3米,第4秒爬1/4米,。问一小时能爬出多远?爬20米需多少秒?根据数学,乌龟能完成环球,可以爬得任意远。这里想比较float和double的计算误差情况。这里只考虑20米需要多少时间。写出下面函数:long scndsf(float d)long i;float x=0.0;for(i=1;x d;+i)x+=1/(float)i;return i-1;现在学习的是第12页,共109页写下面语句,执行时总也不输出: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的特点给了一种简化写法,供参考 学习了其他结构后有改进的写法现在学习的是第17页,共109页例5:定义函数,利用公式求 近似值。设为double dsin(double x)。方法:循环累加,n 趋向无穷的过程中项值趋于0,而累加值趋向函数值。需要用循环。保存累加和的变量sum,循环中求出的项值用 t 保存。sin()()!xxnnnn121210sinxsum=0.0;对n为0计算t;while(需要继续)sum=sum+t;计算下一个t;循环结束条件:例如用项绝对值小于 。106现在学习的是第18页,共109页问题:t 的值如何计算?第一个 t就是 x;分析可以发现项值的递推公式:ttxnnnn 12221/()double dsin(double x)double 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;现在学习的是第19页,共109页一些情况:实参值很小时,循环将很快结束。实参绝对值很大时循环可能做许多次,项的绝对值可能达到很大,n 值也可能超出整数的表示范围 可考虑用fmod把参数归约到0,2 之内启示:理解问题非常重要。发现了项的递推性质能节省许多计算(否则就要再写一个嵌套循环完成项的计算)级数收敛性质能帮人认识情况,改进计算方法写程序时必须仔细考虑问题本身的性质现在学习的是第20页,共109页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退出了循环现在学习的是第21页,共109页希望每个偶数只输出一行。怎样在发现素数对后停止?增加对循环的控制:把发现素数分解作为条件加入。引入整型变量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语言引进了专门的控制语句现在学习的是第22页,共109页break语句,形式:break;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;现在学习的是第23页,共109页利用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。现在学习的是第24页,共109页循环中的几种变量循环中常出现几类变量,注意这些有助于对循环的思考和分析。也是写循环程序的经验总结注意:分类不是绝对的,不同类别没有截然界限1)循环控制变量(循环变量):循环前设初值,循环中递增/递减,达到/超过界限时循环结束。它们控制循环的进行/结束。for中常有这类变量。for(n=0;n=0;-n).for(n=2;n 52;n+=4).这种循环是固定次数的循环。这种循环可能展开现在学习的是第25页,共109页2)累积变量:循环中常用+=或*=等更新。初值常用运算的单位元(加用0;乘用1为初值)。循环结束时变量终值被作为循环计算结果。3)递推变量:前两类变量的推广。几个协同工作的变量,每次由几个变量推出一个新值,其余依次更新。对变量x1、x2、x3,循环体可能有序列:x1=x2;x2=x3;x3=.x1.x2.;例如上面cbrt里的变量x1和x2。现在学习的是第26页,共109页写循环时要考虑和解决问题列表:循环涉及到哪些变量,需引进哪些临时性变量?循环如何开始?循环开始前给变量什么初值?循环中变量的值如何改变?什么情况下继续(或终止)循环?循环终止后如何得到所需结果?用哪种结构实现循环,等等。工作方式:分析问题,发掘线索,最终完成程序。程序设计不是教条,典型问题也无标准答案。并非最简单的问题总有多种解决方法,往往各有长短。“正确”程序常有优劣之分。现在学习的是第27页,共109页4.3循环与递归程序中有循环可能导致很长的计算。没有循环结构也能描述这类计算。C语言允许递归,可在函数内调用自身,程序常常更简单清晰。例:定义计算整数阶乘的函数:12(n-1)n乘的次数依赖于n,定义时不知道,每次用可能不同。程序的典型情况:计算“次数”依赖某些参数的值。省略号不科学。严格定义需用递归形式。现在学习的是第28页,共109页nnnnn!()!1010注意递归定义的形式。这也提出了一种计算方法。如果语言允许递归定义函数,就可以直接翻译为程序。C允许递归定义:在函数定义内调用被定义函数本身。类型特征可定为:int fact(int)阶乘值增长极快(数学),更合适的类型特征:long fact(long)现在学习的是第29页,共109页递归的函数定义: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;比递归定义长,需要引进多个局部变量。现在学习的是第30页,共109页fact(3)3*fact(2)fact(2)2*fact(1)fact(1)1*fact(0)fact(0)调用 fact(3)返回值 1返回值 1返回值 2返回值 6图 3.3fact(3)的计算过程fact实现的计算过程很不简单。计算中fact被递归调用的次数由实参确定。考虑负参数值处理。可改为:n=1?1:.现在学习的是第31页,共109页递归定义导致的计算过程参数不同fact递归调用次数(步数)不同。定义只有一个语句,可能要许多步才能完成。包含递归(和循环)的程序产生的计算过程和性质更复杂,能完成更复杂工作,理解和书写也更困难。递归的函数定义需要条件表达式或if,必须区分:直接给出结果的情况。是递归的基础 需要递归处理的情况。其中把对较复杂情况的计算归结为对更简单情况的计算基本运算/关系判断/条件表达式,加函数定义和递归定义构成了一个(理论上)“足够强的”的程序语言。现在学习的是第32页,共109页程序实例Fibonacci(斐波那契)序列的递归定义:FFFFFnnnn0112111,()Fibonacci 序列增长很快,返回值选long。递归定义:long fib(int n)return n2?1:fib(n-1)+fib(n-2);负参数值定义为 1。这是“合理”处置。问题分析:这个程序好不好?一方面,很好!程序与数学定义的关系很清晰,正确性容易确认,定义易读易理解。现在学习的是第33页,共109页但这个定义有一个本质性缺点。示意图:fib(5)fib(4)fib(3)fib(3)fib(2)fib(2)fib(1)Fib(2)fib(1)fib(1)fib(0)fib(1)fib(0)fib(1)fib(0)图4.1 fib(5)计 算 中 的 函 数 调 用 情 况现在学习的是第34页,共109页存在大量重复计算,参数越大重复计算越多。有关系吗?随着参数增大,计算中重复增长迅速,最快的微机上一分钟大约可以算出fib(45)参数加1,fib多用近一倍时间(指数增长)。最快的微机一小时算不出fib(55),算fib(100)要数万年计算需时间,复杂计算需要很长时间。这是计算机的本质特征/弱点。说明它不万能,有些事清“不能”做。求Fibonacci 值有更好计算办法(下面介绍)。现在学习的是第35页,共109页人们发现了许多实际问题,理论上说可用计算机解决(可写出计算它的程序),但对规模大的情况(“大的参数 n”),人类永远等不到计算完成。这时能说问题解决了吗?理解这个情况对于理解计算机是非常重要的。这里有一大类问题称为计算中的“难解问题”,其中有许多很实际的问题(规划、调度、优化等)。这方面的理论和实际技术的研究极为重要:计算复杂性,难解问题,“P=NP?”问题。另外,对于许多问题的实用的有效算法,有极大的理论价值和实际价值。现在学习的是第36页,共109页为计算过程计时统计程序/程序片段的计算时间有助于理解程序性质。许多语言或系统都提供了内部计时功能。有关函数在time.h,统计程序时间时程序头部应写:#include 在程序里计时,通常写表达式:clock()/CLOCKS_PER_SEC得到从程序开始到表达式求值时所经历的秒数。注意:有些老的C系统(如Turbe-C)用 CLK_TCK。现在学习的是第37页,共109页确定计算fib(45)所需要的时间的程序:#include#include long fib(int n)return n=1?1:fib(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;现在学习的是第38页,共109页Fibonacci数的递推计算 易见 1)F1和F2是12)知道连续两个Fibonacci数,就可算出下一个递推计算方式:逐个前推,可用循环实现:long fib1(int n)long a=1,b=1,c,i;if(n=1)return 1;for(c=a+b,i=2;i n;+i)a=b;b=c;c=a+b;return c;/*对吗?*/现在学习的是第39页,共109页循环结束时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 值是 k(小于n),循环体中的语句使a变成Fk-1,b变成Fk,c变成Fk+1。i 值增 1 使我们又有a为Fi-2,b变成Fi-1,c变成Fi 根据归纳法,每次判断 i 的值时c正是 Fi。现在学习的是第40页,共109页 循环实现重复性计算,循环体可能执行多次。如何保证对各种数据都能正确完成计算?循环中变量不断变化。写循环要考虑变量间的关系,保证某些关系在循环中不变:循环的不变关系。写循环时最重要的就是想清循环中应维持变量间的什么关系才能保证循环结束时变量能处在所需状态。写完循环后应仔细检查是否满足要求。循环不变关系(循环不变量)是理解循环、写好循环的关键。这个方面有很多研究,有许多理论结果。现在学习的是第41页,共109页问题:用循环的函数比用递归定义的好吗?新函数在计算时间上有极大优越性。计算时间由循环次数确定。循环体执行次数大致为n。fib(100)只需约100次循环,几乎察觉不到所花费时间。新函数定义较复杂,有复杂的循环。要理解程序意义,确认函数对任何参数都算出Fibonacci值,需要借助“循环不变关系”的概念和细致分析。上面分析中没考虑数据表示范围,long类型一般无法表示fib(100)。注意:这个例子并不是说明递归比循环的效率低。完全可以写出计算fib的同样高效的递归定义的函数现在学习的是第42页,共109页求最大公约数(greatest common divisor,GCD):写函数 long gcd(long,long)方式1:k取初值1后递增,大于m或n之一时结束。如何得到所需结果?m和n可能有多个公约数,最后的k值不是m和n的公约数(它已大于两数之一)。解法1:逐个检查,直到找到能同时整除m和n的最大整数(生成与检查)。需辅助变量k记录检查值。简单方式:k顺序取值(初值/更新/结束),可用循环实现。需要记录循环中找到的公约数。现在学习的是第43页,共109页只需记录已找到最大的公约数,用变量d,初值1(是公约数),遇到新公约数(一定更大)时记入d:if(m%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会留下来,也正确。现在学习的是第44页,共109页还有一些特殊情况需要处理:1)m和n都为0需特殊处理。例如令函数返回值0;2)若m和n中一个为0,gcd是另一个数。函数的返回值正确。也可直接判断处理;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是最大公约数*/本方法比前一方法简单一些。两种方法的共同点是重复测试。这类方法的缺点是效率较低,参数大时循环次数很多。现在学习的是第46页,共109页解法2:求GCD有著名的欧几里德算法(欧氏算法,辗转相除法)。最大公约数的递归定义:gcd(,)modgcd(,mod)modm nnmnn mnmn00函数定义(递归):假设第二个参数非0,且参数都不小于0。与数学定义直接对应:long gcd1(long m,long n)return m%n=0?n:gcd1(n,m%n);对欧氏算法的研究保证了本函数能结束,对较大的数计算速度也很快,远远优于顺序检查。现在学习的是第47页,共109页对特殊情况可另写一函数,其主体是对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,to);else henoi(n-1,from,by,to);moveone(from,to);henoi(n-1,by,to,from);moveone定义为函数是为了方便。函数调用:henoi(6,a,b,c);现在学习的是第53页,共109页4.4 基本输入输出(IO)IO通过标准库进行常用函数 scanf getchar putchar需要(同printf)现在学习的是第54页,共109页格式输入函数scanf功能与printf对应。scanf从标准输入读数据,根据格式描述将实际输入转换到指定类型,转换结果赋给指定变量:scanf(格式描述串,&变量名,.)格式描述串与printf的类似,其中的转换描述(以%开头)说明输入形式和转换方式。其他参数(个数应与格式串中转换描述一致)指明接受输入的程序变量。形式是在变量名前面加&符号。注意:必须写&符号,不写将引起严重问题。现在学习的是第55页,共109页简单示例:#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 number:等待人的输入。得到输入数据后输出并结束。程序的行为依赖于当时的输入(与前面程序不同)现在学习的是第56页,共109页转转换换描描述述 参参数数变变量量类类型型所所要要求求的的实实际际输输入入%dint十十进进制制整整数数数数字字序序列列%ldlong同同上上%ffloat十十进进制制数数,可可以以有有小小数数点点及及指指数数部部分分%lfdouble同同上上%Lflong double 同同上上注意实数类型的转换描述与printf的差异。例:设有变量定义:int n;double x;float y;可以写语句:scanf(%d%lf%f,&n,&x,&y);常用的 scanf 转换描述:现在学习的是第57页,共109页读数值时,sacnf格式串里的转换描述之间的空格并不必要。上面语句写成下面形式,效果一样。:scanf(%d%lf%f,&n,&x,&y);如果这里的转换描述之间没字符或只有空格,输入的数据之间也只能有空白字符,不能有其他字符。格式串里一般不写转换描述之外的东西。如果写%d,%lf,%f就是要求用逗号分隔输入数据,若输入时不注意就会导致数据不能正常读入。建议不要这样写。scanf格式串的细节在第八章有详细介绍。现在学习的是第58页,共109页缓冲式输入若程序要求从标准输入取得信息(如执行scanf),我们由键盘输入,在按Enter键后程序才能得到输入数据造成这种情况的原因是操作系统通常采用“缓冲式”输入方式,把来自键盘的输入临时保存在“输入缓冲区”(操作系统管理下的一块内存区域)里,直至人按了Enter键,才把缓冲区里的数据送给程序,这时scanf等输入函数才能读到数据程序经常需要输入一批数据,通过一个循环处理。为此需要在循环中反复调用输入函数下面讨论这种循环输入中的控制问题现在学习的是第59页,共109页通过计数器控制的输入循环如果事先知道需要输入的数据项数,就可以用计数器控制输入循环。如由各月降雨量统计一年总量:#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 Precipitation:%fn,sum);return 0;现在学习的是第60页,共109页假定写程序时不清楚需要输入的数据的确切项数,就无法采用计数循环的简单方法。一种方式是用一个特殊“结束标志”控制循环。该“结束标志”应是一个特殊输入值,具有与输入数据同样的类型,但又不是正常输入数据。让程序在循环中不断检测得到的数据,一旦看到这个特殊数据,就知道用户要求结束了。采用这种技术,循环结束条件就是写程序的人与使用者之间的一种约定,当输入满足约定时程序就结束。用特殊结束值控制输入循环现在学习的是第61页,共109页例,计算货物总值,每次输入单价和数量。可考虑用特殊值通知程序数据已输入完,例如用单价为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;这个程序中循环体的执行次数,完全由程序执行时外部提供的输入数据项数决定。现在学习的是第62页,共109页上面两种方式可以解决许多数据输入循环的控制问题,但有时也会遇到困难。例:假定现在要写程序,求一批输入数据的平均值。事先不知道可能输入哪些数据。任何数值都可能出现在需要求平均值的数据中。如果选0作为“结束标志”,而实际数据里有0,这个程序就不能正确处理了(任何选择都有问题)。这种情况具有普遍性,要解决这类问题,就需要进一步理解scanf的功能。现在学习的是第63页,共109页深入理解scanfscanf的返回值是int,它顺序处理格式串:根据格式串要求完成输入、转换和对变量的赋值 工作正常结束时返回所完成的数据转换项数 如果一开始就遇到文件结束,就返回一个特殊符号常量 EOF(是一个int值,后面再介绍)如果没处理完整个格式串就失败时,返回已完成的数据转换项数scanf 用输入数据与正在处理的转换描述比较,如果相符就完成一项转换。例如:若转换描述是%d,输入得到的是一串数字,就把它们转换为一个整数如果实际输入与转换描述不匹配,转换失败现在学习的是第64页,共109页 scanf要求三方面一致:格式串中转换描述、对应参数的类型、运行中提供的数据形式。假如格式串要求做整数转换,赋给整型变量。若实际输入不是一串数字,scanf也无法正常完成工作 在格式串要求读整数或者浮点数,scanf会跳过遇到的空白字符,从下一非空白字符开始处理下面函数调用可能产生三种返回值:scanf(%lf,&x)返回1表示成功读入一项数据,并存入了 x 返回0表示读入数据失败 返回EOF值表示遇到文件结束应该通过这种性质控制循环现在学习的是第65页,共109页例:读入一些圆盘半径,算出各圆盘的面积并输出。不知圆盘数,可利用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;/*什么情况下循环结束?*/只要scanf的返回值不是1,循环就结束现在学习的是第66页,共109页遇到文件结束或错误数据时 scanf 不返回1。如果上面程序遇到输入字母m,转换失败就会导致循环结束。更好的方式是利用标准库定义的符号常量EOF。如果把标准输入定向到某个文件,在读完文件里所有数据后scanf就会返回EOF值。EOF 是什么?一般的C系统把EOF定义为-1,它一定不是正数,不会与scanf的其他返回值混淆。默认情况下,标准输入从键盘得到数据。许多系统里可以用Ctrl-Z或Ctrl-D组合键送入文件结束信息。前面程序运行时,如果按了这种组合键,scanf 就会返回EOF并导致循环结束。现在学习的是第67页,共109页例:统计一批输入数据的个数和最小值/最大值/平均值循环读入数据,并完成其他工作。两个变量记录已知的最小/最大值。读数据中考虑更新,使其保存已读数据的最小最大值(循环不变性质)。两个变量记录数据个数,记录已读入数据之和。循环中要正确更新(循环不变性质)。问题:保存最大值和最小值的变量的初始值?下面程序假定最少有一个输入数据,用读入的第一个数据作为最大和最小变量的初始值。现在学习的是第68页,共109页#include int main()double sum=0.0,biggest,smallest,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;/*要求至少有一个输入数据*/现在学习的是第69页,共109页关于输入循环的总结要输入一批数据时,可根据情况采用不同控制方式循环。主要的三种控制方式:1.程序内部自主控制,根据程序内部情况决定循环继续或终止,是否继续读入。这一技术的缺点是不够灵活,难以处理事先不清楚项数的输入数据。2.从输入数据类型里选一个特殊值作为结束标志值,程序使用者可用它通知程序输入结束。这一技术的缺点是有时难以找到合适的结束标志值。3.通过输入函数的返回值,控制循环的继续或结束。以后的程序实例中还会频繁使用它们。现在学习的是第70页,共109页字符IO函数getchar和putchargetchar是无参函数,从标准输入读一个字符,返回字符的编码值。getchar的类型特征:int getchar(void)典型使用(输入的字符赋给变量c):c=getchar();标准输入默认连到键盘。没有输入数据时getchar等待,直到人输入字符(并换行)。返回类型int的问题下面解释。现在学习的是第71页,共109页putchar把一字符送到标准输出:putchar(O);putchar(K);两字符送到标准输出,使字符显示在屏幕上。例:写程序把由输入的一个字符输出并换行:#include int main()int c;c=getchar();putchar(c);putchar(n);return 0;/*执行情况?*/现在学习的是第72页,共109页输入一系列字符假设要由标准输入得到的多个字符送到标准输出,需要反复读入/输出字符,应该写循环:while(.)c=getchar();putchar(c);本程序具有普遍性:putchar(c)是处理过程的代表,可根据需要换成其他程序片段。怎样描述循环条件?首先要问的是:希望在什么条件下结束循环?现在学习的是第73页,共109页两种可能:1)程序内部确定,与实际输入无关。例如用计数器,读入若干个字符后结束。#include int main()/*读10个字符,输出各个字符的编码*/int c,n;for(n=0;n 10;+n)c=getchar();printf(%dn,c);return 0;现在学习的是第74页,共109页2)根据实际输入决定。循环条件与输入有关,是编程者和使用者的协议,得到满足条件的输入时结束循环。例:输入读一行,输出各字符的编码:#include int main()int c;while(1)/*循环执行多少次由输入行包含多少字符确定*/c=getchar();if(c=n)break;printf(%d,c);return 0;也可要求遇到其他字符结束。何时结束是一种约定。现在学习的是第75页,共109页处理任意的输入字符前面方法需要选一个字符作为表示结束的特殊字符。这个字符就不能再作为输入中的正常字符了。要处理键盘能输入的所有字符,应怎样写结束条件?标准库定义了符号常量EOF(End Of File/文件结束),getchar遇文件结束返回EOF。如果标准输入定向到文件,getchar就会从文件读,文件读完时返回值EOF。由键盘输入文件结束:用Ctrl-Z送文件结束信息。EOF是什么?一般系统定义为-1(具体值并不重要)。程序里只需判断输入函数的返回值是否与EOF值相同。现在学习的是第76页,共109页while(c=getchar()!=EOF)./*对输入的实际处理*/EOF的值不能与任何字符编码相同。若getchar返回char,就无法给出EOF值。所以getchar返回int。总结:正常情况下getchar返回读入的字符,遇文件结束返回EOF值。应该用int变量接收getchar的返回值,以保证正确判断输入结束。如果用char变量,值超出char范围时结果无定义char ch;while(ch=getchar()!=EOF).注意:赋值操作有值,注意加括号。现在学习的是第77页,共109页例:统计(由标准输入得到的)文件中的字符个数。#include int main()int c;long n=0;while(c=getchar()!=EOF)n+;printf(%ldn,n);return 0;标准输入默认连接到键盘。程序执行到getchar等待输入,得到输入后处理。用Ctrl-Z发信息可使循环结束。缓冲式输入:键盘输入字符(行),按Enter键后才能送给程序。因为操作系统通常采用缓冲式输入方式。现在学习的是第78页,共109页从系统中的命名文件读入:设源程序是count.c,编译结果是count.exe。用命令行方式启动程序,将标准输入定向到文件(设被统计文件是abcd.txt):count=1E-6);return x2;现在学习的是第84页,共109页break语句已介绍。continue语句形式:continue;只能用在循环里,使最内层循环体的一次执行结束,进入下次循环。while/do-while的随后动作是条件判断;for的随后动作是变量更新。while(.).break;.continue;.Break 和 continue 语句引起的控制转移现在学习的是第85页,共109页goto语句/转移语句/转跳语句最老的控制语句。现在已很少用。许多语言仍提供。goto语句与标号配合,实现函数体内的任意控制转移。标号可写在任何语句前面作为goto的目标。形式是:标号名:标号名是标识符。goto语句的形式:goto 标号名;作用(语义):使控制转到标号处继续执行。break、continue是受限的goto,实现固定方式的控制转移。循环和分支也是goto的包装。FORTRAN就有goto语句。现在学习的是第86页,共109页无节制地用goto写程序,费解,常带有难发现的错误。1968年Dijkstra撰文“goto是有害的”。六年大辩论的结果是结构程序设计革命,语言都引进“标准”控制结构,教育和实践中提倡结构化程序设计。对goto的认识:不用或尽量少用。大部分goto实际上是为构造条件或循环:1)向前转跳2)向后转跳label:.goto label;.goto label;label:.用循环或条件重写的程序更清晰易读,不容易有错。随便使用goto是不良编程习惯。不合理的goto表明对问题欠分析,没做好流程分解,函数抽象等,写的是不成熟的程序。现在学习的是第87页,共109页开关语句(switch语句)多分支结构,根据一个整型值选择。形式:switch(整型表达式)case 整型常量表达式:语句序列 .default:语句序列常量表达式常用整数/字符等。default部分可缺,语句序列可缺,可含多个语句。“case 整型常量表达式:”看作标号。语义:求值整型表达式,将值顺序与各整型常量表达式比较,遇到相等时转入执行;无匹配但有default则从default:处继续;没有default时结束。现在学习的是第88页,共109页例,按x的值确定分支,1和2分别处理,其他统一处理switch(x)case 1:.break;case 2:.break;default:.break;各case标号值必须互不相同。习惯在各分支最后写break,包括最后分支。规定:如果分支最后无break,语句序列执行完后进入下一