基本程序設(shè)計(jì)技術(shù)_第1頁
基本程序設(shè)計(jì)技術(shù)_第2頁
基本程序設(shè)計(jì)技術(shù)_第3頁
基本程序設(shè)計(jì)技術(shù)_第4頁
基本程序設(shè)計(jì)技術(shù)_第5頁
已閱讀5頁,還剩104頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

關(guān)于基本程序設(shè)計(jì)技術(shù)第一頁,共一百零九頁,編輯于2023年,星期日第四章

基本程序設(shè)計(jì)技術(shù)第二頁,共一百零九頁,編輯于2023年,星期日學(xué)習(xí)程序設(shè)計(jì)需要注意規(guī)律性的東西。三種流程模式是重要總結(jié)。本章還討論:基本輸入和輸出遞歸的程序設(shè)計(jì)其他控制結(jié)構(gòu)順序模式最簡(jiǎn)單選擇模式:要確定判斷條件及不同情況下的動(dòng)作開始的難點(diǎn)在實(shí)現(xiàn)重復(fù)執(zhí)行的循環(huán)。重復(fù)執(zhí)行比較復(fù)雜,牽涉問題多,是本章重點(diǎn)第三頁,共一百零九頁,編輯于2023年,星期日4.1循環(huán)程序設(shè)計(jì)寫循環(huán)首先要發(fā)現(xiàn)循環(huán)。注意計(jì)算中的重復(fù)性動(dòng)作,引進(jìn)循環(huán)可能統(tǒng)一描述和處理。重復(fù)動(dòng)作的常見實(shí)例:一批類似數(shù)據(jù)做同樣加工處理累積一批可按規(guī)律算出的數(shù)據(jù)(累加等)反復(fù)從一個(gè)結(jié)果算出下一結(jié)果(遞推)若重復(fù)次數(shù)很多,就應(yīng)該考慮用循環(huán)如果重復(fù)次數(shù)無法確定,就必須用循環(huán)描述第四頁,共一百零九頁,編輯于2023年,星期日例:求13到315所有數(shù)的平方根之和。可以一個(gè)個(gè)地加,但更方便的方法是寫循環(huán)。需要一個(gè)變量保存部分和,逐步把各平方根加上去;需要一個(gè)變量保存變動(dòng)軌跡,從初值開始每次修改。典型for循環(huán)。假定已有總和變量sum和循環(huán)變量n:for(sum=0.0,n=13;n<=315;++n)sum+=sqrt(n);向下循環(huán):for(sum=0.0,n=315;n>=13;--n)sum+=sqrt(n);這里的兩個(gè)循環(huán)等效。一般采用向上循環(huán)第五頁,共一百零九頁,編輯于2023年,星期日可以用while語句重寫,兩種結(jié)構(gòu)功能上等效。例,求[13,315]間每隔7的各整數(shù)的平方根之和。一般不用浮點(diǎn)數(shù)控制循環(huán),尤其是增量為小數(shù)或包含小數(shù)時(shí)。例:求從0到100每隔0.2的數(shù)的平方根之和:doublesum,x;for(sum=0.0,x=0.2;x<=100.0;x+=0.2)

sum+=sqrt(x);由于浮點(diǎn)計(jì)算誤差,不能保證循環(huán)500次。應(yīng)寫:intn;doublesum;for(sum=0.0,n=1;n<=500;++n)sum+=sqrt(0.2*n);第六頁,共一百零九頁,編輯于2023年,星期日例1:打印出1到200間的完全平方數(shù)。方法一:逐個(gè)檢查,遇平方數(shù)打印。重復(fù)做,每次檢查一個(gè)數(shù)。循環(huán)框架:for(n=1;n<=200;n++)if(n是完全平方數(shù))打印n;需填充:數(shù)是否完全平方數(shù),沒有直接判斷手段。可以順序檢查,是否存在某數(shù),其平方恰為n。這構(gòu)成(循環(huán)內(nèi)的)新循環(huán),需要一個(gè)新循環(huán)變量。m可以從1開始,遞增,直至m*m大于n:

for(m=1;m*m<=n;m++) if(m*m==n)打印n;第七頁,共一百零九頁,編輯于2023年,星期日綜合起來可得到完整程序:#include<stdio.h>intmain(){intm,n;for(n=1;n<=200;n++)for(m=1;m*m<=n;m++)if(m*m==n)printf("%d",n);printf("\n");/*最后輸出一個(gè)換行符*/return0;}內(nèi)層循環(huán)結(jié)束:1,找到m使m*m==n(n是完全平方數(shù))2,試探了所有可能,但都不成功(n不是)第八頁,共一百零九頁,編輯于2023年,星期日方法二:需要打印的一定是從1開始連續(xù)幾個(gè)整數(shù)的平方,可從1開始打印到平方大于200為止。for(n=1;n*n<=200;++n)printf("%d",n*n);/*注意打印什么*/

還可以考慮利用遞推公式:方法一:產(chǎn)生所有備選數(shù)據(jù)(1到200的整數(shù)),檢查排除不合格的。生成與檢查是解決問題的常用方法。方法二是針對(duì)具體問題的特定方法。第九頁,共一百零九頁,編輯于2023年,星期日例2:寫函數(shù)判斷整數(shù)是否為素?cái)?shù)(謂詞)。類型特征可用intisprime(int),返回0/1值n是素?cái)?shù)則它沒有真因子,m是n的因子用(n%m==0)描述,若m<n且m不是1就是真因子。可令變量m取初值2循環(huán)試除。顯然試到m*m>n就夠了。函數(shù)定義:intisprime(intn){/*n是否素?cái)?shù)*/intm=2;for(;m*m<=n;m++)if(n%m==0)return0;return1;/*沒有因子,是素?cái)?shù)*/}第十頁,共一百零九頁,編輯于2023年,星期日從循環(huán)中退出:isprime發(fā)現(xiàn)一個(gè)因子就可做結(jié)論。return使函數(shù)結(jié)束,也導(dǎo)致循環(huán)結(jié)束。函數(shù)不完善,對(duì)1給出“是素?cái)?shù)”,負(fù)數(shù)也會(huì)給出不合理結(jié)果。應(yīng)該在循環(huán)前處理特殊情況:

if(n<=1)return0;負(fù)數(shù)問題?如果需要可以另外考慮處理。第十一頁,共一百零九頁,編輯于2023年,星期日例3,艱難旅程(浮點(diǎn)誤差)。烏龜要去環(huán)球。第1秒爬1米,第2秒爬1/2米,第3秒爬1/3米,第4秒爬1/4米,…。問一小時(shí)能爬出多遠(yuǎn)?爬20米需多少秒?根據(jù)數(shù)學(xué),烏龜能完成環(huán)球,可以爬得任意遠(yuǎn)。這里想比較float和double的計(jì)算誤差情況。這里只考慮20米需要多少時(shí)間。寫出下面函數(shù):longscndsf(floatd){longi;floatx=0.0;for(i=1;x<d;++i)x+=1/(float)i;returni-1;}第十二頁,共一百零九頁,編輯于2023年,星期日寫下面語句,執(zhí)行時(shí)總也不輸出:printf("%lds,%fm\n",scndsf(20.),20.);修改為如下語句:for(x=10.0;x<=20.0;x+=1.0)printf("%lds,%fm\n",scndsf(x),x);程序輸出:12367s,10.000000m33617s,11.000000m91328s,12.000000m248695s,13.000000m662167s,14.000000m1673859s,15.000000m而后又沒反應(yīng)了。第十三頁,共一百零九頁,編輯于2023年,星期日考慮如下的測(cè)試函數(shù):

voidtest_float(){longi;floatsum=0.0,sum0=-1.0;for(i=1;sum!=sum0;++i){sum0=sum;sum+=1/(float)i;}printf("float:%ldtermsat%f\n",i-1,sum);}程序很快就輸出了一行:float:2097152termsat15.403683對(duì)烏龜活動(dòng)的模擬受到數(shù)據(jù)表示精度和范圍的限制。第十四頁,共一百零九頁,編輯于2023年,星期日用double改寫程序,20億秒時(shí)和還增長(zhǎng),輸出:2000000000s,21.993629m經(jīng)過大約63年半,烏龜爬了近22米。在大約2億5千萬秒(約8年)爬過了20米,這是題目第二部分的答案。由于long為32位表示,在此范圍內(nèi)沒找到用double類型的增長(zhǎng)結(jié)束點(diǎn)。但一定比用float大得多。特殊情況中浮點(diǎn)誤差積累可能更迅速。兩個(gè)重要情況:1)將一批小的數(shù)加到很大的數(shù)上,會(huì)導(dǎo)致丟掉小的數(shù)的重要部分,甚至小數(shù)整個(gè)被丟掉(例中情況)2)兩個(gè)值接近的數(shù)相減,可能導(dǎo)致精度大大下降第十五頁,共一百零九頁,編輯于2023年,星期日例4:求x立方根的迭代公式:寫函數(shù)求立方根,要求:函數(shù)取名cbrt,特征:doublecbrt(doublex)無法確定循環(huán)次數(shù),只能用循環(huán)。只能按題目給出循環(huán)條件(計(jì)算方法的研究結(jié)果)求新迭代值要用到x。判斷終止需要先后兩個(gè)近似值,必須用兩個(gè)變量,這可看作是兩個(gè)合作的迭代變量。第十六頁,共一百零九頁,編輯于2023年,星期日函數(shù)的主要部分:doublex1,x2;if(x==0.0)return0.0;/*特殊情況*/x1=x;x2=(2.0*x1+x/(x1*x1))/3.0;while(fabs((x2-x1)/x1)>=1E-6){x1=x2;x2=(2.0*x1+x/(x1*x1))/3.0;}returnx2;這個(gè)程序的一個(gè)缺點(diǎn)是同一表達(dá)式寫了兩次。書上利用C的特點(diǎn)給了一種簡(jiǎn)化寫法,供參考學(xué)習(xí)了其他結(jié)構(gòu)后有改進(jìn)的寫法第十七頁,共一百零九頁,編輯于2023年,星期日例5:定義函數(shù),利用公式求近似值。設(shè)為doubledsin(doublex)。方法:循環(huán)累加,n

趨向無窮的過程中項(xiàng)值趨于0,而累加值趨向函數(shù)值。需要用循環(huán)。保存累加和的變量sum

,循環(huán)中求出的項(xiàng)值用t

保存。sum=0.0;對(duì)n為0計(jì)算t;while(需要繼續(xù)){sum=sum+t;計(jì)算下一個(gè)t;}循環(huán)結(jié)束條件:例如用項(xiàng)絕對(duì)值小于。第十八頁,共一百零九頁,編輯于2023年,星期日問題:t

的值如何計(jì)算?第一個(gè)t就是x;分析可以發(fā)現(xiàn)項(xiàng)值的遞推公式:doubledsin(doublex){doubles=0.0,t=x;intn=0;while(t>=1E-6||t<=-1E-6){s+=t;n=n+1;t=-t*x*x/(2*n)/(2*n+1);}returns;}第十九頁,共一百零九頁,編輯于2023年,星期日一些情況:實(shí)參值很小時(shí),循環(huán)將很快結(jié)束。實(shí)參絕對(duì)值很大時(shí)循環(huán)可能做許多次,項(xiàng)的絕對(duì)值可能達(dá)到很大,n

值也可能超出整數(shù)的表示范圍可考慮用fmod把參數(shù)歸約到[0,2]之內(nèi)啟示:理解問題非常重要。發(fā)現(xiàn)了項(xiàng)的遞推性質(zhì)能節(jié)省許多計(jì)算(否則就要再寫一個(gè)嵌套循環(huán)完成項(xiàng)的計(jì)算)級(jí)數(shù)收斂性質(zhì)能幫人認(rèn)識(shí)情況,改進(jìn)計(jì)算方法寫程序時(shí)必須仔細(xì)考慮問題本身的性質(zhì)第二十頁,共一百零九頁,編輯于2023年,星期日4.2循環(huán)中的問題從循環(huán)中退出有時(shí)需要從正在執(zhí)行的循環(huán)中退出。對(duì)6~200的各偶數(shù)驗(yàn)證哥德巴赫猜想,利用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+%d\n",n,m,n-m);問題:有多種分解時(shí)將產(chǎn)生多對(duì)輸出。如對(duì)10:

10=3+710=5+5前面寫isprime時(shí)借助return退出了循環(huán)第二十一頁,共一百零九頁,編輯于2023年,星期日希望每個(gè)偶數(shù)只輸出一行。怎樣在發(fā)現(xiàn)素?cái)?shù)對(duì)后停止?增加對(duì)循環(huán)的控制:把發(fā)現(xiàn)素?cái)?shù)分解作為條件加入。引入整型變量found,值0表示未發(fā)現(xiàn)素?cái)?shù)對(duì)。發(fā)現(xiàn)時(shí)將found賦1。內(nèi)循環(huán)開始時(shí)found置0。應(yīng)修改內(nèi)層循環(huán)條件。修改后循環(huán)是: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+%d\n",n,m,n-m);found=1;}這種需求很常見。C語言引進(jìn)了專門的控制語句第二十二頁,共一百零九頁,編輯于2023年,星期日break語句,形式:

break;break只能用在循環(huán)語句(及switch語句)里,使最內(nèi)層(循環(huán)可嵌套)循環(huán)語句(或switch)立即停止,執(zhí)行從被終止循環(huán)(或switch)之后繼續(xù)??捎胋reak解決循環(huán)中退出問題,放在條件下。完成前例的程序段:for(n=6;n<=200;n+=2)for(m=3;m<=n/2;m+=2)if(isPrime(m)&&isPrime(n-m)){printf("%d=%d+%d\n",n,m,n-m);break;}第二十三頁,共一百零九頁,編輯于2023年,星期日利用break重寫前面求立方根的函數(shù):doublecbrt(doublex){doublex1,x2=x;if(x==0.0)return0.0;while(1){x1=x2;x2=(2.0*x1+x/(x1*x1))/3.0;if(fabs((x2-x1)/x1)<1E-6)break;}returnx2;}也可以在break處直接寫returnx2。第二十四頁,共一百零九頁,編輯于2023年,星期日循環(huán)中的幾種變量循環(huán)中常出現(xiàn)幾類變量,注意這些有助于對(duì)循環(huán)的思考和分析。也是寫循環(huán)程序的經(jīng)驗(yàn)總結(jié)注意:分類不是絕對(duì)的,不同類別沒有截然界限1)循環(huán)控制變量(循環(huán)變量):循環(huán)前設(shè)初值,循環(huán)中遞增/遞減,達(dá)到/超過界限時(shí)循環(huán)結(jié)束。它們控制循環(huán)的進(jìn)行/結(jié)束。for中常有這類變量。for(n=0;n<10;++n)......for(n=30;n>=0;--n)......for(n=2;n<52;n+=4)......這種循環(huán)是固定次數(shù)的循環(huán)。這種循環(huán)可能展開第二十五頁,共一百零九頁,編輯于2023年,星期日2)累積變量:循環(huán)中常用+=或*=等更新。初值常用運(yùn)算的單位元(加用0;乘用1為初值)。循環(huán)結(jié)束時(shí)變量終值被作為循環(huán)計(jì)算結(jié)果。3)遞推變量:前兩類變量的推廣。幾個(gè)協(xié)同工作的變量,每次由幾個(gè)變量推出一個(gè)新值,其余依次更新。對(duì)變量x1、x2、x3,循環(huán)體可能有序列:

x1=x2; x2=x3; x3=...x1...x2...;例如上面cbrt里的變量x1和x2。第二十六頁,共一百零九頁,編輯于2023年,星期日寫循環(huán)時(shí)要考慮和解決問題列表:循環(huán)涉及到哪些變量,需引進(jìn)哪些臨時(shí)性變量?循環(huán)如何開始?循環(huán)開始前給變量什么初值?循環(huán)中變量的值如何改變?什么情況下繼續(xù)(或終止)循環(huán)?循環(huán)終止后如何得到所需結(jié)果?用哪種結(jié)構(gòu)實(shí)現(xiàn)循環(huán),等等。工作方式:分析問題,發(fā)掘線索,最終完成程序。程序設(shè)計(jì)不是教條,典型問題也無標(biāo)準(zhǔn)答案。并非最簡(jiǎn)單的問題總有多種解決方法,往往各有長(zhǎng)短?!罢_”程序常有優(yōu)劣之分。第二十七頁,共一百零九頁,編輯于2023年,星期日4.3循環(huán)與遞歸程序中有循環(huán)可能導(dǎo)致很長(zhǎng)的計(jì)算。沒有循環(huán)結(jié)構(gòu)也能描述這類計(jì)算。C語言允許遞歸,可在函數(shù)內(nèi)調(diào)用自身,程序常常更簡(jiǎn)單清晰。例:定義計(jì)算整數(shù)階乘的函數(shù):1×2×…×(n-1)×n乘的次數(shù)依賴于n,定義時(shí)不知道,每次用可能不同。程序的典型情況:計(jì)算“次數(shù)”依賴某些參數(shù)的值。省略號(hào)不科學(xué)。嚴(yán)格定義需用遞歸形式。第二十八頁,共一百零九頁,編輯于2023年,星期日注意遞歸定義的形式。這也提出了一種計(jì)算方法。如果語言允許遞歸定義函數(shù),就可以直接翻譯為程序。C允許遞歸定義:在函數(shù)定義內(nèi)調(diào)用被定義函數(shù)本身。類型特征可定為:

intfact(int)階乘值增長(zhǎng)極快(數(shù)學(xué)),更合適的類型特征:

longfact(long)第二十九頁,共一百零九頁,編輯于2023年,星期日遞歸的函數(shù)定義:longfact(longn){returnn==0?1:n*fact(n-1);}也可以用循環(huán)定義:longfact1(longn){longi,f=1;for(i=2;i<=n;++i)f*=i;returnf;}比遞歸定義長(zhǎng),需要引進(jìn)多個(gè)局部變量。第三十頁,共一百零九頁,編輯于2023年,星期日fact實(shí)現(xiàn)的計(jì)算過程很不簡(jiǎn)單。計(jì)算中fact被遞歸調(diào)用的次數(shù)由實(shí)參確定??紤]負(fù)參數(shù)值處理。可改為:n<=1?1:..第三十一頁,共一百零九頁,編輯于2023年,星期日遞歸定義導(dǎo)致的計(jì)算過程參數(shù)不同fact遞歸調(diào)用次數(shù)(步數(shù))不同。定義只有一個(gè)語句,可能要許多步才能完成。包含遞歸(和循環(huán))的程序產(chǎn)生的計(jì)算過程和性質(zhì)更復(fù)雜,能完成更復(fù)雜工作,理解和書寫也更困難。遞歸的函數(shù)定義需要條件表達(dá)式或if,必須區(qū)分:直接給出結(jié)果的情況。是遞歸的基礎(chǔ)需要遞歸處理的情況。其中把對(duì)較復(fù)雜情況的計(jì)算歸結(jié)為對(duì)更簡(jiǎn)單情況的計(jì)算基本運(yùn)算/關(guān)系判斷/條件表達(dá)式,加函數(shù)定義和遞歸定義構(gòu)成了一個(gè)(理論上)“足夠強(qiáng)的”的程序語言。第三十二頁,共一百零九頁,編輯于2023年,星期日程序?qū)嵗鼺ibonacci(斐波那契)序列的遞歸定義:Fibonacci序列增長(zhǎng)很快,返回值選long。遞歸定義:longfib(intn){returnn<2?1:fib(n-1)+fib(n-2);}負(fù)參數(shù)值定義為1。這是“合理”處置。問題分析:這個(gè)程序好不好?一方面,很好!程序與數(shù)學(xué)定義的關(guān)系很清晰,正確性容易確認(rèn),定義易讀易理解。第三十三頁,共一百零九頁,編輯于2023年,星期日但這個(gè)定義有一個(gè)本質(zhì)性缺點(diǎn)。示意圖:第三十四頁,共一百零九頁,編輯于2023年,星期日存在大量重復(fù)計(jì)算,參數(shù)越大重復(fù)計(jì)算越多。有關(guān)系嗎?隨著參數(shù)增大,計(jì)算中重復(fù)增長(zhǎng)迅速,最快的微機(jī)上一分鐘大約可以算出fib(45)參數(shù)加1,fib多用近一倍時(shí)間(指數(shù)增長(zhǎng))。最快的微機(jī)一小時(shí)算不出fib(55),算fib(100)要數(shù)萬年計(jì)算需時(shí)間,復(fù)雜計(jì)算需要很長(zhǎng)時(shí)間。這是計(jì)算機(jī)的本質(zhì)特征/弱點(diǎn)。說明它不萬能,有些事清“不能”做。求Fibonacci值有更好計(jì)算辦法(下面介紹)。第三十五頁,共一百零九頁,編輯于2023年,星期日人們發(fā)現(xiàn)了許多實(shí)際問題,理論上說可用計(jì)算機(jī)解決(可寫出計(jì)算它的程序),但對(duì)規(guī)模大的情況(“大的參數(shù)n”),人類永遠(yuǎn)等不到計(jì)算完成。這時(shí)能說問題解決了嗎?理解這個(gè)情況對(duì)于理解計(jì)算機(jī)是非常重要的。這里有一大類問題稱為計(jì)算中的“難解問題”,其中有許多很實(shí)際的問題(規(guī)劃、調(diào)度、優(yōu)化等)。這方面的理論和實(shí)際技術(shù)的研究極為重要:計(jì)算復(fù)雜性,難解問題,“P=NP?”問題。另外,對(duì)于許多問題的實(shí)用的有效算法,有極大的理論價(jià)值和實(shí)際價(jià)值。第三十六頁,共一百零九頁,編輯于2023年,星期日為計(jì)算過程計(jì)時(shí)統(tǒng)計(jì)程序/程序片段的計(jì)算時(shí)間有助于理解程序性質(zhì)。許多語言或系統(tǒng)都提供了內(nèi)部計(jì)時(shí)功能。有關(guān)函數(shù)在time.h,統(tǒng)計(jì)程序時(shí)間時(shí)程序頭部應(yīng)寫:

#include<time.h>在程序里計(jì)時(shí),通常寫表達(dá)式:

clock()/CLOCKS_PER_SEC得到從程序開始到表達(dá)式求值時(shí)所經(jīng)歷的秒數(shù)。注意:有些老的C系統(tǒng)(如Turbe-C)用CLK_TCK。第三十七頁,共一百零九頁,編輯于2023年,星期日確定計(jì)算fib(45)所需要的時(shí)間的程序:#include<stdio.h>#include<time.h>longfib(intn){returnn<=1?1:fib(n-1)+fib(n-2);}intmain(){/*自己做其他試驗(yàn)*/doublex;x=clock()/CLK_TCK;fib(45);x=clock()/CLK_TCK-x;printf("Timingfib(45):%f.\n",x);return0;}第三十八頁,共一百零九頁,編輯于2023年,星期日Fibonacci數(shù)的遞推計(jì)算

易見1)F1和F2是12)知道連續(xù)兩個(gè)Fibonacci數(shù),就可算出下一個(gè)遞推計(jì)算方式:逐個(gè)前推,可用循環(huán)實(shí)現(xiàn):longfib1(intn){longa=1,b=1,c,i;if(n<=1)return1;for(c=a+b,i=2;i<n;++i){a=b;b=c;c=a+b;}returnc;}/*對(duì)嗎?*/第三十九頁,共一百零九頁,編輯于2023年,星期日循環(huán)結(jié)束時(shí)i等于n,這時(shí)c的值是Fn。要得到此結(jié)論,可設(shè)法證明:每次判斷i的值時(shí)c正是Fi。上面循環(huán)保證這種關(guān)系,可以通過歸納證明:for(c=a+b,i=2;i<n;++i){a=b;b=c;c=a+b;}第一次判斷時(shí)i的值是2,c的值2,正是Fi(且a的值是Fi-1

,b的值是Fi-2

)若某次判斷時(shí)i值是k(小于n),循環(huán)體中的語句使a變成Fk-1

,b變成Fk

,c變成Fk+1

。i值增1使我們又有a為Fi-2

,b變成Fi-1

,c變成Fi

根據(jù)歸納法,每次判斷i的值時(shí)c正是Fi。第四十頁,共一百零九頁,編輯于2023年,星期日循環(huán)實(shí)現(xiàn)重復(fù)性計(jì)算,循環(huán)體可能執(zhí)行多次。如何保證對(duì)各種數(shù)據(jù)都能正確完成計(jì)算?循環(huán)中變量不斷變化。寫循環(huán)要考慮變量間的關(guān)系,保證某些關(guān)系在循環(huán)中不變:循環(huán)的不變關(guān)系。寫循環(huán)時(shí)最重要的就是想清循環(huán)中應(yīng)維持變量間的什么關(guān)系才能保證循環(huán)結(jié)束時(shí)變量能處在所需狀態(tài)。寫完循環(huán)后應(yīng)仔細(xì)檢查是否滿足要求。循環(huán)不變關(guān)系(循環(huán)不變量)是理解循環(huán)、寫好循環(huán)的關(guān)鍵。這個(gè)方面有很多研究,有許多理論結(jié)果。第四十一頁,共一百零九頁,編輯于2023年,星期日問題:用循環(huán)的函數(shù)比用遞歸定義的好嗎?新函數(shù)在計(jì)算時(shí)間上有極大優(yōu)越性。計(jì)算時(shí)間由循環(huán)次數(shù)確定。循環(huán)體執(zhí)行次數(shù)大致為n。fib(100)只需約100次循環(huán),幾乎察覺不到所花費(fèi)時(shí)間。新函數(shù)定義較復(fù)雜,有復(fù)雜的循環(huán)。要理解程序意義,確認(rèn)函數(shù)對(duì)任何參數(shù)都算出Fibonacci值,需要借助“循環(huán)不變關(guān)系”的概念和細(xì)致分析。上面分析中沒考慮數(shù)據(jù)表示范圍,long類型一般無法表示fib(100)。注意:這個(gè)例子并不是說明遞歸比循環(huán)的效率低。完全可以寫出計(jì)算fib的同樣高效的遞歸定義的函數(shù)第四十二頁,共一百零九頁,編輯于2023年,星期日求最大公約數(shù)(greatestcommondivisor,GCD):寫函數(shù)longgcd(long,long)方式1:k取初值1后遞增,大于m或n之一時(shí)結(jié)束。如何得到所需結(jié)果?m和n可能有多個(gè)公約數(shù),最后的k值不是m和n的公約數(shù)(它已大于兩數(shù)之一)。解法1:逐個(gè)檢查,直到找到能同時(shí)整除m和n的最大整數(shù)(生成與檢查)。需輔助變量k記錄檢查值。簡(jiǎn)單方式:k順序取值(初值/更新/結(jié)束),可用循環(huán)實(shí)現(xiàn)。需要記錄循環(huán)中找到的公約數(shù)。第四十三頁,共一百零九頁,編輯于2023年,星期日只需記錄已找到最大的公約數(shù),用變量d,初值1(是公約數(shù)),遇到新公約數(shù)(一定更大)時(shí)記入d:if(m%k==0&&n%k==0)d=k;/*k為新找到的公約數(shù)*/有了d及其初值,k可以從2開始循環(huán)。函數(shù)定義:longgcd(longm,longn){longd=1,k=2;for(;k<=m&&k<=n;k++)if(m%k==0&&n%k==0)d=k;returnd;}參數(shù)互素時(shí)初值1會(huì)留下來,也正確。第四十四頁,共一百零九頁,編輯于2023年,星期日還有一些特殊情況需要處理:1)m和n都為0需特殊處理。例如令函數(shù)返回值0;2)若m和n中一個(gè)為0,gcd是另一個(gè)數(shù)。函數(shù)的返回值正確。也可直接判斷處理;3)m、n為負(fù)時(shí)函數(shù)返回1,可能不對(duì)。應(yīng)在循環(huán)前加語句:

if(m==0&&n==0)return0; if(m<0)m=-m; if(n<0)n=-n; if(m==0)returnn; if(n==0)returnm;第四十五頁,共一百零九頁,編輯于2023年,星期日可能方式2:令k從某大數(shù)開始遞減,找到的第一個(gè)公約數(shù)就是最大公約數(shù)。k初值可取m和n中小的一個(gè)。結(jié)束條件:k值達(dá)到1或找到了公約數(shù)。1總是公約數(shù)。程序主要部分可寫為:for(k=(m>n?n:m); m%k!=0||n%k!=0;k--) ;/*空循環(huán)體*/returnk;/*循環(huán)結(jié)束時(shí)k是最大公約數(shù)*/本方法比前一方法簡(jiǎn)單一些。兩種方法的共同點(diǎn)是重復(fù)測(cè)試。這類方法的缺點(diǎn)是效率較低,參數(shù)大時(shí)循環(huán)次數(shù)很多。第四十六頁,共一百零九頁,編輯于2023年,星期日解法2:求GCD有著名的歐幾里德算法(歐氏算法,輾轉(zhuǎn)相除法)。最大公約數(shù)的遞歸定義:函數(shù)定義(遞歸):假設(shè)第二個(gè)參數(shù)非0,且參數(shù)都不小于0。與數(shù)學(xué)定義直接對(duì)應(yīng):longgcd1(longm,longn){returnm%n==0?n:gcd1(n,m%n);}對(duì)歐氏算法的研究保證了本函數(shù)能結(jié)束,對(duì)較大的數(shù)計(jì)算速度也很快,遠(yuǎn)遠(yuǎn)優(yōu)于順序檢查。第四十七頁,共一百零九頁,編輯于2023年,星期日對(duì)特殊情況可另寫一函數(shù),其主體是對(duì)gcd1的調(diào)用:longgcd(longm,longn){if(m<0)m=-m;if(n<0)n=-n;returnn==0?m:gcd1(m,n);}第四十八頁,共一百零九頁,編輯于2023年,星期日函數(shù)定義(循環(huán)):輾轉(zhuǎn)相除就是反復(fù)求余數(shù),也是重復(fù)性工作,可用循環(huán)結(jié)構(gòu)實(shí)現(xiàn)。出發(fā)點(diǎn)m和n;循環(huán)判斷m%n是否為0,若是則n為結(jié)果;否則更新變量:令m取n的原值,n取m%n的原值。為正確更新需用輔助變量r,正確的更新序列:

r=m%n;m=n;n=r;循環(huán)可寫為:for(r=m%n;r!=0;r=m%n){ m=n;n=r;}第四十九頁,共一百零九頁,編輯于2023年,星期日下面函數(shù)定義假定參數(shù)值不小于0(否則可以在前面增加判斷和處理):longgcd2(longm,longn){longr;if(n==0)returnm;for(r=m%n;r!=0;r=m%n){m=n;n=r;}returnn;}參數(shù)是局部變量,可在函數(shù)體里使用和修改。請(qǐng)考慮,這里的循環(huán)不變式是什么?第五十頁,共一百零九頁,編輯于2023年,星期日河內(nèi)塔(hanoi塔,梵塔)問題

本問題用遞歸寫程序很簡(jiǎn)單,用循環(huán)解決較困難。問題出自古印度(一說西藏)。某神廟有三根細(xì)柱,64個(gè)大小不等、中心有孔的金盤套在柱上,構(gòu)成梵塔。僧侶日夜不息地將圓盤從一柱移到另一柱,規(guī)則是每次只移一個(gè)盤,大盤不能放到小盤上。開始時(shí)圓盤從大到小套在一根柱上,據(jù)說所有圓盤都搬到另一根柱時(shí)世界就要?dú)?。第五十一頁,共一百零九頁,編輯?023年,星期日要求寫程序模擬搬圓盤過程,打印出搬動(dòng)指令序列。為方便,分別將三根圓柱命名為a、b和c,假定開始時(shí)所有圓盤都在a上,要求最終搬到b。初看問題似乎沒規(guī)律。求解的關(guān)鍵在于看到問題的“遞歸性質(zhì)”。搬64個(gè)盤的問題可歸結(jié)為兩次搬63個(gè)盤。一般情況:搬n個(gè)圓盤的問題可以歸結(jié)為搬n-1個(gè)圓盤把n個(gè)盤從柱1搬到柱2的工作可以如下完成:從柱1借助柱2將n-1個(gè)圓盤搬到柱3;將最大圓盤從柱1搬到柱2;從柱3借助柱1將n-1個(gè)圓盤搬到柱2;第五十二頁,共一百零九頁,編輯于2023年,星期日voidmoveone(charfrom,charto){printf("%c->%c\n",from,to);}

voidhenoi(intn,charfrom,charto,charby){if(n==1)moveone(from,to);else{henoi(n-1,from,by,to);moveone(from,to);henoi(n-1,by,to,from);}}moveone定義為函數(shù)是為了方便。函數(shù)調(diào)用:henoi(6,'a','b','c');第五十三頁,共一百零九頁,編輯于2023年,星期日4.4基本輸入輸出(IO)IO通過標(biāo)準(zhǔn)庫(kù)進(jìn)行常用函數(shù)scanfgetcharputchar需要<stdio.h>(同printf)第五十四頁,共一百零九頁,編輯于2023年,星期日格式輸入函數(shù)scanf功能與printf對(duì)應(yīng)。scanf從標(biāo)準(zhǔn)輸入讀數(shù)據(jù),根據(jù)格式描述將實(shí)際輸入轉(zhuǎn)換到指定類型,轉(zhuǎn)換結(jié)果賦給指定變量:

scanf(格式描述串,&變量名,...)格式描述串與printf的類似,其中的轉(zhuǎn)換描述(以%開頭)說明輸入形式和轉(zhuǎn)換方式。其他參數(shù)(個(gè)數(shù)應(yīng)與格式串中轉(zhuǎn)換描述一致)指明接受輸入的程序變量。形式是在變量名前面加&符號(hào)。注意:必須寫&符號(hào),不寫將引起嚴(yán)重問題。第五十五頁,共一百零九頁,編輯于2023年,星期日簡(jiǎn)單示例:#include<stdio.h>intmain(){inti,n;printf("Pleaseinputanumber:");scanf("%d",&n);printf("%d%d\n",n,n*n);return0;}程序執(zhí)行后輸出提示串:Pleaseinputanumber:等待人的輸入。得到輸入數(shù)據(jù)后輸出并結(jié)束。程序的行為依賴于當(dāng)時(shí)的輸入(與前面程序不同)第五十六頁,共一百零九頁,編輯于2023年,星期日注意實(shí)數(shù)類型的轉(zhuǎn)換描述與printf的差異。例:設(shè)有變量定義:

intn;doublex;floaty;可以寫語句:

scanf("%d%lf%f",&n,&x,&y);常用的scanf轉(zhuǎn)換描述:第五十七頁,共一百零九頁,編輯于2023年,星期日讀數(shù)值時(shí),sacnf格式串里的轉(zhuǎn)換描述之間的空格并不必要。上面語句寫成下面形式,效果一樣。:scanf("%d%lf%f",&n,&x,&y);如果這里的轉(zhuǎn)換描述之間沒字符或只有空格,輸入的數(shù)據(jù)之間也只能有空白字符,不能有其他字符。格式串里一般不寫轉(zhuǎn)換描述之外的東西。如果寫"%d,%lf,%f"就是要求用逗號(hào)分隔輸入數(shù)據(jù),若輸入時(shí)不注意就會(huì)導(dǎo)致數(shù)據(jù)不能正常讀入。建議不要這樣寫。scanf格式串的細(xì)節(jié)在第八章有詳細(xì)介紹。第五十八頁,共一百零九頁,編輯于2023年,星期日緩沖式輸入若程序要求從標(biāo)準(zhǔn)輸入取得信息(如執(zhí)行scanf),我們由鍵盤輸入,在按Enter鍵后程序才能得到輸入數(shù)據(jù)造成這種情況的原因是操作系統(tǒng)通常采用“緩沖式”輸入方式,把來自鍵盤的輸入臨時(shí)保存在“輸入緩沖區(qū)”(操作系統(tǒng)管理下的一塊內(nèi)存區(qū)域)里,直至人按了Enter鍵,才把緩沖區(qū)里的數(shù)據(jù)送給程序,這時(shí)scanf等輸入函數(shù)才能讀到數(shù)據(jù)程序經(jīng)常需要輸入一批數(shù)據(jù),通過一個(gè)循環(huán)處理。為此需要在循環(huán)中反復(fù)調(diào)用輸入函數(shù)下面討論這種循環(huán)輸入中的控制問題第五十九頁,共一百零九頁,編輯于2023年,星期日通過計(jì)數(shù)器控制的輸入循環(huán)如果事先知道需要輸入的數(shù)據(jù)項(xiàng)數(shù),就可以用計(jì)數(shù)器控制輸入循環(huán)。如由各月降雨量統(tǒng)計(jì)一年總量:#include<stdio.h>

intmain(){doublex,sum;intn;for(sum=0,n=0;n<12;++n){printf("Enternextdata:");scanf("%lf",&x);sum+=x;}printf("AnnualPrecipitation:%f\n",sum);return0;}第六十頁,共一百零九頁,編輯于2023年,星期日假定寫程序時(shí)不清楚需要輸入的數(shù)據(jù)的確切項(xiàng)數(shù),就無法采用計(jì)數(shù)循環(huán)的簡(jiǎn)單方法。一種方式是用一個(gè)特殊“結(jié)束標(biāo)志”控制循環(huán)。該“結(jié)束標(biāo)志”應(yīng)是一個(gè)特殊輸入值,具有與輸入數(shù)據(jù)同樣的類型,但又不是正常輸入數(shù)據(jù)。讓程序在循環(huán)中不斷檢測(cè)得到的數(shù)據(jù),一旦看到這個(gè)特殊數(shù)據(jù),就知道用戶要求結(jié)束了。采用這種技術(shù),循環(huán)結(jié)束條件就是寫程序的人與使用者之間的一種約定,當(dāng)輸入滿足約定時(shí)程序就結(jié)束。用特殊結(jié)束值控制輸入循環(huán)第六十一頁,共一百零九頁,編輯于2023年,星期日例,計(jì)算貨物總值,每次輸入單價(jià)和數(shù)量。可考慮用特殊值通知程序數(shù)據(jù)已輸入完,例如用單價(jià)為0。#include<stdio.h>intmain(){doubleprice=1.0,amount,sum=0.0;while(price!=0){printf("Nextdata(priceamount):");scanf("%lf%lf",&price,&amount);sum+=price*amount;}printf("Totalprice:%f\n",sum);return0;}這個(gè)程序中循環(huán)體的執(zhí)行次數(shù),完全由程序執(zhí)行時(shí)外部提供的輸入數(shù)據(jù)項(xiàng)數(shù)決定。第六十二頁,共一百零九頁,編輯于2023年,星期日上面兩種方式可以解決許多數(shù)據(jù)輸入循環(huán)的控制問題,但有時(shí)也會(huì)遇到困難。例:假定現(xiàn)在要寫程序,求一批輸入數(shù)據(jù)的平均值。事先不知道可能輸入哪些數(shù)據(jù)。任何數(shù)值都可能出現(xiàn)在需要求平均值的數(shù)據(jù)中。如果選0作為“結(jié)束標(biāo)志”,而實(shí)際數(shù)據(jù)里有0,這個(gè)程序就不能正確處理了(任何選擇都有問題)。這種情況具有普遍性,要解決這類問題,就需要進(jìn)一步理解scanf的功能。第六十三頁,共一百零九頁,編輯于2023年,星期日深入理解scanfscanf的返回值是int,它順序處理格式串:根據(jù)格式串要求完成輸入、轉(zhuǎn)換和對(duì)變量的賦值工作正常結(jié)束時(shí)返回所完成的數(shù)據(jù)轉(zhuǎn)換項(xiàng)數(shù)如果一開始就遇到文件結(jié)束,就返回一個(gè)特殊符號(hào)常量EOF(是一個(gè)int值,后面再介紹)如果沒處理完整個(gè)格式串就失敗時(shí),返回已完成的數(shù)據(jù)轉(zhuǎn)換項(xiàng)數(shù)scanf用輸入數(shù)據(jù)與正在處理的轉(zhuǎn)換描述比較,如果相符就完成一項(xiàng)轉(zhuǎn)換。例如:若轉(zhuǎn)換描述是%d,輸入得到的是一串?dāng)?shù)字,就把它們轉(zhuǎn)換為一個(gè)整數(shù)如果實(shí)際輸入與轉(zhuǎn)換描述不匹配,轉(zhuǎn)換失敗第六十四頁,共一百零九頁,編輯于2023年,星期日scanf要求三方面一致:格式串中轉(zhuǎn)換描述、對(duì)應(yīng)參數(shù)的類型、運(yùn)行中提供的數(shù)據(jù)形式。假如格式串要求做整數(shù)轉(zhuǎn)換,賦給整型變量。若實(shí)際輸入不是一串?dāng)?shù)字,scanf也無法正常完成工作在格式串要求讀整數(shù)或者浮點(diǎn)數(shù),scanf會(huì)跳過遇到的空白字符,從下一非空白字符開始處理下面函數(shù)調(diào)用可能產(chǎn)生三種返回值:

scanf("%lf",&x)返回1表示成功讀入一項(xiàng)數(shù)據(jù),并存入了x返回0表示讀入數(shù)據(jù)失敗返回EOF值表示遇到文件結(jié)束應(yīng)該通過這種性質(zhì)控制循環(huán)第六十五頁,共一百零九頁,編輯于2023年,星期日例:讀入一些圓盤半徑,算出各圓盤的面積并輸出。不知圓盤數(shù),可利用scanf的返回值控制循環(huán)結(jié)束#include<stdio.h>voidpc_area(doubler){/*定義略*/}intmain(){doublex;while(scanf("%lf",&x)==1)if(x<0)printf("Inputerror:%f\n",x);elsepc_area(x);return0;}/*什么情況下循環(huán)結(jié)束?*/只要scanf的返回值不是1,循環(huán)就結(jié)束第六十六頁,共一百零九頁,編輯于2023年,星期日遇到文件結(jié)束或錯(cuò)誤數(shù)據(jù)時(shí)scanf不返回1。如果上面程序遇到輸入字母m,轉(zhuǎn)換失敗就會(huì)導(dǎo)致循環(huán)結(jié)束。更好的方式是利用標(biāo)準(zhǔn)庫(kù)定義的符號(hào)常量EOF。如果把標(biāo)準(zhǔn)輸入定向到某個(gè)文件,在讀完文件里所有數(shù)據(jù)后scanf就會(huì)返回EOF值。EOF是什么?一般的C系統(tǒng)把EOF定義為-1,它一定不是正數(shù),不會(huì)與scanf的其他返回值混淆。默認(rèn)情況下,標(biāo)準(zhǔn)輸入從鍵盤得到數(shù)據(jù)。許多系統(tǒng)里可以用Ctrl-Z或Ctrl-D組合鍵送入文件結(jié)束信息。前面程序運(yùn)行時(shí),如果按了這種組合鍵,scanf就會(huì)返回EOF并導(dǎo)致循環(huán)結(jié)束。第六十七頁,共一百零九頁,編輯于2023年,星期日例:統(tǒng)計(jì)一批輸入數(shù)據(jù)的個(gè)數(shù)和最小值/最大值/平均值循環(huán)讀入數(shù)據(jù),并完成其他工作。兩個(gè)變量記錄已知的最小/最大值。讀數(shù)據(jù)中考慮更新,使其保存已讀數(shù)據(jù)的最小最大值(循環(huán)不變性質(zhì))。兩個(gè)變量記錄數(shù)據(jù)個(gè)數(shù),記錄已讀入數(shù)據(jù)之和。循環(huán)中要正確更新(循環(huán)不變性質(zhì))。問題:保存最大值和最小值的變量的初始值?下面程序假定最少有一個(gè)輸入數(shù)據(jù),用讀入的第一個(gè)數(shù)據(jù)作為最大和最小變量的初始值。第六十八頁,共一百零九頁,編輯于2023年,星期日#include<stdio.h>intmain(){doublesum=0.0,biggest,smallest,x;intcount=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;}…/*輸出結(jié)果,略*/return0;} /*要求至少有一個(gè)輸入數(shù)據(jù)*/第六十九頁,共一百零九頁,編輯于2023年,星期日關(guān)于輸入循環(huán)的總結(jié)要輸入一批數(shù)據(jù)時(shí),可根據(jù)情況采用不同控制方式循環(huán)。主要的三種控制方式:1.程序內(nèi)部自主控制,根據(jù)程序內(nèi)部情況決定循環(huán)繼續(xù)或終止,是否繼續(xù)讀入。這一技術(shù)的缺點(diǎn)是不夠靈活,難以處理事先不清楚項(xiàng)數(shù)的輸入數(shù)據(jù)。2.從輸入數(shù)據(jù)類型里選一個(gè)特殊值作為結(jié)束標(biāo)志值,程序使用者可用它通知程序輸入結(jié)束。這一技術(shù)的缺點(diǎn)是有時(shí)難以找到合適的結(jié)束標(biāo)志值。3.通過輸入函數(shù)的返回值,控制循環(huán)的繼續(xù)或結(jié)束。以后的程序?qū)嵗羞€會(huì)頻繁使用它們。第七十頁,共一百零九頁,編輯于2023年,星期日字符IO函數(shù)getchar和putchargetchar是無參函數(shù),從標(biāo)準(zhǔn)輸入讀一個(gè)字符,返回字符的編碼值。getchar的類型特征:

intgetchar(void)典型使用(輸入的字符賦給變量c):

c=getchar();標(biāo)準(zhǔn)輸入默認(rèn)連到鍵盤。沒有輸入數(shù)據(jù)時(shí)getchar等待,直到人輸入字符(并換行)。返回類型int的問題下面解釋。第七十一頁,共一百零九頁,編輯于2023年,星期日putchar把一字符送到標(biāo)準(zhǔn)輸出:

putchar('O');putchar('K');兩字符送到標(biāo)準(zhǔn)輸出,使字符顯示在屏幕上。例:寫程序把由輸入的一個(gè)字符輸出并換行:#include<stdio.h>intmain(){intc;c=getchar();putchar(c);putchar('\n');return0;} /*執(zhí)行情況?*/第七十二頁,共一百零九頁,編輯于2023年,星期日輸入一系列字符假設(shè)要由標(biāo)準(zhǔn)輸入得到的多個(gè)字符送到標(biāo)準(zhǔn)輸出,需要反復(fù)讀入/輸出字符,應(yīng)該寫循環(huán):

while(....){ c=getchar(); putchar(c); } 本程序具有普遍性:putchar(c)是處理過程的代表,可根據(jù)需要換成其他程序片段。怎樣描述循環(huán)條件?首先要問的是:希望在什么條件下結(jié)束循環(huán)?第七十三頁,共一百零九頁,編輯于2023年,星期日兩種可能:1)程序內(nèi)部確定,與實(shí)際輸入無關(guān)。例如用計(jì)數(shù)器,讀入若干個(gè)字符后結(jié)束。#include<stdio.h>intmain(){/*讀10個(gè)字符,輸出各個(gè)字符的編碼*/intc,n;for(n=0;n<10;++n){c=getchar();printf("%d\n",c);}return0;}第七十四頁,共一百零九頁,編輯于2023年,星期日2)根據(jù)實(shí)際輸入決定。循環(huán)條件與輸入有關(guān),是編程者和使用者的協(xié)議,得到滿足條件的輸入時(shí)結(jié)束循環(huán)。例:輸入讀一行,輸出各字符的編碼:#include<stdio.h>intmain(){intc;while(1){/*循環(huán)執(zhí)行多少次由輸入行包含多少字符確定*/c=getchar();if(c=='\n')break;printf("%d",c);}return0;}也可要求遇到其他字符結(jié)束。何時(shí)結(jié)束是一種約定。第七十五頁,共一百零九頁,編輯于2023年,星期日處理任意的輸入字符前面方法需要選一個(gè)字符作為表示結(jié)束的特殊字符。這個(gè)字符就不能再作為輸入中的正常字符了。要處理鍵盤能輸入的所有字符,應(yīng)怎樣寫結(jié)束條件?標(biāo)準(zhǔn)庫(kù)定義了符號(hào)常量EOF(EndOfFile/文件結(jié)束),getchar遇文件結(jié)束返回EOF。如果標(biāo)準(zhǔn)輸入定向到文件,getchar就會(huì)從文件讀,文件讀完時(shí)返回值EOF。由鍵盤輸入文件結(jié)束:用Ctrl-Z送文件結(jié)束信息。EOF是什么?一般系統(tǒng)定義為-1(具體值并不重要)。程序里只需判斷輸入函數(shù)的返回值是否與EOF值相同。第七十六頁,共一百零九頁,編輯于2023年,星期日while((c=getchar())!=EOF){....../*對(duì)輸入的實(shí)際處理*/}EOF的值不能與任何字符編碼相同。若getchar返回char,就無法給出EOF值。所以getchar返回int??偨Y(jié):正常情況下getchar返回讀入的字符,遇文件結(jié)束返回EOF值。應(yīng)該用int變量接收getchar的返回值,以保證正確判斷輸入結(jié)束。如果用char變量,值超出char范圍時(shí)結(jié)果無定義charch;while((ch=getchar())!=EOF)...注意:賦值操作有值,注意加括號(hào)。第七十七頁,共一百零九頁,編輯于2023年,星期日例:統(tǒng)計(jì)(由標(biāo)準(zhǔn)輸入得到的)文件中的字符個(gè)數(shù)。#include<stdio.h>intmain(){intc;longn=0;while((c=getchar())!=EOF)n++;printf("%ld\n",n);return0;}標(biāo)準(zhǔn)輸入默認(rèn)連接到鍵盤。程序執(zhí)行到getchar等待輸入,得到輸入后處理。用Ctrl-Z發(fā)信息可使循環(huán)結(jié)束。緩沖式輸入:鍵盤輸入字符(行),按Enter鍵后才能送給程序。因?yàn)椴僮飨到y(tǒng)通常采用緩沖式輸入方式。第七十八頁,共一百零九頁,編輯于2023年,星期日從系統(tǒng)中的命名文件讀入:設(shè)源程序是count.c,編譯結(jié)果是count.exe。用命令行方式啟動(dòng)程序,將標(biāo)準(zhǔn)輸入定向到文件(設(shè)被統(tǒng)計(jì)文件是abcd.txt):count<<abcd.txt讀入循環(huán)中可以完成對(duì)輸入內(nèi)容的各種處理,例如:統(tǒng)計(jì)某個(gè)字符出現(xiàn)的次數(shù),統(tǒng)計(jì)文件中的行數(shù)等等練習(xí)中包含許多這種題目第七十九頁,共一百零九頁,編輯于2023年,星期日輸入函數(shù)的返回值程序可分為兩類:一類自主運(yùn)行,產(chǎn)生結(jié)果后結(jié)束;另一類不斷與外界交流(交互式程序)。交互式程序時(shí)應(yīng)設(shè)法處理各種可能情況,特別需要獲得有關(guān)輸入情況的信息,以便恰當(dāng)處理。標(biāo)準(zhǔn)輸入函數(shù)用返回值說明執(zhí)行情況。如scanf返回int值,指明完成轉(zhuǎn)換項(xiàng)數(shù),遇文件結(jié)束返回EOF。若程序要求輸入三個(gè)整數(shù),一種寫法:while(scanf("%d%d%d",&a,&b,&c)!=3){printf("Formaterror.3intsagain.\n");while(getchar()!='\n');/*吃掉一行字符*/} 第八十頁,共一百零九頁,編輯于2023年,星期日標(biāo)準(zhǔn)輸入可看作很長(zhǎng)的字符序列。輸入函數(shù)從這個(gè)序列最前面的取字符。getchar取走一個(gè)字符。scanf可能取走幾個(gè)字符(如果符合要求);無法轉(zhuǎn)換時(shí)一個(gè)也不取。沒取走的字符仍在那里。scanf工作到處理完所有轉(zhuǎn)換描述或轉(zhuǎn)換失敗,返回正確完成的轉(zhuǎn)換項(xiàng)數(shù)。例,若把上面程序中循環(huán)的條件部分改為:while(scanf("%lf",&x)!=EOF)...如果執(zhí)行時(shí)輸入字母m,程序進(jìn)入無窮循環(huán)。為什么?考慮函數(shù)scanf的返回值。第八十一頁,共一百零九頁,編輯于2023年,星期日標(biāo)準(zhǔn)輸入與文件OS允許標(biāo)準(zhǔn)輸入重新定向。將標(biāo)準(zhǔn)輸入定向到文件可使文件成為getchar或scanf的輸入源。程序里不必區(qū)分實(shí)際輸入來自鍵盤還是實(shí)際文件。處理連續(xù)輸入時(shí),這兩者沒有本質(zhì)差別。在后面的討論和例子里,將直接說程序從文件讀入等,而實(shí)際寫的是從標(biāo)準(zhǔn)輸入(標(biāo)準(zhǔn)輸入文件)讀入。對(duì)于標(biāo)準(zhǔn)輸出也類似。有時(shí)需要由特定的命名文件輸入,或者向特定命名文件輸出。第八章討論文件處理。第八十二頁,共一百零九頁,編輯于2023年,星期日4.5其他控制結(jié)構(gòu)和控制語句還有兩種控制結(jié)構(gòu)和幾種控制語句。理論上說不必要,提供是為了寫程序更方便。do-while結(jié)構(gòu)語法形式:do語句

while(條件);執(zhí)行過程如圖。與while的差異在于判斷在后,至少執(zhí)行語句一次。第八十三頁,共一百零九頁,編輯于2023年,星期日do-while使用較少,沒有for和while多。用do-while結(jié)構(gòu)重寫求立方根函數(shù):doublecbrt(doublex){doublex1,x2=x;if(x==0.0)return0.0;/*處理特殊情況*/do{x1=x2;x2=(2.0*x1+x/(x1*x1))/3.0;}while(fabs((x2-x1)/x1)>=1E-6);returnx2;}第八十四頁,共一百零九頁,編輯于2023年,星期日break語句已介紹。continue語句形式:

continue;只能用在循環(huán)里,使最內(nèi)層循環(huán)體的一次執(zhí)行結(jié)束,進(jìn)入下次循環(huán)。while/do-while的隨后動(dòng)作是條件判斷;for的隨后動(dòng)作是變量更新。第八十五頁,共一百零九頁,編輯于2023年,星期日goto語句/轉(zhuǎn)移語句/轉(zhuǎn)跳語句最老的控制語句?,F(xiàn)在已很少用。許多語言仍提供。goto語句與標(biāo)號(hào)配合,實(shí)現(xiàn)函數(shù)體內(nèi)的任意控制轉(zhuǎn)移。標(biāo)號(hào)可寫在任何語句前面作為goto的目標(biāo)。形式是:

標(biāo)號(hào)名:

標(biāo)號(hào)名是標(biāo)識(shí)符。goto語句的形式:

goto標(biāo)號(hào)名;作用(語義):使控制轉(zhuǎn)到標(biāo)號(hào)處繼續(xù)執(zhí)行。break、continue是受限的goto,實(shí)現(xiàn)固定方式的控制轉(zhuǎn)移。循環(huán)和分支也是goto的包裝。FORTRAN就有g(shù)oto語句。第八十六頁,共一百零九頁,編輯于2023年,星期日無節(jié)制地用goto寫程序,費(fèi)解,常帶有難發(fā)現(xiàn)的錯(cuò)誤。1968年Dijkstra撰文“goto是有害的”。六年大辯論的結(jié)果是結(jié)構(gòu)程序設(shè)計(jì)革命,語言都引進(jìn)“標(biāo)準(zhǔn)”控制結(jié)構(gòu),教育和實(shí)踐中提倡結(jié)構(gòu)化程序設(shè)計(jì)。對(duì)goto的認(rèn)識(shí):不用或盡量少用。大部分goto實(shí)際上是為構(gòu)造條件或循環(huán):1)向前轉(zhuǎn)跳 2)向后轉(zhuǎn)跳 label:.... ...gotolabel;.... .......gotolabel; label:....用循環(huán)或條件重寫的程序更清晰易讀,不容易有錯(cuò)。隨便使用goto是不良編程習(xí)慣。不合理的goto表明對(duì)問題欠分析,沒做好流程分解,函數(shù)抽象等,寫的是不成熟的程序。第八十七頁,共一百零九頁,編輯于2023年,星期日開關(guān)語句(switch語句)多分支結(jié)構(gòu),根據(jù)一個(gè)整型值選擇。形式:switch(整型表達(dá)式){case整型常量表達(dá)式:語句序列

........default:語句序列} 常量表達(dá)式常用整數(shù)/字符等。default部分可缺,語句序列可缺,可含多個(gè)語句。“case整型常量表達(dá)式:”看作標(biāo)號(hào)。語義:求值整型表達(dá)式,將值順序與各整型常量表達(dá)式比較,遇到相等時(shí)轉(zhuǎn)入執(zhí)行;無匹配但有default則從default:處繼續(xù);沒有default時(shí)結(jié)束。第八十八頁,共一百零九頁,編輯于2023年,星期日例,按x的值確定分支,1和2分別處理,其他統(tǒng)一處理

switch(x){ case1:......break; case2:......break; default:......break; }各case標(biāo)號(hào)值必須互不相同。習(xí)慣在各分支最后寫break,包括最后分支。規(guī)定:如果分支最后無break,語句序列執(zhí)行完后進(jìn)入下一分支的語句序列。這導(dǎo)致一種代碼共享。一般認(rèn)為,除非幾個(gè)分支的語句相同,否則不提倡共享。因?yàn)檫@導(dǎo)致程序段相互依賴,對(duì)程序修改不利。第八十九頁,共一百零九頁,編輯于2023年,星期日while((c=getchar())!=EOF)switch(c){case'':nb++;break;case'1':case'2':case'3':case'4':case'5':case'6':case'7':case'8':case'9':case'0':nd++;break;case'\n':nl++;break;case'{':case'}':nc++;break;default:nn++;break;}例:分別對(duì)空格/數(shù)字/行數(shù)/花括號(hào)/其他字符計(jì)數(shù)。第九十頁,共一百零九頁,編輯于2023年,星期日4.6程序設(shè)計(jì)實(shí)例例:簡(jiǎn)單交互式計(jì)算器。假定它可以輸入并計(jì)算:

128+365 254+1438

10313+524輸入一行算一個(gè)結(jié)果。直至用戶要求結(jié)束。顯然程序里應(yīng)有一個(gè)基本循環(huán):

while(還有輸入){

取得數(shù)據(jù)

計(jì)算并輸出

}可考慮用scanf讀數(shù)據(jù),用文件結(jié)束或非數(shù)字表示輸入結(jié)束。第九十一頁,共一百零九頁,編輯于2023年,星期日#include<stdio.h>intmain(){intleft,right;printf("Smallcalculator.\n");printf("Anyno-digitcharactertostop.\n");while(scanf("%d",&left)==1){if(getchar()!='+'||scanf("%d",&right)!=1){printf("Fmterror.Enter:nnn+mmm\n");while(getchar()!='\n')//丟掉本行剩余字符

;continue;}printf("%d+%d=%d\n",left,right,left+right);}return0;}第九十二頁,共一百零九頁,編輯于2023年,星期日“常量”是標(biāo)識(shí)符形式,在程序里代表同一常數(shù)的東西。用enum定義(枚舉)可方便地定義一組符號(hào)常量:enum{NUM=10,LEN=20};

定義枚舉常量例:#include<stdio.h>enum{START=0,END=300,STEP=20};intmain(void){intc;for(c=START;c<=END;c+=STEP)printf("C=%d,F=%f\n",c,c*5.0/9.0+32.0);return0;}/*這樣的程序更容易修改*/第九十三頁,共一百零九頁,編輯于2023年,星期日符號(hào)形式表示能幫人理解程序意義。程序里兩個(gè)0可能代表不同意義,數(shù)值形式?jīng)]有任何區(qū)分。采用符號(hào)常量可提高可讀性。將所需常數(shù)定義為符號(hào)常量,在程序中統(tǒng)一使用是很好的方法。使程序更容易修改(修改時(shí)不必瀏覽整個(gè)程序)。對(duì)大程序的作用更明顯。第五章介紹其他常量定義方式。enum的詳細(xì)討論在第九章,目前作為一種定義符號(hào)整型常量的機(jī)制。

第九十四頁,共一百零九頁,編輯于2023年,星期日單詞計(jì)數(shù)問題正文文件可看成字符序列,空白字符(空格''、制表符'\t'、換行符'\n')把序列分隔為一個(gè)個(gè)“單詞”。要求寫程序統(tǒng)計(jì)文件中的單詞個(gè)數(shù)。需要一個(gè)計(jì)數(shù)器,遇到一個(gè)詞將計(jì)數(shù)器加一??紤]用函數(shù)getchar讀字符。程序主要部分的框架:

while(文件未結(jié)束)

遇到一個(gè)詞時(shí)計(jì)數(shù)器加一;

打印統(tǒng)計(jì)信息;用getchar輸入,很容易判斷文件結(jié)束。問題是如何確定“遇到了一個(gè)詞”。第九十五頁,共一百零九頁,編輯于2023年,星期日若讀的字符是單詞首字符,則計(jì)數(shù)器加一。讀入字符過程中需要區(qū)分是否空白。問題:非空白字符未必是詞的開始,是否新詞要看前一字符是否空白??梢姡翰荒芄铝⒌靥幚恚獏⒖记懊媲闆r。必須做情況記錄,以便后面參考。前后關(guān)系分兩種情況:1)讀到空白,隨后遇非空白字符就是新詞;2)讀到非空白,隨后不會(huì)遇到新詞??煽醋魈幚磉^程的不同狀態(tài)。兩種狀態(tài):1)讀在詞外(遇到非空白是新詞);2)讀在詞內(nèi)。在讀入字符的過程中讀入狀態(tài)也不斷轉(zhuǎn)換。典型,可以用有限狀態(tài)轉(zhuǎn)換系統(tǒng)(自動(dòng)機(jī))描述。第九十六頁,共一百零九頁,編輯于2023年,星期日IN/OUT(詞里/詞外)表示兩種讀入狀態(tài)。讀字符的動(dòng)態(tài)過程可以用圖示形象描述。在從OUT轉(zhuǎn)換到IN時(shí),遇到新詞,計(jì)數(shù)。第九十七頁,共一百零九頁,編輯于2023年,星期日用變量state記錄狀態(tài),令其值為IN/OUT,只要求這兩個(gè)值不同(不會(huì)同時(shí)處在兩種狀態(tài))。一個(gè)字符(假設(shè)存在變量c)的處理可描述為: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;}/*許多地方可以簡(jiǎn)化*/第九十八頁,共一百零九頁,編輯于2023年,星期日#include<stdio.h>enum{IN=1,OUT=0};intmain(void){intc,count=0,state=OUT;while((c=getchar())!=EOF)if(c==''||c=='\t'||c=='\n')state=OUT;elseif(state==OUT){state=IN;++count;}

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論