<style> html, body, .ui-content { background-color: #333; color: #ddd; } body > .ui-infobar { display: none; } .ui-view-area > .ui-infobar { display: block; } .markdown-body h1{ color: #9CCEF2; } .markdown-body h2{ color: #B1D6CA; } .markdown-body h3{ color: #F5F6B6; } .markdown-body h4, .markdown-body h5, .markdown-body h6 { color: #ddd; } .markdown-body h1, .markdown-body h2 { border-bottom-color: #ffffff69; } .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link { color: #fff; } .markdown-body img { background-color: transparent; } .ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a { color: white; border-left: 2px solid white; } .expand-toggle:hover, .expand-toggle:focus, .back-to-top:hover, .back-to-top:focus, .go-to-bottom:hover, .go-to-bottom:focus { color: white; } .ui-toc-dropdown { background-color: #333; } .ui-toc-label.btn { background-color: #191919; color: white; } .ui-toc-dropdown .nav>li>a:focus, .ui-toc-dropdown .nav>li>a:hover { color: white; border-left: 1px solid white; } .markdown-body blockquote { color: #bcbcbc; } .markdown-body table tr { background-color: #5f5f5f; } .markdown-body table tr:nth-child(2n) { background-color: #4f4f4f; } .markdown-body code, .markdown-body tt { color: #eee; background-color: rgba(230, 230, 230, 0.36); } a, .open-files-container li.selected a { color: #5EB7E0; } </style> ###### tags: `tgirc早修book` # 副程式 副程式又可稱作「自定義函式」,也就是自訂一個函式。 ## 函式 函式就是像 `sort()`、`getline()`、`find()` 這些,它們各自有特定的功能。 舉 `find()` 為例,如果字串 str 內容為 “Hello world!”,想在字串 str 裡尋找 “world”,並輸出它的位置,那程式碼會是這樣: ```cpp= str = "Hello world!"; cout << "position: " << str.find("world", 0); ``` 此行程式執行結果會是: ``` position: 6 ``` 當在程式碼中,經常需要使用到某段程式,但又沒有內建函數可以使用時,就可以自己撰寫一份函數出來。 每當要使用時直接呼叫它,就不用重複撰寫多次重覆的程式碼,修改時也只需要修改函數中的程式碼就好 ## 基本架構 這裡舉一個 a+b 的副程式 `plus(a, b)` 為例,呼叫時傳入 a 與 b,則此程式回傳 a+b 的值 副程式的本體會長這樣: ```cpp= int plus(int a,int b){ //開頭的 int,代表這個名叫 plus 的函式回傳值會是 int 型態 //括號內是它的兩個參數 a 與 b //不管傳入的變數原本叫什麼,在函式內都以 a 與 b 為名稱進行操作 int sum; //在函式裡也能宣告變數 sum = a+b; return sum; //return 後的 sum 即為此函式的回傳值 } ``` ## 宣告與呼叫 函數的宣告有兩種方式,分別是把函數主體放在主函數前與主函數後 註:主函數就是 `int main()` * <font color="F5F6B6">**在主函數前**</font> 在主函數前,就先把整個函式本體寫完,優點是不用先宣告一次 ```cpp= #include<iostream> using namespace std; //在主程式前宣告 int plus(int a, int b){ return a+b; } //主程式 int main(){ cout << plus(10,5); return 0; } ``` * **<font color="F5F6B6">**在主函數後**</font>** 先在主函數前宣告一次,主函數後再補上函數整體 ```cpp= #include<iostream> using namespace std; //切記!要先在前面這裡宣告過,不然程式會不認得 plus() int plus(int, int); //這裡可以不用宣告參數名稱,只要把每個參數的型態依序列出就好 //主函數 int main(){ cout << plus(10,5); return 0; } //函數 int plus(int a,int b){ return a+b; } ``` 若要呼叫寫好的函數,只需要在使用時呼叫函數名字,再加上 ( ) 傳遞參數即可 只是要注意傳遞的參數型別與參數的數量是否和當初宣告函數時相同 ## void `return` 值有 `int`、`char`、`bool` 等各種型態。 但如果只是要讓副程式做個動作,而不希望它回傳任何東西,那就把函數回傳型態型態設為 void 。 以交換兩變數的值為例: ```cpp= #include<iostream> using namespace std; void swap(int a,int b){ int temp; temp = a; a = b; b = temp; cout << "a: " << a << " b: " << b << '\n'; } int main(){ int a = 2, b = 3; cout << "a: " << a << " b: " << b << '\n'; cout << '\n'; cout << "swap:\n"; swap(a, b); return 0; } ``` ![swap](https://i.imgur.com/bqWcjwW.png) ## 其他操作 ### return 有回傳值的程式,會用一個 return 來回傳值。反之,程式只要碰到 return 就會回傳並結束 一個程式只會 return 一個值,縱使有很多條 return,但它只會回傳第一個 return,可以善用 return 來進行條件判斷與程式中止 以下示範return的運作模式: ```cpp= int test(){ return 6; return 10; } int main(){ cout << test(); return 0; } ``` 輸出如下: ``` 6 ``` `test()` 的 return 值為 6 而非 10。 這是因為在 `test()` 碰到第一次 return 時,就會回傳並中止整個副程式,後面的 `return 10;` 這行自然不會被處理到 ### 函數之間可以互相呼叫 一個函數可以呼叫另一個函數,甚至可以呼叫自己,這在之後的遞迴裡會很常使用。 ```cpp= int function_1(int a){ //略 } int function_2(int a){ int n; function_1(n); //可以呼叫其他副程式 function_2(n); //甚至可以呼叫自己! } ``` ### 一維陣列傳入函數 宣告時一樣要加上中括號,但不用加上長度,就算加上後也會被省略。 陣列在傳遞時以指標方式傳遞,而函數中對陣列的操作會保留下來,也就是若更動陣列中的某個值,那原本的陣列也會一同被更動。 ```cpp= int function(int x[],char C); //宣告時加上中括號 int main(){ int arr[10] = {...}; char n; function(arr, n); //呼叫時直接以指標傳入 . . . return 0; } ``` ### 多維陣列傳入函數 因為多維陣列在位址的計算上,會需要第一維之外每一維的大小 所以在宣告時,必須加上其他維的大小,並且要與宣告時給定的值相同。 ```cpp= #include<iostream> using namespace std; int sum(int (*x)[3], int n){ int num = 0; for(int i = 0; i < n; i++){ for(int j = 0; j < 3; j++){ num += x[i][j]; } } return num; } int main(){ int a[2][3] = { {1,1,1}, {2,2,2} }; int b[3][2] = { {1,1}, {2,2}, {3,3} }; cout << sum(a, 2) << '\n'; cout << sum(b, 3) << '\n'; //compile error return 0; } ``` ## 變數的生命週期 根據變數宣告的位置不同,可分為 <font color="F5F6B6">**全域變數**</font> 跟 <font color="F5F6B6">**區域變數**</font> ### 全域變數 宣告在函數外且不在任何區域中的變數,就叫做全域變數。 全域變數的初始值為 0,並且所有的函數都可以對其進行取用。 ```cpp= #include <iostream> using namespace std; int b=3; int f(int n,int m){ return n+m; } int main(){ int a=10; cout<<a-b<<'\n'; cout<<f(a,b)<<'\n'; return 0; } ``` ![](https://i.imgur.com/n9ECrG3.png) ### 區域變數 區域變數只在宣告的區域內才有效,使用不當可能會 overflow ,或是沒有指定初始值。 因為區域變數只在宣告的區域中有效,所以在區域外可以使用相同的變數名稱(區域內的可以當成專屬於該區域)。 ```cpp= for(int i = 0; i < 3; i++){ string s = "ouob"; cout << s << '\n'; //可以取用同區域內的 s } cout << i << '\n'; //compile error cout << s << '\n'; //compile error ``` 上述程式碼中, `i` 跟 `s` 都是 for 迴圈區塊內的區域變數,所以離開了宣告範圍後就無效了,再使用就會 CE。