0%

0-9 函式

函式是什麼

函式就像一台大機器,接受輸入(有時也可以沒有輸入),回傳輸出給使用者。發現了嗎?其實到目前為止,你寫的每一個程式,都是函式,因為它接收了一些東西,並輸出結果。

適當的使用函式,可以使程式碼更為簡潔,可讀性更高。不僅這樣,在下一章的遞迴中,函式這個工具是無法替代的必要基礎,因此,讓我們一起來看看函式怎麼寫吧!

語法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<bits/stdc++.h>
using namespace std;

//一般寫在main函式前

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';
}

輸入:

1
5 8 9

輸出:

1
9

這個函式接收三個數,並回傳三者的最大值。下面就來一一介紹每個片段的意思。

回傳值型態

位於函式最前方的 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;

//一般寫在main函式前

void print(int a, int b, int c){
cout << "a = " << a << "\nb = "<< b << "\nc = " << c << '\n';
return; // (void 函式可省略)
}

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;

//一般寫在main函式前

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';
}

輸入:

1
2.35 2.54

輸出:

1
2

於進入函式時,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;

//一般寫在main函式前

void nothing_happened(int a){
a -= 2;
a = a * 9;
}

int main(){
int a = 3;
nothing_happened(a);
cout<<a<<'\n';
}

輸出:

1
3

不過,其實也是有辦法直接傳那個變數的,使用參照(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;

//一般寫在main函式前

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
9

函式也可以沒有輸入:

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();
}

輸出:

1
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();
}

輸出:

1
2
5
3

將 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;

//一般寫在main函式前

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。

使用時機

  1. 避免重複寫多次相同的式子。例如,我們需要輸入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';
    }
  2. 於大型程式,例如超過200行的程式裡,使用函式將大問題拆解成小問題分別處理,增加程式的可讀性。

  3. 進行遞迴

    下一章再見!

練習

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";
}
}
//1
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";
}
}
}
//2
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";
}
}
}
//3
int D(int n){
A(10*n);
}
//4
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";
}
//5
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';
}
}