跳转至

函數

函數的聲明

編程中的函數(function)一般是若干語句的集合。我們也可以將其稱作「子過程(subroutine)」。在編程中,如果有一些重複的過程,我們可以將其提取出來,形成一個函數。函數可以接收若干值,這叫做函數的參數。函數也可以返回某個值,這叫做函數的返回值。

聲明一個函數,我們需要返回值類型、函數的名稱,以及參數列表。

1
2
3
4
// 返回值類型 int
// 函數的名稱 some_function
// 參數列表 int, int
int some_function(int, int);

如上圖,我們聲明瞭一個名為 some_function 的函數,它需要接收兩個 int 類型的參數,返回值類型也為 int。可以認為,這個函數將會對傳入的兩個整數進行一些操作,並且返回一個同樣類型的結果。

實現函數:編寫函數的定義

只有函數的聲明(declaration)還不夠,他只能讓我們在調用時能夠得知函數的 接口 類型(即接收什麼數據、返回什麼數據),但其缺乏具體的內部實現,也就是函數的 定義(definition)。我們可以在 聲明之後的其他地方 編寫代碼 實現(implement)這個函數(也可以在另外的文件中實現,但是需要將分別編譯後的文件在鏈接時一併給出)。

如果函數有返回值,則需要通過 return 語句,將值返回給調用方。函數一旦執行到 return 語句,則直接結束當前函數,不再執行後續的語句。

1
2
3
4
5
6
7
8
9
int some_function(int, int);  // 聲明

/* some other code here... */

int some_function(int x, int y) {  // 定義
  int result = 2 * x + y;
  return result;
  result = 3;  // 這條語句不會被執行
}

在定義時,我們給函數的參數列表的變量起了名字。這樣,我們便可以在函數定義中使用這些變量了。

如果是同一個文件中,我們也可以直接將 聲明和定義合併在一起,換句話説,也就是在聲明時就完成定義。

1
int some_function(int x, int y) { return 2 * x + y; }

如果函數不需要有返回值,則將函數的返回值類型標為 void;如果函數不需要參數,則可以將參數列表置空。同樣,無返回值的函數執行到 return; 語句也會結束執行。

1
2
3
4
5
6
7
void say_hello() {
  cout << "hello!\n";
  cout << "hello!\n";
  cout << "hello!\n";
  return;
  cout << "hello!\n";  // 這條語句不會被執行
}

函數的調用

和變量一樣,函數需要先被聲明,才能使用。使用函數的行為,叫做「調用(call)」。我們可以在任何函數內部調用其他函數,包括這個函數自身。函數調用自身的行為,稱為 遞歸(recursion)。

在大多數語言中,調用函數的寫法,是 函數名稱加上一對括號 (),如 foo()。如果函數需要參數,則我們將其需要的參數按順序填寫在括號中,以逗號間隔,如 foo(1, 2)。函數的調用也是一個表達式,函數的返回值 就是 表達式的值

函數聲明時候寫出的參數,可以理解為在函數 當前次調用的內部 可以使用的變量,這些變量的值由調用處傳入的值初始化。看下面這個例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void foo(int, int);

/* ... */

void foo(int x, int y) {
  x = x * 2;
  y = y + 3;
}

/* ... */

a = 1;
b = 1;
// 調用前:a = 1, b = 1
foo(a, b);  // 調用 foo
            // 調用後:a = 1, b = 1

在上面的例子中,foo(a, b) 是一次對 foo 的調用。調用時,foo 中的 xy 變量,分別由調用處 ab 的值初始化。因此,在 foo 中對變量 xy 的修改,並不會影響到調用處的變量的值

如果我們需要在函數(子過程)中修改變量的值,則需要採用「傳引用」的方式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void foo(int& x, int& y) {
  x = x * 2;
  y = y + 3;
}

/* ... */

a = 1;
b = 1;
// 調用前:a = 1, b = 1
foo(a, b);  // 調用 foo
            // 調用後:a = 2, b = 4

上述代碼中,我們看到函數參數列表中的「int」後面添加了一個「&(and 符號)」,這表示對於 int 類型的 引用(reference)。在調用 foo 時,調用處 ab 變量分別初始化了 foo 中兩個對 int 類型的引用 xy。在 foo 中的 xy,可以理解為調用處 ab 變量的「別名」,即 foo 中對 xy 的操作,就是對調用處 ab 的操作。

main 函數

特別的,每個 C/C++ 程序都需要有一個名為 main 的函數。任何程序都將從 main 函數開始運行。

main 函數也可以有參數,通過 main 函數的參數,我們可以獲得外界傳給這個程序的指令(也就是「命令行參數」),以便做出不同的反應。

下面是一段調用了函數(子過程)的代碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// hello_subroutine.cpp

#include <iostream>

void say_hello() {
  std::cout << "hello!\n";
  std::cout << "hello!\n";
  std::cout << "hello!\n";
}

int main() {
  say_hello();
  say_hello();
}