---
# System prepended metadata

title: 6. 函式

---

# 函式

## 自訂函式

每個函式有返回型別、名稱、參數列表及主體。`void` 函式表示無返回值，`int`、`double` 等表示返回具體資料型別

```cpp
#include <iostream>
using namespace std;

// 定義無返回值的函式
void greet() {
    cout << "Hello from function!" << endl;
}
// 定義有返回值的函式
int add(int a, int b) {
    return a + b;
}

int main() {
    greet();  // 呼叫 greet 函式
    int result = add(5, 10);  // 呼叫 add 函式
    cout << "5 + 10 = " << result << endl;

    return 0;
}
```

:::danger
函式名稱不得用數字開頭
:::
:::spoiler 專案多載
如果你的project中有多個檔案，每個檔案中都有 “ABC” 這個function 。這時會導致編譯錯誤
    
    **多重定义：multiple definition of**
    
    解決辦法就是在每個function 前 加上靜態的 static
    
    公有與私有的詳細解說在下文
    
    [@Program](https://www.notion.so/16d9def5443d80658f3fed2b96357a3a?pvs=21) 
::: 

## 先宣告
```cpp=
#include <iostream>
using namespace std;

int main()
{
    int x, y;
    // ↓↓↓↓ 先宣稱「有這樣子的函數存在」但略過本體 ↓↓↓↓
    int f(int);

    cin >> x;
    y = f(x);
    cout << y << '\n';
    return 0;
}

// 宣告在全域的 f() 本體
int f(int x)
{
    int res = x*2 - 1;
    return res;
}
```

## 回傳值 return value

函數的本體可透過 `return` 來回傳一個值作為計算結果，

一旦執行到 `return` 函數便會停止執行。

```cpp=
int divide(int p, int q)
{
    if (q == 0)
    {
        cout << "NOOOOO you CANNOT divide 0!!\n";
        return 0;
        cout << "after return 0, this won't be executed.\n";
    }
    return p / q;
}
```

例如上述程式碼，在 q == 0 時便會因 `return 0;` 而結束執行，

## void 型別的函數
不回傳任何結果，可將型別宣告為 void。

它將不再能回傳任何值，也不能作為運算元參與計算。
仍然可以使用 return 結束函數執行，但不能回傳任何值。

```cpp=
void output(int t)
{
    if (t < 0)
    {
        cout << "negative.\n";
        // return 仍會結束執行，但不得傳回任何值，必須接著分號 ;
        return;
    }
    cout << "non-negative.\n";
}
```

上述程式碼宣告一個型別為 void 的函數，
將只能執行，不會回報任何值，無法參與計算。

```cpp=
// compile error
int a = output(-4);
// 僅能呼叫不能參與計算
output(-4);
```
# 指標
**變數的記憶體位址。**

&：取地址
*：解參考（存取該地址的值）
```cpp=
int a = 10;
int* p = &a;    // p 存的是 a 的記憶體位置
cout << *p;     // *p 代表「拿 p 指向的值」→ 輸出 10
```
用於
* 傳遞大量資料時避免複製
* 實作資料結構（linked list, tree）

# 變數生命週期
## 區域變數的遮蔽 shadowing

具有完全相同可見度和生命週期的變數，為免混淆不能取相同的名字；
但是不同的可以，範圍小的會遮蔽掉範圍大的。

因此，區域變數可以遮蔽全域變數，
:::warning
而更小更新的區域變數，可以遮蔽更大更舊的。
:::


```cpp=
#include <iostream>
using namespace std;

int n;

void gg(int t)
{
    // 遮蔽全域的 n
    int n;
    n = t;
    n *= 2;
    cout << "gg " << n << '\n';
}

int main()
{
    cin >> n;
    cout << "n: " << n << '\n';
    gg(n+5);
    cout << "n: " << n << '\n';
    // 遮蔽全域的 n
    for (int n=1; n<=3; n++)
    {
        cout << "for n: " << n << '\n';
    }
    // 脫離 n 的可見範圍，區域變數的 n 已消滅，被遮蔽的全域 n 重見天日
    cout << "after n: " << n << '\n';
    return 0;
}
```
# lambda匿名函式
格式
```cpp
[capture](parameter) -> return_type { body }
```
> capture是 **捕捉變數**: 你可以抓外面的參數進來
```cpp=
int x = 10;
auto f = [x](int y) { return x + y; }; // 捕捉 x 的值
cout << f(5); // 15
// 或用指標(下一章會說明)
int x = 10;
auto f = [&x](int y) { return x + y; };
x = 20;
cout << f(5); // 25

```
## 常見用法 - sort
可以搭配sort函式，根據不同條件排序
> sort可搭配vector, pair做出超強組合技，後面會說到。
```cpp=
// 降冪排序
sort(nums.begin(), nums.end(), [](int a, int b) {
    return a > b;
});
```