第四十七章 指針(二)
「理論上應(yīng)該沒有問題的呀,要實(shí)現(xiàn)交換兩個(gè)變量的值容易得很!可是老爹的笑容讓我心里一陣發(fā)毛……這其中絕對(duì)有貓膩!」
面對(duì)老爹提出的疑問,原本信心滿滿地我瞬間失去了所有的底氣。一旁的小弦子雖然沒有吭聲,眉頭卻是皺得跟個(gè)小老頭兒似的。
有道是實(shí)踐是檢驗(yàn)檢驗(yàn)真理的唯一準(zhǔn)則,是騾子是馬,拉出來溜溜不就知道結(jié)果了么?
于是乎我三下五除二就寫好了swap函數(shù),為了謹(jǐn)慎起見,我還在函數(shù)中打印了交換前后的值。
以我如今的水品寫這種簡(jiǎn)單的代碼出錯(cuò)的幾率還是很小的,編譯運(yùn)行一氣呵成,小黑屏上打印出來的結(jié)果的的確確顯示兩個(gè)變量的值已經(jīng)成功交換了。
「這是幾個(gè)意思?難道老爹是在故布疑陣,唱空城計(jì)?」
小弦子看到我咬手指甲,面露沉思,于是也把腦袋湊過來看了一眼顯示屏,隨即開始捏著下巴冥思苦想。
「老爹,打印的結(jié)果明明顯示交換成功了呀!」
為了穩(wěn)妥起見,我還進(jìn)行了單步調(diào)試,可是一切都按照預(yù)期的流程執(zhí)行,結(jié)果也是預(yù)期的結(jié)果。最終我還是沒有發(fā)現(xiàn)問題到底出在哪里,于是只能向老爹求助。
「我可沒有讓你打印形式參數(shù)x,y的值,我說的是交換實(shí)際參數(shù)a和b的值,也就是這樣:
int a = 3;
int b = 4;
swap(a,b);
printf(“a =%d\n“, a);
printf(“b =%d\n“, b);」
「橋豆麻袋,形式參數(shù)……實(shí)際參數(shù)又是從哪兒冒出來的新名詞???」
「哦,這點(diǎn)是我的疏忽,在給你們講函數(shù)這一部分知識(shí)的時(shí)候忘了說。函數(shù)參數(shù)列表中的變量叫做形式參數(shù),比如說:
swap(int x, int y)
這里的x和y就就做形式參數(shù)。
而我們?cè)谡{(diào)用函數(shù)時(shí),為函數(shù)傳遞的參數(shù),這個(gè)就叫實(shí)際參數(shù),例如:
swap(a,b)
a和b就是實(shí)際參數(shù)。
而我剛剛提出的要求是要交換兩個(gè)是實(shí)際參數(shù)的變量,所以我們應(yīng)該監(jiān)控a和b在調(diào)用函數(shù)的前后的變化?!?p> 明白了老爹的意圖后,我對(duì)代碼進(jìn)行了更改,然后重新編譯、運(yùn)行。
「WHAT!」
看到小黑屏上打印的結(jié)果后,我沒有忍住喊了出來。因?yàn)槠聊簧洗蛴〉慕Y(jié)果顯示,a和b在調(diào)用swap函數(shù)前后居然沒有任何變化!我簡(jiǎn)直不敢相信自己的眼睛,這就好比你用計(jì)算器計(jì)算1+1的值,它妹的居然給你顯示1+1=3!
為了委托期間,我重啟了電腦又執(zhí)行了幾次,但是結(jié)果都是一樣的!換句話說,swap函數(shù)交換a和b的值失敗了,但是函數(shù)分明已經(jīng)是執(zhí)行了的!
「老爹,這是怎么回事兒啊?」
我實(shí)在是想不通這其中的癥結(jié)所在,只能夠向老爹提問。
「這就要從函數(shù)參數(shù)的傳遞問題開始說起了。
在C語言中,我們?cè)谡{(diào)用函數(shù)的時(shí)候,參數(shù)的傳遞實(shí)際上是采用的值的傳遞方式,什么意思呢?
就拿我們剛剛舉那個(gè)例子來說,當(dāng)我們?cè)谡{(diào)用swap(a,b)的時(shí)候,實(shí)際上是把a(bǔ)的值3復(fù)制了一份給變量x,而不是a這個(gè)變量換了一個(gè)名字,變成了x。同理,變量b的值4復(fù)制了一份給y。而x和y跟a和b沒有任何關(guān)系,所以在函數(shù)中無論我們對(duì)x和y做什么操作,都不會(huì)影響到a和b,所以對(duì)a和b進(jìn)行值交換沒有成功。」
「原來如此!那用指針為什么就能達(dá)到這個(gè)效果呢?」
我點(diǎn)了點(diǎn)頭,然后拋出了自己的疑問。
「誒,我什么時(shí)候說了用指針可以實(shí)現(xiàn)這個(gè)功能?」
「雖然任叔叔你沒有明說,但是你特地挖了一個(gè)坑讓我和玥玥往里面跳,肯定是別有目的的。既然現(xiàn)在咱們正好在學(xué)習(xí)指針,再加上剛剛我和玥玥都對(duì)指針的強(qiáng)大表示懷疑,你自然是要想辦法來說服我們呀!」
我還沒有開口,小弦子倒是按捺不住了。
「你們兩個(gè)……」
見自己的心思被我們看穿,老爹有些無語。
「好吧,那我先給你們演示用指針怎么來實(shí)現(xiàn)剛剛的功能:
swap(int* x, int* y)
{
int temp =*x;
*x =*y;
*y = temp;
}
int main(int argc, char const *argv[])
{
int a = 3;
int b = 4;
swap(&a,&b);
}
這樣的話,就能夠?qū)崿F(xiàn)交換a和b中的值了。」
雖然老爹一幅信誓旦旦的樣子,但我和小弦子剛剛才折戟沉沙,自然會(huì)先驗(yàn)證一遍。一切果然如同老爹所言,a和b中的內(nèi)容果然變了!那么why?how?
「好了,到這里我們正式進(jìn)入正題,只有我們將指針的知識(shí)講了,你們才會(huì)明白這其中的緣由。
剛剛我們說過,指針,其實(shí)就是指向一個(gè)變量的地址,這里的地址無非就是一個(gè)數(shù)字。通常的數(shù)據(jù),如int、double這種,我們都是放在一個(gè)變量中,然后使用它們。同樣的,如果我們使用這些地址,也得有相應(yīng)的數(shù)據(jù)類型類存放,而用來存放地址的變量,就叫做指針變量。
就拿swap函數(shù)的參數(shù)列表來說,int* x其實(shí)就是聲明了一個(gè)指向int類型的指針變量x。
什么意思呢?這其中其實(shí)包含了對(duì)內(nèi)存的讀寫信息。
舉個(gè)例子,假設(shè)你們班的教室編號(hào)是520,因?yàn)槟銈兝蠋熃虒W(xué)水平太高了,受到了廣大學(xué)生和家長(zhǎng)的認(rèn)可,于是大家都想進(jìn)入你們班學(xué)習(xí)。
于是你們班上的學(xué)生就越來越多,一個(gè)教室已經(jīng)裝不下了,這個(gè)時(shí)候校長(zhǎng)說把521、522、523這三間教室跟你教室打通。
但是這四間教室只有520這個(gè)編碼,如果這個(gè)時(shí)候有人問四年級(jí)二班在哪兒,我們就需要告訴人家,520編號(hào)的四間教室都是。
同樣,因?yàn)椴煌臄?shù)據(jù)類型占用的內(nèi)存字節(jié)不同,比如說變量a,它是一個(gè)int類型的,那么它在內(nèi)存中占4個(gè)字節(jié)。但是我們獲取它的指針地址時(shí),獲取到的實(shí)際上是它的首個(gè)字節(jié)的地址,因此我們?cè)谑褂弥羔樀臅r(shí)候就必須告訴計(jì)算機(jī)要取幾個(gè)字節(jié)。
int*的意思就是一個(gè)指向int數(shù)據(jù)類型的指針,這樣聲明計(jì)算機(jī)就知道,原來使用這個(gè)指針地址讀寫數(shù)據(jù)的時(shí)候要按照int類型的標(biāo)準(zhǔn)來,也就是要從當(dāng)前地址開始,操作四個(gè)字節(jié)的內(nèi)存。
同樣的,如果我們聲明一個(gè)執(zhí)行double類型的指針變量,就要用double*,以此類推。」