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