第6章 C控制语句:循环
學習筆記——《C Prime Plus》
第6章 C控制語句:循環(huán)
- 6.1 再探 while 循環(huán)
- 6.1.1 程序注釋
- 6.1.2 C風格讀取循環(huán)
- 6.2 while 語句
- 6.2.1 終止 while 循環(huán)
- 6.2.2 何時終止循環(huán)
- 6.2.3 while:入口循環(huán)條件
- 6.2.4 語法要點
- 6.3 _Bool 類型
- 6.4 不確定循環(huán)和計數(shù)循環(huán)
- 6.5 for 循環(huán)
- 6.5.1 利用 for 的靈活性
- 6.6 其他運算符:+=、-=、*=、/=、%=
- 6.7 逗號運算符
- 6.8 出口條件循環(huán):do while
- 6.9 如何選擇循環(huán)
- 6.10 循環(huán)嵌套
- 6.10.1 程序分析
- 6.10.2 循環(huán)變式
- 6.11 數(shù)組簡介
- 6.11.1 在 for 循環(huán)中使用數(shù)組
- 6.12 使用函數(shù)返回值的循環(huán)實例
- 6.12.1 程序分析
對于計算機科學而言,一門語言應該提供以下 3 種形式的程序流:
- 執(zhí)行語句序列;
- 如果滿足某些條件就重復執(zhí)行語句序列(循環(huán));
- 通過測試選擇執(zhí)行哪一個語句序列(分支)。
6.1 再探 while 循環(huán)
#include <stdio.h> int main(void) {long num;long sum = 0L; //把 sum 初始化為0,用long類型儲存更大的數(shù)int status;printf("Please enter an integer to be summed");printf("(q to quit):");status = scanf("%ld", &num);while(status == 1){sum = sum + num;printf("Please enter next integer (q to quit):");status = scanf("%ld", &num);}printf("Those integers sum to %ld. \n", sum);return 0; }6.1.1 程序注釋
首先:
根據(jù)測試條件 status == 1,只要 status 等于 1 ,循環(huán)就會重復。每次循環(huán),num 的當前值都被加到 sum 上,這樣 sum 的值始終是當前整數(shù)之和。當 status 的值不為 1 時,循環(huán)結束。
然后:
要讓程序正常運行,每次循環(huán)都要獲取 num 的一個新值,并重置 status。利用 scanf() 的兩個不同的特性來完成。
該程序利用 scanf() 的雙重特性避免了在循環(huán)中交互輸入時的這個棘手的問題。。例如,假設 scanf() 沒有返回值,那么每次循環(huán)只會改變 num 的值。
該程序的結構:
把 sum 初始化為0 提示用戶輸入數(shù)據(jù) 讀取用戶輸入的數(shù)據(jù)當輸入的數(shù)據(jù)為整數(shù)時 {輸入添加給 sum提示用戶進行輸入然后讀取下一個輸入 }輸入完成后,打印 sum 的值順便一提,這叫偽代碼(pseudocode),是一種用簡單的句子表示程序思路的方法,它與計算機語言的形式相對應。
偽代碼有助于設計程序的邏輯。確定程序的邏輯無誤之后,再把偽代碼翻譯成實際的編程代碼。使用偽代碼的好處之一是,可以把注意力集中在程序的組織和邏輯上,不用再設計程序時還要分心如何用編程語言來表達自己的想法。
6.1.2 C風格讀取循環(huán)
總之,因為 while 循環(huán)是入口條件循環(huán),程序在進入循環(huán)體之前必須獲取輸入的數(shù)據(jù)并檢查 status 的值, 所以在 while 前面要有一個 scanf() 。要讓循環(huán)繼續(xù)執(zhí)行,在循環(huán)內需要一個讀取數(shù)據(jù)的語句,這樣程序才能獲取下一個 status 的值,所以在 while 循環(huán)末尾還要有一個 scanf() ,它為下一次迭代做好了準備。
while 循環(huán)偽代碼標準格式:
根據(jù)偽代碼的設計思路,編寫代碼:
status = scanf("%ld", &num); while(status == 1) {//循環(huán)行為status = scanf("%ld", &num); //讓循環(huán)繼續(xù)執(zhí)行,為下一次迭代 }也可寫為:
while(scanf("%ld", &num) == 1) {//循環(huán)行為 }6.2 while 語句
while 循環(huán)的通用形式如下:
while( expression ) {statement; }statement 部分可以是以分號結尾的簡單語句,也可以是用花括號括起來的復合語句。
到目前為止,程序示例中的 expression 部分都使用關系表達式。也就是說,expression 是值之間的比較,可以使用任何表達式。如expression 為真(或者更一般地說,非零),執(zhí)行 statement 部分一次,然后再次判斷 expression。在 expression 為假(0)之前,循環(huán)的判斷和執(zhí)行一直重復進行。每次循環(huán)都被稱為一次迭代(iteration)。
6.2.1 終止 while 循環(huán)
while 循環(huán)有一點非常重要:在構建 while 循環(huán)時,必須讓測試表達式的值有變化,表達式最終要為假。否則,循環(huán)就不會終止。(可以使用 break 和 if 語句來終止循環(huán))。
index = 1; while (index < 5) {printf("Good morning!\n"); }上面程序段將打印無數(shù)次 Good morning!。因為循環(huán)中 index 的值一直都是原來的值 1,不曾改變。
6.2.2 何時終止循環(huán)
要明確一點:只有在對測試條件求值時,才決定是終止還是繼續(xù)循環(huán)。
#include <stdio.h> int main(void) {int n = 5;while(n < 7) //第7行{ printf("n = %d\n", n);n++; //第10行 printf("Now n = %d\n", n); //第11行 }printf("The loop has finished.\n"); return 0; }運行結果:
在第 2 次循環(huán)時,變量 n 在第 10 行首次獲得值 7 。但是,此時程序并未退出,它結束本次循環(huán)(第 11 行),并在對第 7 行的測試條件求值時才退出循環(huán)(變量 n 在第 1 次判斷時為 5 ,第 2 次判斷時為 6 )。
6.2.3 while:入口循環(huán)條件
while 循環(huán)是使用入口條件的有條件循環(huán)。所謂“有條件”指的是語句部分的執(zhí)行取決于測試表達式描述的條件,如(index < 5)。該表達式是一個入口條件(entry condition),因為必須滿足條件才能進入循環(huán)體。
6.2.4 語法要點
//糟糕的代碼創(chuàng)建了一個無限循環(huán) #include <stdio.h> int main(void) {int n = 0;while(n<3)printf("n is %d\n", n);n++;printf("That's all this program does\n'");return 0;}運行結果:
屏幕上回一直輸出以上內容,除非強行關閉這個程序。
雖然程序中縮進了 n++;這句話,但是并未把它和上一條語句括在花括號內。因此,只有直接跟在測試條件后面的一條語句是循環(huán)的一部分。變量 n 的值不會改變,條件 n < 3 一直為真。該循環(huán)體會一直打印 n is 0 ,這是一個無限循環(huán)(infinite loop),沒有外部干涉不會退出。
切記:
即使 while 語句本身使用復合語句,在語句構成上,它也是一條單獨的語句。該語句從while 開始執(zhí)行,到第 1 個分號結束。在使用了復合語句的情況下,到右花括號結束。
6.3 _Bool 類型
在編程中,表示真或假的變量被稱為布爾變量(Boolean variable),所以_Bool 是 C 語言中布爾變量的類型名,_Bool 類型的變量只能儲存 1(真)或 0(假)。如果把其他非零數(shù)值賦值非 _Bool 類型的變量,該變量會被設置為 1 。這反映了 C 把所有的非零值都視為真。給布爾變量取一個能表示真或假值的變量名是一種常見的做法。
6.4 不確定循環(huán)和計數(shù)循環(huán)
一些 while 循環(huán)是不確定循環(huán)(indefinite loop)。所謂不確定循環(huán),指在測試表達式為假之前,預先不知道要執(zhí)行多少次循環(huán)。例如,開頭的程序,通過與用戶交互獲得數(shù)據(jù)來計算整數(shù)之和,我們事先并不知道用戶會輸入什么整數(shù)。
另外,還有一類是計數(shù)循環(huán)(counting loop)。這類循環(huán)在執(zhí)行循環(huán)之前就知道要重復執(zhí)行多少次。
在創(chuàng)建一個重復執(zhí)行固定次數(shù)的循環(huán)中涉及了3個行為:
while 循環(huán)的測試條件執(zhí)行比較,遞增運算符執(zhí)行遞增,遞增發(fā)生在循環(huán)的末尾,這可以防止不小心漏掉遞增。但是計數(shù)器的初始化放在循環(huán)外,就有可能忘記初始化。實踐告訴我們可能發(fā)生的事情終究會發(fā)生,所以,我們可以用另一種控制語句,可以避免這些問題—— for 循環(huán)。
6.5 for 循環(huán)
for 循環(huán)把上述 3 個行為(初始化、測試和更新)組合在一處。
for 語句是一種入口條件循環(huán),即在執(zhí)行循環(huán)之前就決定了是否執(zhí)行虛幻。因此,for 循環(huán)可能一次都不執(zhí)行。
關鍵字 for 后面的圓括號中有 3 個表達式,分別用兩個分號隔開。
- 第 1 個表達式是初始化,只會在 for 循環(huán)開始時執(zhí)行一次。
- 第 2 個表達式是測試條件,在執(zhí)行循環(huán)之前對表達式求值。如果表達式為假,就結束循環(huán)。
- 第 3 個表達式執(zhí)行更新,在每次循環(huán)結束時求值。
完整的 for 循環(huán)還包括后面的簡單語句或復合語句。for 圓括號中的表達式也叫做控制表達式。
打印整數(shù) 1 ~ 6 及其對應的立方。
運行結果:
for 循環(huán)的第 1 行包含了循環(huán)所需的所有信息:num 的初始值,num 的種植和每次循環(huán) num 的增量。
注意:num 的終值不是 6 ,而是 7 。雖然最后一次循環(huán)打印的 num 的值是6, 但隨后 num++ 使num 的值為 7,然后 num<=6 為假,for 循環(huán)結束。
6.5.1 利用 for 的靈活性
for 的靈活性源于如何使用 for 循環(huán)中的 3 個表達式。第 1 個表達式給計數(shù)器賦初值,第 2 個表達式表示計數(shù)器的范圍,第 3 個表達式遞增計數(shù)器。
此外,for 循環(huán)還有其他 9 種用法。
(1)可以使用遞減運算符來遞減計數(shù)器:
#include <stdio.h> int main(void) {int secs;for(secs = 5; secs > 0; secs--){printf("%d seconds!\n", secs);}printf("We have ignition!\n");return 0; }(2)可以讓計數(shù)器遞增 2、10 等:
#include <stdio.h> int main(void) {int n; for(n=2; n < 60; n = n + 13) //從2開始,每次遞增13,在60內 printf("%d \n",n);return 0; }(3)可以用字符代替數(shù)字計數(shù):
#include <stdio.h> int main(void) {char ch;for(ch = 'a'; ch <= 'z'; ch++)printf("The ASCII value for %c is %d.\n", ch, ch);return 0; }(4)處理測試迭代次數(shù)外,還可以測試其他條件:
for (num = 1; num <= 6; num++)替換成:
for (num = 1; num*num*num <= 216; num++)如果與控制次數(shù)相比,你更關心限制立方的大小,就可以使用這樣的測試條件。
(5)可以讓遞增的量幾何增長,而不是算術增長。也就是說,每次都乘上一個固定量:
#include <stdio.h> int main(void) {double debt;for (debt = 100.0; debt < 150; debt = debt * 1.1)printf("Your debt si now %.2lf.\n", debt);return 0; }(6)第 3 個表達式可以使用任何合法的表達式。無論是什么表達式,每次迭代都會更新該表達式的值:
#include <stdio.h> int main(void) {int x;int y = 55;for (x = 1; y <= 75; y= (++x * 5) + 50)printf("%10d %10d\n", x, y);return 0; }運行結果:
該循環(huán)打印 x 的值和表達式 ++x * 5 + 50 的值。
注意:測試涉及 y,而不是 x。for 循環(huán)中的 3 個表達式可以是不同的變量;雖然該例可以正常運行,但是編程風格不太好。
(7)可以省略一個或多個表達式(但是不能省略分號),只要在循環(huán)中包含能結束循環(huán)的語句即可:
#include <stdio.h> int main(void) {int ans, n;ans = 2;for (n = 3; ans <= 25;)ans = ans * n;printf("n = %d; ans = %d.\n", n, ans);return 0; }該循環(huán)保持 n 的值為 3 。變量 anx 開始的值為 2 ,然后遞增到 6 和 18,最終是 54。
注意:省略第 2 個表達式會被視為真,程序會一直運行。
(8)第 1 個表達式不一定是給變量賦初值,也可以使用 printf() 。記住,在執(zhí)行循環(huán)的其他部分之前,只對第 1 個表達式求值一次或執(zhí)行一次。
#include <stdio.h> int main(void) {int num = 0;for (printf("Keep entering numbers!\n"); num != 6; )scanf("%d", &num);printf("That's the one I want!\n'");return 0; }程序打印第 1 行的句子一次,在用戶輸入 6 之前不斷接受數(shù)字。
(9)循環(huán)體的行為可以改變循環(huán)頭中的表達式:
for (n = 1; n < 10000; n = n + data)如果程序經過幾次迭代后發(fā)現(xiàn) delta 太大或太小,循環(huán)中的 if 語句可以改變 delta 的大小。
6.6 其他運算符:+=、-=、*=、/=、%=
//一下兩兩一組寫法相互等價: socore += 20; socore = socore + 20;dimes -= 2; dimes = dimes - 2;bunnies *= 2; bunnies = bunnies * 2;time /= 2.73; time = time / 2.73;reduce %= 3; reduce = reduce % 3;6.7 逗號運算符
逗號運算符擴展了 for 循環(huán)的靈活性,以便在循環(huán)頭中包含更多的表達式。
例如,打印一類郵件資費,郵資為首重 40 元/千克,續(xù)重20 元/千克:
該程序在初始化表達式和更新表達式中使用了逗號運算符。
初始化表達式中的逗號使 ounces 和 cost 都進行了初始化,更新表達式中的逗號每次都迭代 ounces 遞增1、cost 遞增 20(NEXT_Z 的值是20)。
逗號運算符并不局限于 for 循環(huán)中使用,但這是它最常使用的地方。
逗號運算符有兩個其他性質:
x = ( y = 3, ( z = ++y + 2 ) + 5 );
先把 3 賦給 y,遞增 y 為 4,然后把 4 加 2 之和 6 賦給 z,接著加上 5,最后把結果 11 賦給 x。
6.8 出口條件循環(huán):do while
while 循環(huán)和 for 循環(huán)都是入口條件循環(huán),即在循環(huán)的每次迭代之前檢查測試條件,所以有可能根本不執(zhí)行循環(huán)體中的內容。
C 語言還有出口條件循環(huán)(exit-condition loop),即在循環(huán)的每次迭代之后檢查測試條件,這保證了至少執(zhí)行循環(huán)體中的內容一次。
在用戶輸入 13 之前不斷提示用戶輸入數(shù)字。
也可使用 while 循環(huán)語句(入口條件):
#include <stdio.h> int main(void) {const int secret_code = 13;int code_entered;printf("To enter the triskaidekaphobia therapy club,\n");printf("please enter the secret code number:");scanf("%d", &code_entered);while (code_entered != secret_code){printf("To enter the triskaidekaphobia therapy club,\n");printf("please enter the secret code number:");scanf("%d", &code_entered);}printf("Congratulations! You are cured!\n");return 0; }需滿足循環(huán)條件,才會進入循環(huán);所有在 while 循環(huán)前面先實現(xiàn)一遍循環(huán)體的內容。
do while 循環(huán)的通用形式:
do {statement; }while( expression );statement 可以是一條簡單語句或復合語句。do while 循環(huán)以分號結尾。
do while 循環(huán)在執(zhí)行完循環(huán)體后才執(zhí)行測試條件,所以至少執(zhí)行循環(huán)體一次;而 for 循環(huán)或 while 循環(huán)都是在執(zhí)行循環(huán)體之前先執(zhí)行測試條件。
do while 循環(huán)使用于那些至少迭代一次的循環(huán)。例如,下面是一個包含 do while 循環(huán)的密碼程序偽代碼:
6.9 如何選擇循環(huán)
首先,確定是入口條件循環(huán)還是出口條件循環(huán)。
通常,入口條件循環(huán)用得比較多,原因是:
一般而言,當循環(huán)涉及初始化和更新變量時,用 for 循環(huán)比較合適,而在其他情況下用 while 循環(huán)更好。
對于下面這種條件,用 while 循環(huán)就很好:
while (scanf("%ld", &num) == 1)對于涉及索引計數(shù)的循環(huán),用 for 循環(huán)更合適:
for (count = 1; count <= 100; count++)6.10 循環(huán)嵌套
循環(huán)嵌套(nested loop)指在一個循環(huán)內包含另一個循環(huán)。嵌套循環(huán)常用于按行和列顯示數(shù)據(jù),也就是說,一個循環(huán)處理一行中的所有列,另一個循環(huán)處理所有的行。
#include <stdio.h> #define ROWS 6 #define CHARS 10int main(void) {int row;char ch;//內層循環(huán)一行打印 10 個字符,外層循環(huán)創(chuàng)建 6 行for(row = 0; row < ROWS; row++) //第10行 { for(ch = 'A'; ch < ('A'+ CHARS); ch++) //第12行{printf("%c", ch);}printf("\n");}return 0; }運行結果:
6.10.1 程序分析
(1)代碼第 10 行開始的 for 循環(huán)被稱為外層循環(huán)(outer loop),第 12 行開始的 for 循環(huán)被稱為內層循環(huán)(inner loop)。
(2)外層循環(huán)從 row 為 0 開始循環(huán), 到 row 為 6 時結束;因此,外層循環(huán)要執(zhí)行 6 次,row 的值從 0 變?yōu)?5。
(3)每次迭代要執(zhí)行的第 1 條語句是內層的 for 循環(huán),該循環(huán)要執(zhí)行 10 次,在同一行打印字符 A~J;第二句是外層循環(huán)的 printf("\n");
(4)嵌套循環(huán)中的內層循環(huán)在每次外層循環(huán)迭代是都要執(zhí)行完所有的循環(huán);比如:此代碼中的內層循環(huán)每次都要執(zhí)行 10 次才結束,然后執(zhí)行 printf("\n"); 然后再進行外層循環(huán)的下一次循環(huán)。
6.10.2 循環(huán)變式
可以通過外層循環(huán)控制內層循環(huán),在每次外層循環(huán)迭代時內層循環(huán)完成不同的任務。比如:內層循環(huán)開始打印的字符取決于外層循環(huán)的迭代次數(shù)。
#include <stdio.h> int main(void) {const int ROWS = 6;const int CHARS = 6; //用const關鍵字代替#define int row;char ch;//依賴外部循環(huán)的嵌套循環(huán) for(row = 0; row < ROWS; row++){for(ch = ('A' + row); ch <('A'+ CHARS); ch++){printf("%c", ch);}printf("\n");}return 0; }運行結果:
6.11 數(shù)組簡介
數(shù)組(array)是按順序儲存的一系列類型相同的值,如 10 個 char 類型的字符或 15 個 int 類型的值,整個數(shù)組有一個數(shù)組名,通過整數(shù)下標訪問數(shù)組中單獨的項或元素(element)。
float debts[20]; debts[5] = 32.54; debts[6] = 1.2e+21;聲明 debts 是一個內含 20 個元素的數(shù)組,每個元素都可以儲存 float 類型的值。數(shù)組的第 1 個元素是 debts[0],第 2 個元素是 debts[1],以此類推,直到 debts[19]。
注意,數(shù)組元素的編號從 0 開始,而不是從 1 開始。
把值讀入指定的元素中:
scanf("%f", &debts[4]); //把一個值讀入數(shù)組的第5個元素用于識別數(shù)組元素的數(shù)字被稱為下標(subscript)、索引(indice)、或偏移量(offset)。下標必須是整數(shù),而且要從 0 開始計數(shù)。數(shù)組的元素被依次儲存在內存中相鄰的位置。
6.11.1 在 for 循環(huán)中使用數(shù)組
該程序讀取 10 個高爾夫分數(shù),用 for 循環(huán)來讀取數(shù)據(jù)。
程序打印總分、平均分、差點(handicap,是平均分與標準分的差值)
運行結果:
(1)首先,注意程序實例雖然打印了 11 個數(shù)字,但是只讀入了 10 個數(shù)字,因為循環(huán)只讀了 10 個值。
(2)由于scanf() 會跳過空白字符,所以可以在一行輸入 10 個數(shù)字,也可以每行只輸入一個數(shù)字,或者像本例這樣混合使用空格和換行符隔開每個數(shù)字(因為輸入時緩沖的,只有當用戶鍵入 Enter 鍵后數(shù)字才會被發(fā)送給程序)。
要讀取 int 類型變量 fue,應該寫成:
scanf("%d", &fue);要讀取 int 類型的元素 score[index],應該寫成:
scanf("%d",&score[index]);較好的編程風格:
(1)使用 #define 指令創(chuàng)建的明示常量(SIZE)來指定數(shù)組的大小。如果以后要擴展程序處理 20 個分數(shù),只需簡單地把 SIZE 重新定義為 20 即可,不用逐一修改程序中使用了數(shù)組大小的每一處。
(2)使用 3 個獨立的 for 循環(huán),遵循了模塊化(modularity)的原則。模塊化的思想是:應該把程序劃分為一些獨立的單元,每個單元執(zhí)行一個任務。這樣做提高了程序的可讀性,也方便后續(xù)更新或修改程序。
6.12 使用函數(shù)返回值的循環(huán)實例
編寫一個有返回值的函數(shù),要完成以下內容:
例如:
double power(double n, int p) //返回一個 double 類型的值 {double pow = 1;int i;for (i = 1; i<=p; i++)pow *= n;return pow; //返回 pow 的值 }要聲明函數(shù)的返回類型,在函數(shù)名前寫出類型即可,就像聲明一個變量那樣。
關鍵字 return 表明該函數(shù)將把它后面的值返回給主調函數(shù)。
返回值也可以是表達式的值:
運行結果
6.12.1 程序分析
main()
實例中的 main() 是一個驅動程序(driver),即被設計用來測試函數(shù)的小程序。
while 循環(huán)
(1)輸入1.2 12,scanf() 成功讀取兩值,并返回2,循環(huán)繼續(xù)。因為 scanf() 跳過空白,多頁可以多行輸入。
(2)輸入 q 會使 scanf() 的返回值為 0,因為 q 與 scanf() 中的轉換說明 %lf 不匹配。scanf() 將返回 0,循環(huán)結束。類似地,輸入 2.8 q 會使 scanf() 的返回值為 1,循環(huán)也會結束。
power() 函數(shù)
(1)第一次出現(xiàn):double power(double n, int p); //ANSI函數(shù)原型
這是 power() 函數(shù)的原型,它聲明程序將使用一個名為 power() 的函數(shù)。開頭的關鍵字 double 表明 power() 函數(shù)返回一個 double 類型的值。編譯器要知道 power() 函數(shù)返回值的類型,才能知道有多少字節(jié)的數(shù)據(jù),以及如何解釋它們,這就是為什么必須聲明函數(shù)的原因。
圓括號中的 double n, int p 表示power() 函數(shù)的兩個參數(shù)。第 1 個參數(shù)應該是 double 類型的值,第 2 個參數(shù)應該是 int 類型的值。
----------------------------------------------------------------------------------------------------------------------------
(2)第二次出現(xiàn):xpow = power(x, exp); //函數(shù)調用
程序調用 power() 函數(shù),把兩個值傳遞給它。該函數(shù)計算 x 的 exp 次冪,并把計算結果返回給主調函數(shù)。在主調函數(shù)中,返回值將被賦給變量 xpow。
----------------------------------------------------------------------------------------------------------------------------
(3)第三次出現(xiàn):double power(double n, int p) //函數(shù)定義
power() 函數(shù)有兩個形參,一個是 double 類型,一個是 int 類型,分別由變量 n 和變量 p 表示。
注意:函數(shù)定義的末尾沒有分號,而函數(shù)原型的末尾有分號。
power() 函數(shù)用 for 循環(huán)計算 n 的 p 次冪,并把計算結果賦給 pow,然后返回 pow 的值,return pow;
總結
以上是生活随笔為你收集整理的第6章 C控制语句:循环的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第4章 字符串和格式化输入/输出
- 下一篇: 第7章 C控制语句:分支和跳转