函式是什麼
函式就像一台大機器,接受輸入(有時也可以沒有輸入),回傳輸出給使用者。發現了嗎?其實到目前為止,你寫的每一個程式,都是函式,因為它接收了一些東西,並輸出結果。
適當的使用函式,可以使程式碼更為簡潔,可讀性更高。不僅這樣,在下一章的遞迴中,函式這個工具是無法替代的必要基礎,因此,讓我們一起來看看函式怎麼寫吧!
語法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<bits/stdc++.h> using namespace std;
int biggest(int a, int b, int c){ if(a >= b && a >= c) return a; else if(b >= a && b >= c) return b; else return c; }
int main(){ int x, y, z; cin >> x >> y >> z; cout << biggest(x, y, z) << '\n'; }
|
輸入:
輸出:
這個函式接收三個數,並回傳三者的最大值。下面就來一一介紹每個片段的意思。
回傳值型態
位於函式最前方的 int,代表此函式的回傳值型態。也就是,我回傳(return)給使用者的東西會是什麼型態的。
在本範例中,函式接收三個整數,並回傳三者的最大值,這個最大值自然也是一個整數,所以我們使用 int 作為回傳值。
常見的回傳值就跟變數型態一樣,int、double、string、char、bool 全部都可以作為回傳值。
不過,函式也不一定要 return 東西,如果我只是想單純執行某些指令,不須回傳,就可以使用 void 作為型態,結束時只需寫 return 即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include<bits/stdc++.h> using namespace std;
void print(int a, int b, int c){ cout << "a = " << a << "\nb = "<< b << "\nc = " << c << '\n'; return; }
int main(){ int x, y, z; cin >> x >> y >> z; print(x, y, z); }
|
回傳值與輸入值,並沒有一定要是相同型態,輸入值為 int,也可以回傳 string。
名稱
範例中的名稱即為函式名稱,命名上與變數命名相同,不要碰到關鍵字,其他都可以。
但建議函式最好命名成讓人知道這個函式在做什麼,避免 ‘a’, ‘owo’ 之類的名稱出現。
參數
由括號包住的部分,即為傳入之參數,數量沒有限制,要幾個就幾個。不必與傳入時變數名稱相同,但數量一定要相同,不然會CE。
參數需要宣告型態,傳入時,需與參數型態相同,如果不相同,傳入的數字會自動轉型,如果無法轉型,會導致 CE。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<bits/stdc++.h> using namespace std;
int biggest(int a, int b, int c){ if(a >= b && a >= c) return a; else if(b >= a && b >= c) return b; else return c; }
int main(){ double x, y; cin >> x >> y; cout << biggest(x, y, 1.12) << '\n'; }
|
輸入:
輸出:
於進入函式時,double 已被轉型為 int,因此在 biggest 函式中的 a, b, c,是 2, 2, 1。
傳入的方式,其實是將原資料複製一次以後,再拿複製出來的版本給函式中的變數,因此,函式中不管對變數進行了什麼操作,對原資料都是沒有影響的。將變數命名為相同名稱也沒有用,即使有著相同名稱,由於作用範圍不同,他們也不是相同的變數。這部分將於之後”變數的作用範圍”中進一步的探討。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include<bits/stdc++.h> using namespace std;
void nothing_happened(int a){ a -= 2; a = a * 9; }
int main(){ int a = 3; nothing_happened(a); cout<<a<<'\n'; }
|
輸出:
不過,其實也是有辦法直接傳那個變數的,使用參照(reference),可以告訴程式,我不要複製,我就是要直接改這個變數!方法也很簡單,只需要加一個&,變成這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include<bits/stdc++.h> using namespace std;
void nothing_happened(int a){ a -= 2; a = a * 9; }
void something_happened(int &a){ a -= 2; a = a * 9; }
int main(){ int a = 3; nothing_happened(a); cout<<a<<'\n'; something_happened(a); cout<<a<<'\n'; }
|
輸出:
函式也可以沒有輸入:
1 2 3 4 5 6 7 8 9 10
| #include<bits/stdc++.h> using namespace std;
void say_hello(){ cout << "hello!\n"; }
int main(){ say_hello(); }
|
輸出:
函式主體
大致上,你平常怎麼做,函式裡就怎麼寫。if/else、迴圈…全部都跟在main函式裡一樣用就好。
想特別提出來講的一點是,可以使用哪些變數。在main函式中的變數,是無法使用的,函式可以使用的變數,是傳入的參數、在函式裡自己宣告的變數、以及全域且在函式之前宣告的變數。例如:
1 2 3 4 5 6 7 8 9 10 11
| #include<bits/stdc++.h> using namespace std;
void say_A(){ cout << A << "\n"; }
int main(){ int A = 5; say_A(); }
|
這是一個會 CE 的程式碼,因為 A 是屬於 main 函式的。
你可以這樣寫:
1 2 3 4 5 6 7 8 9 10 11
| #include<bits/stdc++.h> using namespace std;
void say_A(int A){ cout << A << "\n"; }
int main(){ int A = 5; say_A(A); }
|
將 A 作為參數傳入,或者是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include<bits/stdc++.h> using namespace std;
int A = 5;
void say_A(){ cout << A << "\n"; }
int main(){ say_A(); A = 3; say_A(); }
|
輸出:
將 A 宣告於全域變數,這樣它就是 “大家的A” 了!
這也是建議陣列應該宣告於全域變數的原因,這樣在使用函式時比較方便。雖然陣列也能透過傳遞指標的方式作為參數傳入,但較為麻煩,因此建議大型陣列一律開在全域,以方便函式使用。
回傳值
return即為回傳的意思,與break等不同,return會直接結束整個函式並回傳其後方接著的值給呼叫它的人。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<bits/stdc++.h> using namespace std;
int biggest(int a, int b, int c){ if(a >= b && a >= c) return a; else if(b >= a && b >= c) return b; else return c; }
int main(){ int x, y; cin >> x >> y; int n = biggest(x, y, 9); }
|
main 函式最後一行,其意思即為,將 n 設為 biggest(x, y, 9) 的回傳值。
回傳值的型態,必須與宣告的回傳型態相同,否則一樣會進行型態轉換,無法轉換則會 CE 。
在型態是 void 的函式中,return 的意思僅為結束函式,畢竟沒有回傳值嘛。
函式中呼叫另一函式
比較少見的情況,不過如果 A 函式中需要用到 B 函式,那 B 函式必須在 A 函式以前宣告,否則會 CE,因為在編譯到 A 函式內部的時候,編譯器還不知道有 B 函式,所以會跳錯誤。
錯誤範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<bits/stdc++.h> using namespace std;
void A(){ for(int i=0;i<5;i++){ B(i); } }
void B(int x){ cout<<x<<"\n"; }
int main(){ A(); }
|
正確:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<bits/stdc++.h> using namespace std;
void B(int x){ cout<<x<<"\n"; }
void A(){ for(int i=0;i<5;i++){ B(i); } }
int main(){ A(); }
|
那如果,A 函式中需要用到 B 函式,B 函式中也需要用到 A 函式呢?
這其實就有點遞迴的感覺了,下一章會詳細介紹,不過我們先來解決語法問題。
其實,在宣告函式時,可以先不寫內容,之後再寫!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include<bits/stdc++.h> using namespace std;
void A(); void B(int x);
void A(){ for(int i=0;i<5;i++){ B(i); } }
void B(int x){ if(x == 4) A(); cout<<x<<"\n"; }
int main(){ A(); }
|
只是執行這段程式會停不下來就是了XD。
使用時機
避免重複寫多次相同的式子。例如,我們需要輸入a, b, c,並計算其三次方加一時,比起這樣:
1 2 3 4 5 6 7 8 9 10 11
| #include<bits/stdc++.h> using namespace std;
int main(){ int a, b, c; cin >> a >> b >> c; a = a * a * a + 1; b = b * b * b + 1; c = c * c * c + 1; cout << a << b << c << '\n'; }
|
這樣寫可以避免重複:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include<bits/stdc++.h> using namespace std;
int func(int x){ return x * x * x + 1; }
int main(){ int a, b, c; cin >> a >> b >> c; a = func(a); b = func(b); c = func(c); cout << a << b << c << '\n'; }
|
於大型程式,例如超過200行的程式裡,使用函式將大問題拆解成小問題分別處理,增加程式的可讀性。
進行遞迴
下一章再見!
練習
TOJ 170
AC Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| #include<iostream> using namespace std;
int A(int n){ for(int g=1;g<=n;g++) { for(int j=1;j<=n-g;j++){ cout<<" "; } for(int j=1;j<=2*g-1;j++){ cout<<"*"; } cout<<"\n"; } }
int B(int n){ for(int z=1;z<=2;z++){ for(int g=1;g<=n;g++) { for(int j=1;j<=n-g;j++){ cout<<" "; } for(int j=1;j<=2*g-1;j++){ cout<<"*"; } cout<<"\n"; } } }
int C(int n){ for(int z=1;z<=n;z++){ for(int g=1;g<=z;g++) { for(int j=1;j<=n-g;j++){ cout<<" "; } for(int j=1;j<=2*g-1;j++){ cout<<"*"; } cout<<"\n"; } } }
int D(int n){ A(10*n); }
int E(int n){ for(int g=1;g<=n;g++) { for(int j=1;j<=n-g+2;j++){ cout<<" "; } for(int j=1;j<=2*g-1;j++){ cout<<"*"; } cout<<"\n"; } for(int z=1;z<=2*n+3;z++){ cout<<"#"; } cout<<"\n"; }
int I(int n){ for(int g=1;g<=n;g++) { for(int j=1;j<=n-g;j++){ cout<<" "; } for(int j=1;j<=2*g-1;j++){ cout<<"*"; } cout<<"\n"; } }
int T; char k;
int main() { cin>>T; for(int i=1, x;i<=T;i++){ cin>>c>>x; if (k=='A') A(x); else if (k=='B') B(x); else if (k=='C') C(x); else if (k=='D') D(x); else if (k=='E') E(x); else if (k=='F') A(2*x); else if (k=='G') A(3*x); else if (k=='H') A(7*x); else if (k=='I') I(4*x-1); cout << '\n'; } }
|