2024-08-28|閱讀時間 ‧ 約 11 分鐘

C 語言| 迴圈寫法| 函數作用域

在開始學習程式時,迴圈(loops)是你會經常使用的工具。想像一下,不用一遍又一遍地手動寫程式,而是讓程式自動完成這些重複的工作。本篇會用有趣的例子解釋三種常見的迴圈:forwhiledo...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)和函數作用域

既然已經熟悉三種迴圈的使用方式,我們來練習寫出九九乘法表。

思路拆解:(可參考一遍我寫的,稍後會詳細介紹思考流程)

  1. 需要有「相乘」的功能。
  2. 先成功寫出第一列(row),注意唯一的變化是哪些數字。
  3. 再往外擴展外層迴圈。
#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. 作用域

作用域指的是一個變數可以在哪裡被存取。

  • 像是變數 ij 它們只在 for 迴圈裡有效,這叫做「區域變數」。這些變數只會在迴圈裡面被使用,出了迴圈就消失。
  • mul 函數裡的變數 ab 也是區域變數,它們只在 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)。
  • 函數宣告與作用域賦予乘積的功能。






分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.