基本程序設計技術_第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

提交評論