在開始學習程式時,迴圈(loops)是你會經常使用的工具。想像一下,不用一遍又一遍地手動寫程式,而是讓程式自動完成這些重複的工作。本篇會用有趣的例子解釋三種常見的迴圈:for
、while
和do...while
,計算 1 到 100 的總和,再進階運用函數寫出九九乘法表。學會迴圈跟函數的概念,不僅在 C 語言使用,也可以在其他語言靈活運用來簡化程式。
如何用迴圈寫 1 加到 100?
在寫程式時我們先拆解成小問題,成 1+2+3+4。首先我們先看如何寫出 1 2 3 4 ,每一個都比前一個加 1,所以若 1 改成 i,2 就會寫成 i+1。
當你寫 i=i+1
時 (也可以寫成i++
),意思是讓 i 自己加 1。舉個例子:如果 i 原本是 1,i=i+1
就會讓 i 變成 2。接著,新的 i 變成 2 後,再次執行i=i+1
,它就會變成 3。每次都讓 i 變成原本的 i 再加 1。
sum=sum+i
。這個意思是,每次多加一個新的 i 所以要重新加總。在寫程式的時候不可能每次都重打 sum=sum+i
這時候就要使用迴圈。for 用法
for (起始值; 停止條件 ; 每次執行完迴圈後變數更新) {
// 要重複做的事情
}
- 注意中間的分隔是
;
獲得這兩個邏輯後,我第一次寫程式居然跑出的答案是 2601 ,但我完全不知道為什麼。
#include <stdio.h>
int main(){
int sum = 0;
for (int i = 0 ; i <= 100 ; i++ ){ // i從0開始每次做完i都加1,直到100才停下
i = i + 1;
sum = sum + i;
}
printf("總和為%i", sum);
}
試了幾次答案都不是 5050。有一個簡單的方式是先輸入範圍較小的數字測試,例如 1 加到 3,觀察答案是否等於 6。
最後發現是我多寫 i = i +1
。
for
迴圈中的第三個欄位,每次執行時 i 都會增加 1,正常來說應該遍歷 0 到 100。但是,因為我在迴圈裡又寫了 i = i + 1;
,所以每次 i 都會額外增加 1,這會導致每次 i 的值跳過一個數字(例如從 0 跳到 2,再從 2 跳到 4)。結果:迴圈實際上只加總了偶數的 i 值(2, 4, 6,..., 100),而不是 1 到 100 的全部數字。
- 為了更方檢查是哪個區段出問題,我將起始值跟最後的值設為變數。
#include <stdio.h>
#include <cs50.h>
int main(){
int x = get_int("起點: ");
int y = get_int("終點: ");
int sum = 0;
for (int i = x-1 ; i <= y;i++ ){
sum = sum + i;
}
printf("總和為%i",sum);
}
do...while 用法
也是迴圈的一種,但無論條件是否成立,他至少會執行第一次。
do {
// 先做這裡的事
} while (條件);
同樣計算 1 到 100 的總和,用 do...while 就會是:
#include <stdio.h>
int main(){
int sum = 0;
int i = 0;
do{
sum = sum + i; // 要做的事
i++; // 每次做完都要執行的動作
}while (i<= 100);
printf("總和為%i",sum);
}
什麼時候適合用 do...while
do...while
的迴圈會用在輸入使用者密碼,想讓他們先輸入一次密碼,再來檢查是否正確。如果密碼錯誤,就要求他們再輸入。這時候,do...while
就很有用,因為你至少需要讓使用者先輸入一次密碼。
#include <stdio.h>
int password;
do {
printf("請輸入密碼:");
scanf("%d", &password); //scanf()讀取使用者輸入的值,後續會介紹&變數的意思
} while (password != 1234);
while 用法
while (條件) {
// 當條件成立時,重複執行這裡的程式碼
}
#include <stdio.h>
int main(){
int sum = 0;
int i = 0;
while(i <= 100) { // i 小於100的條件成立,重複執行以下的程式碼
sum = sum +i;
i++;
}
printf("總和為%i",sum);
}
什麼時候用 while
:
- 當你不知道要執行幾次:例如要求使用者輸入正確的密碼,或者等到某個狀況發生為止,這種情況下
while
很適合。 - 持續重複某個動作直到條件改變:例如等待某個感測器達到一定溫度,或者等到某個數據變化。
- 如果條件永遠成立,會造成無限迴圈,例如:
while (1) { // 1 是布林值true
// 這樣會一直執行,因為條件永遠成立
}
這樣會使讓程式卡住,需特別小心。
只加總偶數
至於 1 到 100 的偶數需要搭配餘數的概念,a%b
意思是取 a/b 的餘數。
#include <stdio.h>
int main(){
for (int i=1; i <= 100; i++){
int a = i; // 一開始我先用實際的整數,像是2 4 6 8 測試
int b = 2;
if (a % b == 0 ){ // a/b的餘數等於0,百分比表示餘數
printf("%i ",a);
}
}
從九九乘法表來看巢狀迴圈(nested loops)和函數作用域
既然已經熟悉三種迴圈的使用方式,我們來練習寫出九九乘法表。

思路拆解:(可參考一遍我寫的,稍後會詳細介紹思考流程)
- 需要有「相乘」的功能。
- 先成功寫出第一列(row),注意唯一的變化是哪些數字。
- 再往外擴展外層迴圈。
#include <stdio.h>
int mul(int a, int b); // 宣告後面會使用mul這個函數。
int main()
{
for (int j = 1; j <= 9; j++)//重複九列
{
for (int i = 1; i <= 9; i++) // 外層寫i,內層j的寫法更常見
{
int z = mul(i, j); //這是第一列 (先寫)
printf("%ix%i=%i\t", i, j, z);
}
printf("\n"); //一列跑完就換列
}
}
int mul(int a, int b)
{
return a * b;
}
相乘的功能怎麼寫?
在C語言可以自定義函數,我習慣解釋成「功能」。例如我定義 mul
兩數相乘的功能,只要我餵了兩個數字,這個函數接受兩個數字,然後回傳它們的乘積。 建議使用 a 跟 b (任何獨立於主程式的變數) 而不是待會要跑迴圈使用的變數,避免在解讀程式時搞混變數。a 跟 b 也可以解釋成,接受兩個不同的數值。
int mul(int a, int b)
{
return a * b;
}
1. 作用域
作用域指的是一個變數可以在哪裡被存取。
- 像是變數
i
和j
它們只在for
迴圈裡有效,這叫做「區域變數」。這些變數只會在迴圈裡面被使用,出了迴圈就消失。 mul
函數裡的變數a
和b
也是區域變數,它們只在mul
裡面有效,函數執行完畢後它們就不再存在了,所以我們還需要做函數宣告。
2. 函數宣告的概念(函數原形 function prototype)
在 int mul(int a, int b);
這裡,這是一個函數宣告,寫在主程式前面。告訴程式「等一下我會用到 mul
這個函數」。這樣程式知道這個函數的存在,即使它的細節在後面才寫出來。
為什麼不要作用在全域?
1. 程式可讀性降低
當一個變數作用於全域,也就是適用於整個程式,會使追蹤變數的來源變得很困難。當你想知道其中一個數值是怎麼來的,你需要檢查整個程式。
2. 除錯(debug)困難
因為全域變數可以在任何地方被修改,當出現問題時,你很難快速確定是哪個部分的程式修改了這個變數。
成功寫出第一列
for (int i = 1; i <= 9; i++)
{
int z = mul(i, 1); // 這是第一列 (一開始寫,先定義1個變數就好)
printf("%ix%i=%i\t", i, 1, z); // 印出 1x1=1 2x1=2 ....
}
- 雖然程式運行從外圈開始運行,但我習慣從內圈往外寫。
- 先定義的 z ,z 的值用
mul
函數,計算乘積。 - 印出來最後加上
\t
使每次印出的資料都空格一個 tab 鍵。
加上第二個迴圈(外圈)
for (int j = 1; j <= 9; j++) { // (2)總共跑9列,定變數為j,再去把內層迴圈裡面的1改成j
for (int i = 1; i <= 9; i++) {
int z = mul(i, j);
printf("%ix%i=%i\t", i, j, z);
}
printf("\n");// (1)一列跑完要換列
}
外層迴圈(for (int j = 1; j <= 9; j++)
):
- 我的思路:(1) 成功跑出第一列後,要換列。(2) 預期要連續跑 9 列,所以再做一個外層迴圈,定義變數為
j
(一般來說是外層i
內層j
,但我的示範是保留原本的紀錄) - 這個迴圈會讓變數
j
從 1 遞增到 9。 - 這是外層迴圈,它控制了每一列的開始。
總結
- 外層迴圈控制行數(從 1 到 9)。
- 內層迴圈控制每行中的列數(從 1 到 9)。
- 函數宣告與作用域賦予乘積的功能。