一、質數問題
我們先來看一個問題,假設我們要寫一個程式,輸入一個正整數 N,如果 N 是質數則印出 Y,否則印出 N。我們看到這個問題,最直覺的想法假設系統有一個函數 P(N) 可以檢查 N 是不是質數,如果是則傳回 1 ,否則傳回 0,於是我們把它寫成下面的程式:
#include <stdio.h>
void main()
{
int a;
scanf("%d", &a);
if(P(a)) printf("Y\n");
else printf("N\n");
}
上面的程式很容易可以理解,但是系統並沒有提供這樣的一個函數 P(N),所幸有 C 語言中我們可以自定建立函數,定義函數的語法如下:
傳回值型態 函數名稱(參數一型態 參數一名稱, 參數二型態 參數二名稱, ....)
{
變數宣告
程式碼
return 傳回值;
}
上面的寫法是不是和 main() 很像,事實上 main() 本身也是一個函數,只不過 main() 是一個特別的函數,它會在程式開始的時候就自動執行。而要讓 main() 函數可以使用這個新的函數,要把這個函數定義在 main() 之前,或是定義在 main() 之後,而在 main() 之前補上這個函數的宣告。我們先用這函數定義在 main() 之前的寫法:
#include <stdio.h>
int P(int n)
{
int p=1, i;
if(n<2) return 0;
for(i=2; i<n; i++)
if( (n%i)==0 ) p=0;
return p;
}
void main()
{
int a;
scanf("%d", &a);
if(P(a)) printf("Y\n");
else printf("N\n");
}
上面黃色的部分是我們新增的部分,在函數 P 中,由於最小的質數是 2,所以先檢查它是否小於 2,如果是的話,則傳回 0。接下來我們假設它是質數(用變數 p 記錄),然後我們從 2 到 n-1 去對 n 取餘數,只要有一個餘數為 0,也就是 n 可以被那個數整除,那麼 n 就不是質數,就把 p 設為 0。最後,我們再把它是不是質數,也就是變數 p 的值用 return 傳回去。要注意的是,在函數 main 及 P 中的變數只限於該函數使用,至於我們用 P(a) 的方式呼叫函數 P,這個 a 到了 P 的時候會變成 n。接下來我們看到宣告函數的語法:
傳回值型態 函數名稱(參數一型態 , 參數二型態 , ...);
宣告函數的用意是告訴下面的程式這個函數的用法,也就是讓系統知道這個函數要有哪些參數、以及傳回值,所以在宣告函數的時候不需要寫程式碼,也不用加上參數的名稱(要加也可以),但是宣告函數那一行後面要加上分號 ; 代表結束。宣告函數之後,就可以使用該函數,我們把上面的程式改用先宣告後定義的寫法:
#include <stdio.h>
int P(int);
void main()
{
int a;
scanf("%d", &a);
if(P(a)) printf("Y\n");
else printf("N\n");
}
int P(int n)
{
int p=1, i;
if(n<2) return 0;
for(i=2; i<n; i++)
if( (n%i)==0 ) p=0;
return p;
}
至於我們常用的一些函數如 printf、scanf 等,都已經在它們的標頭檔 *.h 中宣告,所以只要把該標頭檔 include 進來後,就可以直接使用。另外,定義函數的同時也等於宣告了該函數,所以第一個例子就不用再寫一行函數宣告。而同一個程式中,可以對同一個函數寫很多次的宣告,但是只能寫一次的定義。
至於函數要定義在 main 之前還是之後,這個見人見智,寫在前面的優點是不用再宣告一次,寫在後面的優點是 main 主程式在前面,可以很快看懂這個程式(因為函數只要知道它的功能即可,不用去管它是如何寫出來的)。
上面的例子雖然不一定要用函數的寫法,但是使用自定函數有下列的好處:
- 讓主程式 main 更簡潔
- 可以分工完成各函數後再合併起來
最後,這先把程式分成幾個大步驟,再將這些大步驟分解成數個小步驟的寫法,我們稱之為「由上而下的寫法」(Top-Down),有助於讓一個複雜的問題簡單化。
二、數學星球的問題
公元 20xx年,數學星球派來大量的太空船,企圖佔領地球。將過地球人代表陳阿扁和數學星球人交涉後,數學星球人開出一個條件,如果地球人可以在 1 分鐘之內完成 10 題數學星球的三位數相加,數學星球人則立即離開地球,放棄入侵地球的計畫。他們的前三題是:
135+246
234+543
616+353
這時,小明為維護高中數學教師的尊嚴,挺身而出上台挑戰,只花了 20 秒不到就完成十題的答案:381、777、969....,可是,數學星球人卻用電擊把他電昏了。這個時候,同樣也是高中數學教師的小志發現數學星球人的 左手只有四根指頭、右手只有三根指頭,也就是說,數學星球是使用七進位。為了拯救地球,小志決定寫一個程式,輸入兩個七進位數字 A、B,並印出相加後的結果。
由於 C 語言沒有讀取七進位的函數,所以我們可以先用字串把這兩個數讀進來,接著用自定函數 R7 把該字串依照七進位的規則把它轉換成數字,接下來再用自定函數 P7 把兩數相加的結果轉換成七進位印出來,主程式 main 的部分如下:
void main()
{
char s1[10], s2[10];
int a, b;
scanf("%s %s", s1, s2);
a=R7(s1);
b=R7(s2);
P7(a+b);
}
接下來我們看到 R7 的寫法:
int R7(char s[])
{
int n=0, i;
for(i=0; i<strlen(s); i++)
n=n*7+s[i]-'0';
return n;
}
上面先假設該數 n 為 0,接下來將每個位數的字元減去 '0' 以取得該位數的數值,再將原先的數字乘以七後加上該位數的數值,重複這個步驟到最後一個位數為止,最後再將該數 n 傳回。接著看到 P7 的寫法:
void P7(int n)
{
int p[10], i, j=0;
while(n>0) {
p[j]=n%7;
n/=7;
j++;
}
for(i=j-1; i>=0; i--)
printf("%d", p[i]);
}
上面我們用一個整數陣列儲存轉換後的每一個位數,我們將該數對 7 做連續的除法,餘數存到陣列中,商數則繼續除下去,直到變成 0 為止,我們再從最高位數(陣列中最後面一個數字)往前面印回來。最後我們把這三個部分的程式合併起來:
#include <stdio.h>
#include <string.h>
int R7(char s[])
{
int n=0, i;
for(i=0; i<strlen(s); i++)
n=n*7+s[i]-'0';
return n;
}
void P7(int n)
{
int p[10], i, j=0;
while(n>0) {
p[j]=n%7;
n/=7;
j++;
}
for(i=j-1; i>=0; i--)
printf("%d", p[i]);
}
void main()
{
char s1[10], s2[10];
int a, b;
scanf("%s %s", s1, s2);
a=R7(s1);
b=R7(s2);
P7(a+b);
}
接下來,我想再想一個問題,如果數學星球人的手指頭是左手七根、右手八根,也就是他們是使用十五進位的話,那該怎麼辦?