下列何種情境最適合使用 while 迴圈(而不是一開始就知道確切次數的 for 迴圈)?
選項 A 正確(★ 本題標準答案)
使用者會輸入幾次是執行時才會知道的,事先無法確定次數,這正是最適合用 while(依條件持續執行)的情境。
選項 B 錯誤
1 加到 100 的次數在寫程式的當下就已經確定是 100 次,用 for 迴圈更直覺。
選項 C 錯誤
九九乘法表的次數固定是 81 次,屬於次數已知的情境,適合用 for 迴圈(甚至巢狀 for)。
選項 D 錯誤
印出前 10 個元素的次數也是固定已知的 10 次,適合用 for 迴圈。
關於 C 語言 for、while、do-while 三種迴圈的敘述,下列何者正確?
選項 A 錯誤
只要把初始化、條件、更新三個部分正確搬移,for 迴圈幾乎都能改寫成功能相同的 while 迴圈,並非「無法達成相同效果」。
選項 B 正確(★ 本題標準答案)
三種迴圈在能力上是等價的,差異主要在於程式碼的撰寫習慣與可讀性(例如已知次數用 for 較直覺),而不是某一種迴圈能做到、其他迴圈做不到的事。
選項 C 錯誤
do-while 迴圈完全可以搭配 break 與 continue,用法與其他迴圈相同。
選項 D 錯誤
while 與 for 迴圈都可以依賴一個在執行時才確定真假的條件持續執行,不需要在宣告時就指定確切次數。
執行後輸出結果為何?
1 #include <stdio.h>
2 int main(void) {
3 int product = 1;
4 for (int i = 5; i >= 1; i--) {
5 product *= i;
6 }
7 printf("%d\n", product);
8 return 0;
9 }選項 A 錯誤
15 是 1 到 5 的總和,不是這個迴圈計算的乘積。
選項 B 錯誤
20 不符合 5×4×3×2×1 依序相乘的結果。
選項 C 正確(★ 本題標準答案)
迴圈從 i=5 倒數到 i=1,product 依序累乘 5×4×3×2×1,結果為 120,也就是 5 的階乘。
選項 D 錯誤
24 是 4 的階乘(4×3×2×1),並不是這個迴圈(從 5 開始)計算出的結果。
撰寫者原本想要讓迴圈把 1 加到 5 的總和存入 sum,但行號 5 多打了一個分號。關於這個程式,下列敘述何者正確?
1 #include <stdio.h>
2 int main(void) {
3 int sum = 0;
4 int i;
5 for (i = 1; i <= 5; i++);
6 {
7 sum += i;
8 }
9 printf("%d\n", sum);
10 return 0;
11 }選項 A 錯誤
`for(...);` 讓分號本身成為一個合法的「空敘述」,這是完全合法的 C 語法(雖然通常是不小心造成的),不會產生編譯錯誤。
選項 B 錯誤
for 迴圈本身仍然會照常執行(把 i 從 1 遞增到讓條件失敗為止),行號 6-8 的區塊也仍然會執行一次,並不是整段都被跳過。
選項 C 錯誤
多出來的分號讓 for 迴圈的主體變成空敘述,sum += i 這一行其實已經不在迴圈控制範圍內,並不會被重複執行 5 次,所以不會是 15。
選項 D 正確(★ 本題標準答案)
分號讓 for 的主體變成空敘述,迴圈只是不斷把 i 遞增直到 6(此時 6<=5 為假才結束),中途什麼都沒做;行號 6-8 的區塊是獨立於迴圈之外、只會執行一次的敘述,用的是迴圈結束後 i 的值 6,因此 sum 只被加了一次 6,結果是 6。
此迴圈的主體內,除了 for 迴圈本身的更新部分 i++ 之外,又額外在行號 6 手動對 i 做了一次遞增。執行後 count 印出的值是多少?
1 #include <stdio.h>
2 int main(void) {
3 int count = 0;
4 for (int i = 0; i < 5; i++) {
5 count++;
6 i++;
7 }
8 printf("%d\n", count);
9 return 0;
10 }選項 A 正確(★ 本題標準答案)
每一輪主體內,i 除了 for 本身的更新(i++)外,又被手動多加了 1,等於每輪 i 實際上增加了 2:i=0→(手動)1→(for更新)2;i=2→(手動)3→(for更新)4;i=4→(手動)5→(for更新)6,此時 6<5 為假而結束。主體總共只執行了 3 次,count 印出 3。
選項 B 錯誤
5 是假設沒有行號 6 這行手動遞增、只靠 for 本身更新時的執行次數,但此程式因為多了一次手動遞增,實際執行次數比 5 少。
選項 C 錯誤
2 低估了主體實際被執行的次數。
選項 D 錯誤
在迴圈主體內修改迴圈變數是完全合法的 C 語法(雖然通常會讓邏輯變得難以理解,不建議這樣寫),不會造成編譯錯誤。
n 與 m 的初始值都是 3,條件也都是「大於 0」。執行後兩行輸出分別是什麼?
1 #include <stdio.h>
2 int main(void) {
3 int n = 3;
4 while (n > 0) {
5 printf("%d ", n);
6 n--;
7 }
8 printf("\n");
9 int m = 3;
10 do {
11 printf("%d ", m);
12 m--;
13 } while (m > 0);
14 printf("\n");
15 return 0;
16 }選項 A 錯誤
do-while 並不會因此「少印出」任何數字;當條件一開始就成立時,do-while 的行為與 while 完全相同。
選項 B 正確(★ 本題標準答案)
n 與 m 一開始都滿足大於 0 的條件,因此 while 的「先判斷才執行」與 do-while 的「先執行才判斷」在這種情況下不會造成差異:兩者都會依序印出 3、2、1,然後才因為條件不成立而結束。
選項 C 錯誤
while 迴圈在 n 變成 0 時,條件 n>0 已經為假,不會再印出 0,因此不會印出 "3 2 1 0"。
選項 D 錯誤
n 與 m 在主體中都有遞減動作,最終都會讓條件變成假而正常結束,不會是無窮迴圈。
小明想要寫一個「輸入驗證」程式:不斷要求使用者輸入一個 1 到 5 之間的數字,直到輸入正確為止;而且無論如何,程式一開始至少要先詢問使用者一次。下列何種迴圈結構最適合用來實作這個需求?
選項 A 錯誤
使用 while 時,在第一次檢查條件之前,input 必須已經有一個值可以判斷;但此時使用者根本還沒被詢問過,input 的初始值只能是一個沒有實際意義的暫定值,邏輯上不乾淨。
選項 B 錯誤
與 while 版本有相同的問題:條件式在第一次檢查時就需要用到 input,但這時使用者還沒有被詢問過。
選項 C 正確(★ 本題標準答案)
do-while 保證主體(詢問並讀取)至少會先執行一次,才去檢查輸入是否在範圍內,這正好符合「一開始至少要先詢問一次」的需求,不需要為 input 準備一個沒有意義的初始值。
選項 D 錯誤
如選項 A、B、C 的分析所示,針對「必須先執行至少一次」這個需求,do-while 的寫法明顯比另外兩者更自然、更乾淨,三者並非完全沒有差別。
關於 while 與 do-while 迴圈的敘述,下列何者正確?
選項 A 錯誤
選擇哪一種迴圈應該依照「是否需要至少執行一次」的實際需求來決定,並沒有哪一種是普遍上更推薦優先使用的。
選項 B 錯誤
do-while 迴圈完全可以搭配 break 提前跳出,用法與 while、for 相同。
選項 C 錯誤
這個描述並不是判斷兩者是否會產生不同執行次數的正確依據;真正的關鍵在於「條件一開始(進入迴圈前)是真是假」。
選項 D 正確(★ 本題標準答案)
當條件一開始就成立時,兩者行為相同(如第 6 題);當條件一開始就不成立時,while 完全不執行主體,do-while 仍會先執行一次(如第 7 題的情境),這是兩者唯一會造成差異的情況。
執行後輸出結果為何?
1 #include <stdio.h>
2 int main(void) {
3 int arr[5] = {3, 7, 2, 9, 5};
4 int target = 2;
5 int foundIndex = -1;
6 for (int i = 0; i < 5; i++) {
7 if (arr[i] == target) {
8 foundIndex = i;
9 break;
10 }
11 }
12 printf("%d\n", foundIndex);
13 return 0;
14 }選項 A 正確(★ 本題標準答案)
陣列依序比對 arr[0]=3、arr[1]=7 都不等於 target=2;arr[2]=2 剛好相符,foundIndex 被設為 2,接著 break 跳出迴圈,因此印出 2。
選項 B 錯誤
-1 是 foundIndex 找不到時的預設值;但此陣列中確實有元素 2(索引 2),會被找到並更新 foundIndex,不會維持在 -1。
選項 C 錯誤
0 並不是 target 被找到時的索引;arr[0]=3,並不等於 target=2。
選項 D 錯誤
5 已經超出陣列的合法索引範圍(0 到 4),也不是 target 被找到的位置。
執行後 sum 印出的值是多少?
1 #include <stdio.h>
2 int main(void) {
3 int arr[5] = {4, -2, 6, -8, 3};
4 int sum = 0;
5 for (int i = 0; i < 5; i++) {
6 if (arr[i] < 0)
7 continue;
8 sum += arr[i];
9 }
10 printf("%d\n", sum);
11 return 0;
12 }選項 A 錯誤
3 只計算了最後一個非負元素,並沒有把 4、6 一起加總進去。
選項 B 正確(★ 本題標準答案)
陣列中 -2 與 -8 是負數,會被 continue 跳過(不執行 sum += arr[i]);其餘的 4、6、3 會被加總:4+6+3=13。
選項 C 錯誤
-3 完全不符合把非負元素相加後應該得到的結果,可能是誤把負數也加了進去。
選項 D 錯誤
5 沒有正確反映 4+6+3 的加總結果。
關於此巢狀迴圈的執行結果,下列敘述何者正確?
1 #include <stdio.h>
2 int main(void) {
3 int count = 0;
4 for (int i = 1; i <= 2; i++) {
5 for (int j = 1; j <= 3; j++) {
6 if (j == 2)
7 continue;
8 count++;
9 }
10 }
11 printf("%d\n", count);
12 return 0;
13 }選項 A 錯誤
j=2 時 count++ 確實會被跳過,因此 count 增加的次數並不等於總迭代次數 6,而是比 6 少(只有 4 次真正執行了 count++)。
選項 B 錯誤
continue 只會影響它所在的那一層(內層 j 迴圈),並不會讓外層 i 迴圈跳過任何動作。
選項 C 正確(★ 本題標準答案)
每一輪外層迴圈中,內層 j=1 讓 count++,j=2 被 continue 跳過,j=3 又讓 count++,所以每輪外層貢獻 2 次;外層跑 2 輪,總共讓 count 增加 4 次。
選項 D 錯誤
continue 完全可以用在巢狀迴圈的任何一層內部,這是非常常見且合法的寫法。
執行後 count 印出的值是多少?
1 #include <stdio.h>
2 int main(void) {
3 int count = 0;
4 for (int i = 0; i < 5; i++) {
5 for (int j = 0; j < 4; j++) {
6 count++;
7 }
8 }
9 printf("%d\n", count);
10 return 0;
11 }選項 A 錯誤
5 只是外層迴圈的執行次數。
選項 B 錯誤
4 只是內層迴圈單獨一輪的執行次數。
選項 C 錯誤
9 是 5 與 4 相加的結果,但巢狀迴圈的總執行次數應該是相乘,不是相加。
選項 D 正確(★ 本題標準答案)
外層迴圈執行 5 次,每次外層迭代內層都會完整執行 4 次,總執行次數是 5×4=20。
執行後 count 印出的值是多少?
1 #include <stdio.h>
2 int main(void) {
3 int count = 0;
4 for (int i = 1; i <= 4; i++) {
5 for (int j = 1; j <= 4; j++) {
6 if (j == 2)
7 break;
8 count++;
9 }
10 }
11 printf("%d\n", count);
12 return 0;
13 }選項 A 正確(★ 本題標準答案)
每一輪外層迴圈中,內層只有 j=1 會讓 count++,j=2 時就 break 跳出內層;外層總共跑 4 輪,因此 count 總共增加 4×1=4。
選項 B 錯誤
16(4×4)是假設完全不受 break 影響、內層每輪都跑滿 4 次才會得到的結果。
選項 C 錯誤
8 並不符合每輪外層只讓 count 增加 1 次的實際情況。
選項 D 錯誤
1 只反映了單一一輪的貢獻,忽略了外層迴圈總共跑了 4 輪。
執行後會印出幾行星號圖案?每一行分別有幾個星號?
1 #include <stdio.h>
2 int main(void) {
3 for (int i = 1; i <= 3; i++) {
4 for (int j = 1; j <= i; j++) {
5 printf("*");
6 }
7 printf("\n");
8 }
9 return 0;
10 }選項 A 錯誤
內層迴圈的終止條件是 `j <= i`,並不是固定的 3,而是會隨著外層的 i 一起變化,因此每一行的星號數量並不相同。
選項 B 正確(★ 本題標準答案)
當 i=1 時,內層只跑 j=1(1 個星號);i=2 時內層跑 j=1,2(2 個星號);i=3 時內層跑 j=1,2,3(3 個星號),配合每輪外層結束後的換行,形成 3 行、星號數依序為 1、2、3 的三角形圖案。
選項 C 錯誤
行號 7 的換行是寫在外層迴圈內部、內層迴圈結束之後,每跑完一輪外層就會換一次行,因此不會全部擠在同一行。
選項 D 錯誤
此程式的內層迴圈次數並不是固定的 3,而是依 i 而變(1、2、3 次),並不是簡單的 3×3=9。
撰寫者原本想要反向印出陣列的所有元素(從索引 4 印到索引 0)。執行後,這個程式最可能發生什麼問題?
1 #include <stdio.h>
2 int main(void) {
3 int arr[5] = {10, 20, 30, 40, 50};
4 for (unsigned int i = 4; i >= 0; i--) {
5 printf("%d ", arr[i]);
6 }
7 printf("\n");
8 return 0;
9 }選項 A 錯誤
確實會先正確印出 50 40 30 20 10,但迴圈並不會就此停止,i 遞減到 0 之後還會繼續減 1 而環繞,導致後續存取超出陣列範圍。
選項 B 錯誤
迴圈在印出 5 個數字後,i 會從 0 再被減 1,因為型態是 unsigned int,並不會變成 -1 而讓 `i >= 0` 判斷為假,所以不會正常結束。
選項 C 正確(★ 本題標準答案)
i 遞減到 0 之後再減 1,會環繞成一個極大的正整數,使得 `i >= 0` 恆為真;此後程式會不斷嘗試存取像 arr[極大的數字] 這種遠遠超出陣列範圍的記憶體位置,這是未定義行為,實際執行時很可能造成當機或印出亂數垃圾值。
選項 D 錯誤
編譯器(在一般設定下)並不會自動偵測並擋下這種執行期才會發生問題的陷阱,這種寫法通常可以正常通過編譯。
在檢查別人撰寫的 C 語言迴圈程式碼、找出潛在的 unsigned 計數器陷阱時,下列何種特徵最值得特別留意?
選項 A 錯誤
變數命名只是慣例上的習慣,與是否會發生 unsigned 環繞陷阱完全無關。
選項 B 錯誤
迴圈內是否呼叫 printf 只是印出結果的動作,與計數器本身是否會溢位沒有直接關係。
選項 C 錯誤
執行次數的多寡本身不是判斷是否有 unsigned 陷阱的依據,次數少的迴圈一樣可能踩到這個陷阱(如第 15 題只有 5 個元素)。
選項 D 正確(★ 本題標準答案)
unsigned(或 size_t)計數器搭配「可能遞減到 0 以下」的邏輯(例如反向走訪、倒數),正是造成環繞陷阱最典型也最需要特別檢查的組合。
承第 15 題,若想要修正該程式,讓它能夠正確且安全地反向印出陣列所有元素(從索引 4 到索引 0),下列哪一種修改方式最恰當?
選項 A 正確(★ 本題標準答案)
把 i 的型態從 unsigned int 改成一般的 int,可以讓 i 正確表示到 -1,迴圈就能在 i 遞減到 -1 時正常讓條件 `i >= 0` 判斷為假並結束,正確且安全地走訪索引 4 到 0。
選項 B 錯誤
把條件改成 `i > 0` 雖然能避免環繞造成的無窮迴圈,但會在 i=0 時就提前結束,導致索引 0(陣列的第一個元素)沒有被印出,並沒有完整達成需求。
選項 C 錯誤
`i -= 1` 與 `i--` 效果完全相同,並不能解決 unsigned 環繞的根本問題。
選項 D 錯誤
i 仍然是 unsigned int,`i < 0` 這個判斷式對 unsigned 變數而言永遠不可能為真(unsigned 沒有負數),這個保護完全不會被觸發,無法真正解決問題。
執行後輸出結果為何?
1 #include <stdio.h>
2 int main(void) {
3 int arr[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
4 for (int i = 1; i < 12; i += 4) {
5 printf("%d ", arr[i]);
6 }
7 printf("\n");
8 return 0;
9 }選項 A 錯誤
此迴圈從 i=1 開始,並不是從 0 開始逐一走訪。
選項 B 正確(★ 本題標準答案)
i 依序為 1、5、9(下一次會是 13,因為 13<12 為假而停止),對應印出 arr[1]、arr[5]、arr[9],也就是 1、5、9。
選項 C 錯誤
4、8、12 這組數字並不是此迴圈實際走訪到的索引;12 也已經超出合法索引範圍(0 到 11)。
選項 D 錯誤
此選項只列出部分結果,且與 i 從 1 開始、步伐為 4 的實際走訪順序不符。
執行後兩行輸出分別是什麼?
1 #include <stdio.h>
2 int myStrlen(char s[]) {
3 int len = 0;
4 while (s[len] != '\0') {
5 len++;
6 }
7 return len;
8 }
9 int main(void) {
10 printf("%d\n", myStrlen(""));
11 printf("%d\n", myStrlen("Cat"));
12 return 0;
13 }選項 A 錯誤
空字串常值 "" 是完全合法的 C 語法,它在記憶體中就是單獨一個 '\0' 字元,不會造成編譯錯誤。
選項 B 錯誤
空字串的長度應該是 0(沒有任何實際字元),s[0] 就是 '\0',迴圈主體完全不會執行,不會是 1。
選項 C 正確(★ 本題標準答案)
myStrlen("") 中 s[0] 就是 '\0',迴圈條件立刻為假,len 維持 0;myStrlen("Cat") 依序遇到 'C'、'a'、't' 讓 len 累加到 3,接著遇到 s[3]='\0' 而停止,回傳 3。
選項 D 錯誤
"Cat" 的長度是 3(C、a、t 三個字元),結尾的 '\0' 是終止符號,不應該被算進字串長度,因此不會是 4。
若呼叫 findMax(arr, 4),其中 arr = {-5, -2, -8, -1}(陣列中所有元素都是負數),這個函式的回傳值會是多少?程式碼本身有沒有問題?
1 int findMax(int arr[], int size) {
2 int max = 0;
3 for (int i = 0; i < size; i++) {
4 if (arr[i] > max)
5 max = arr[i];
6 }
7 return max;
8 }選項 A 錯誤
程式實際上並不會回傳 -1;因為 max 從 0 開始,陣列中所有元素都比 0 小,`arr[i] > max` 永遠不成立,max 從頭到尾都不會被更新。
選項 B 錯誤
迴圈中並沒有任何會把 max 更新成陣列最小值的邏輯;max 只會維持在初始值 0,不會變成 -8。
選項 C 錯誤
把 max 宣告並初始化為 0 是完全合法的 C 語法,不會造成編譯錯誤。
選項 D 正確(★ 本題標準答案)
max 被寫死初始化為 0,但陣列中所有元素都是負數(都比 0 小),導致 `arr[i] > max` 永遠不成立,max 永遠不會更新,最後回傳的 0 甚至不是陣列裡真正存在的數字;正確做法應該把 max 初始化為陣列的第一個元素 arr[0],才能正確處理「全部都是負數」這種情況。