學習C++ -> 進一步了解函數
一、函數的參數傳遞
1>. 值傳遞
值傳遞是指向函數傳遞自身的一個副本, 也可以認為是自身的克隆, 他最大的一個特點就是函數對傳入的副本進行操作不會影響到實參的本身, 看一個示例, 我們想通過函數來改變一個變量的值:
1 #include<iostream> 2 3 using namespace std ; 4 5 void fun(int val) 6 { 7 val = 100 ; //將傳進來的參數的值更改為100 8 } 9 10 int main() 11 { 12 int n = 0 ; 13 fun(n) ; //嘗試通過函數改變n的值 14 cout<<"n = "<<n ; 15 16 return 0 ; 17 }
輸出:
n = 0 Process returned 0 (0x0) execution time : 0.031 s Press any key to continue.
通過輸出的結果可以看到, n的值並未發生任何改變。
2>. 指針傳遞
一個有效的指針變量中存放的是該變量的地址, 向函數參數中傳遞指針就意味着函數能根據指針指向的地址來找到這個變量的實際存儲位置, 對實際位置中的內容進行操作, 實參的值自然就會跟着改變, 看一個示例:
1 #include<iostream> 2 3 using namespace std; 4 5 void fun(int *p) 6 { 7 *p = 100 ; //通過指針p改變實參的值 8 } 9 10 int main() 11 { 12 int n = 0 ; 13 int *p = &n ; 14 fun(p) ; //將指向變量n的指針傳入到函數fun 15 cout<<"n = "<<n ; 16 17 return 0 ; 18 }
輸出:
n = 100 Process returned 0 (0x0) execution time : 0.625 s Press any key to continue.
很明顯, 將指針作為參數傳遞到函數后, 函數對該指針進行操作就直接影響到了實參n的值。
3>. 引用傳遞
引用傳遞函數也具有"異地"操作實參的能力, 與指針相比倒是省去了定義指針變量所開辟的空間, 並且在使用時的風險比指針要低, 引用不是實參的"副本", 而是真實的指向實參值在內存中的地址, 所以對引用進行操作同樣也等同於對實測進行操作。
1 #include<iostream> 2 3 using namespace std; 4 5 void fun(int &r) 6 { 7 r = 100 ; //通過引用改變實參的值 8 } 9 10 int main() 11 { 12 int n = 0 ; 13 int &rn = n ; 14 fun(rn) ; //傳遞n的引用rn 15 cout<<"n = "<<n ; 16 17 return 0 ; 18 }
二、函數的默認參數
所謂的默認參數是指在調用時可以省略該參數, 即不傳入參數, 一個示例:
1 #include<iostream> 2 3 using namespace std; 4 5 void fun(int n = 100) //聲明參數列表時將形參n的值賦值為100, 表示默認值 6 { 7 cout<<"n = "<<n<<endl ; 8 } 9 10 int main() 11 { 12 cout<<"使用默認參數:" ; 13 fun() ; //使用默認參數形式調用函數 14 15 cout<<"使用自定義參數:" ; 16 fun(200) ; //向參數內傳遞值200 17 18 return 0 ; 19 }
輸出:
使用默認參數:n = 100 使用自定義參數:n = 200 Process returned 0 (0x0) execution time : 0.766 s Press any key to continue.
默認參數的一大特點就是當不傳入參數時函數自動調用默認的參數值, 而當我們傳入參數時, 函數便不再理會默認的參數值, 轉而使用傳遞進來的參數值。
使用函數默認參數需要注意的問題:
參數默認必須是按從后向前的順序, 例如 fun(int b, int n = 100) 這樣是合法的, 而 fun(int n = 100, int b) 就是不合法的, 因為編譯器會將傳入的參數一一對應, 傳入1個參數時編譯器就會與默認參數對應起來, 此時b就沒有傳入參數, 也就自然報錯了。如果傳入兩個那么默認參數也就沒有存在的意義, 所以編譯器規定參數默認必須是按從后向前。
三、inline函數
inline 函數又稱 "內聯函數", 他主要運用在函數體重執行的語句較少但又會被頻繁調用的函數中, 例如調用函數輸出10000條"hello":
1 #include<iostream> 2 using namespace std; 3 4 void fun() 5 { 6 cout<<"Hello, world!\n" ; 7 } 8 9 int main() 10 { 11 int i ; 12 for(i=0; i<10000; i++) 13 fun() ; //短時間內將會被調用10000次 14 15 return 0 ; 16 }
fun函數只有一個功能, 就是輸出一條 "Hello, world!", 所以在main函數內我們通過控制for循環的方式調用10000次fun函數來實現這個功能, 那么這么做有什么不妥呢?
先來從大致上了解下函數的調用過程:
在一個函數中調用另一個函數, 系統首先會中斷原函數的執行, 在中斷時需要做一些現場保護工作, 記錄當前函數的中斷時的一些信息以便被調函數執行完后原函數的繼續執行, 記錄完成后才會把執行的流程轉向被調函數, 等被調函數執行完后再返回原函數繼續執行。
對於內容比較多的函數, 這點中斷時的這點時間開銷基本上是可以忽略不計的, 但是當調用十分頻繁執行的內容又較少時, 這點時間久不能忽視了, 即便目前的計算機性能越來越好, 但是在能夠更快的情況下我們還是盡量讓他更快一些。
inline函數的作用就是優化這些將會被頻繁調用的函數, 他就相當於把這些inline函數中的函數體直接復制到被調用的函數中一樣, 不再使其頻繁的中斷。 定義inline函數非常簡單, 只要在定義時在前面加上 inline 關鍵字即可, 例如把上面的 輸出 10000 次 "Hello, world!" 的程序改成 inline 函數的形式:
1 #include<iostream> 2 using namespace std; 3 4 inline void fun() 5 { 6 cout<<"Hello, world!\n" ; 7 } 8 9 int main() 10 { 11 int i ; 12 for(i=0; i<10000; i++) 13 fun() ; //短時間內將會被調用10000次 14 15 return 0 ; 16 }
在使用inline函數時需要注意的幾點問題:
1>. inline 函數的函數體語句不適合過多 ;
2>. inline 函數中不能有 循環、if或switch語句, 否則編譯器將會把該函數當做普通函數來處理 ;
3>. 一個文件中定義的inline函數不能再另一個文件中使用 。
四、函數的聲明
在前面的示例中, 我們采取的是直接定義一個函數, 然后再在main函數或其他函數中進行調用, 但是這樣做有一個問題, 當函數定義過多, 然后再互相嵌套調用時就非常容易報錯, 先看一個示例:
1 #include<iostream> 2 using namespace std; 3 4 int main() 5 { 6 fun() ; 7 8 return 0 ; 9 } 10 11 void fun() 12 { 13 cout<<"Hello, world!\n" ; 14 }
當我們把fun函數定義在main函數后時, 編譯時就會報錯: error: 'fun' was not declared in this scope 其意思就是'fun'這個標識符在這個文件中沒有聲明。或許有些讀者會想到, 把函數全部定義在main函數前不就行了嗎? 事實並不是這樣, 繼續看一個示例:
1 #include<iostream> 2 using namespace std; 3 4 void fun() 5 { 6 cout<<"Hello, world!\n" ; 7 print() ; //調用下面的print函數 8 } 9 10 void print() 11 { 12 cout<<"print\n" ; 13 } 14 15 int main() 16 { 17 fun() ; 18 19 return 0 ; 20 }
fun函數和print函數都是在main函數前, 並且fun調用了print函數, 這樣便又報錯 error: 'print' was not declared in this scope。
要避免這樣的錯誤, 只需要在使用前對函數進行下聲明即可, 例如示例一中的代碼添加上函數的聲明:

1 #include<iostream> 2 using namespace std; 3 4 void fun() ; //聲明fun函數 5 6 int main() 7 { 8 fun() ; 9 10 return 0 ; 11 } 12 13 void fun() 14 { 15 cout<<"Hello, world!\n" ; 16 }
示例二加上聲明:

#include<iostream> using namespace std; void fun() ; //聲明fun函數 void print() ; //聲明print函數 void fun() { cout<<"Hello, world!\n" ; print() ; //調用下面的print函數 } void print() { cout<<"print\n" ; } int main() { fun() ; return 0 ; }
函數聲明類似於函數定義, 只不過沒有進行實現, 函數聲明是一條語句, 所以要以分號進行結束, 在書寫函數聲明時, 就像只要把函數頭復制下來並在末尾添加上分號即可。
當函數有參數時, 聲明時同樣要將參數列表一同聲明, 例如定義一個max函數, 其中有兩個參數:
void max(int x, int y) { if(x>y) cout<<"max = "<<x ; else cout<<"max = "<<y ; }
那么在聲明時就應該 void max(int x, int y); 不過在聲明時編譯器並不要求連形參的名稱一並聲明, 因此上面的聲明形式還可以改寫為: void max(int, int);
--------------------
wid, 2013.01.25