在開始學習程式時,迴圈(loops)是你會經常使用的工具。想像一下,不用一遍又一遍地手動寫程式,而是讓程式自動完成這些重複的工作。本篇會用有趣的例子解釋三種常見的迴圈:for
、while
和do...while
,計算 1 到 100 的總和,再進階運用函數寫出九九乘法表。學會迴圈跟函數的概念,不僅在 C 語言使用,也可以在其他語言靈活運用來簡化程式。
在寫程式時我們先拆解成小問題,成 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 (起始值; 停止條件 ; 每次執行完迴圈後變數更新) {
// 要重複做的事情
}
;
獲得這兩個邏輯後,我第一次寫程式居然跑出的答案是 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 (條件);
同樣計算 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 (條件) {
// 當條件成立時,重複執行這裡的程式碼
}
#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);
}
}
既然已經熟悉三種迴圈的使用方式,我們來練習寫出九九乘法表。
思路拆解:(可參考一遍我寫的,稍後會詳細介紹思考流程)
#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;
}
作用域指的是一個變數可以在哪裡被存取。
i
和 j
它們只在 for
迴圈裡有效,這叫做「區域變數」。這些變數只會在迴圈裡面被使用,出了迴圈就消失。mul
函數裡的變數 a
和 b
也是區域變數,它們只在 mul
裡面有效,函數執行完畢後它們就不再存在了,所以我們還需要做函數宣告。在 int mul(int a, int b);
這裡,這是一個函數宣告,寫在主程式前面。告訴程式「等一下我會用到 mul
這個函數」。這樣程式知道這個函數的存在,即使它的細節在後面才寫出來。
當一個變數作用於全域,也就是適用於整個程式,會使追蹤變數的來源變得很困難。當你想知道其中一個數值是怎麼來的,你需要檢查整個程式。
因為全域變數可以在任何地方被修改,當出現問題時,你很難快速確定是哪個部分的程式修改了這個變數。
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 ....
}
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++)
):
j
(一般來說是外層 i
內層 j
,但我的示範是保留原本的紀錄)j
從 1 遞增到 9。