1. 程式人生 > >指標引數傳遞實質及二級指標使用

指標引數傳遞實質及二級指標使用

水平有限,如有錯誤,歡迎指正,謝謝。

先看兩個程式

耐心仔細看,應該能理解。

1

void test(char *p)

{

       printf("[test1][p]:%p.\n",p);

       printf("[test2][p]:%s.\n",p);

       p=(char *)malloc(10);

       strcpy(p,"ABCDE");

       printf("[test3]malloc之後.....\n");

       printf("[test4][p]:%p.\n",p);

       printf("[test5][p]:%s.\n",p);

       free(p);

}

int main()

{

       char b[6] = "abcde";

       char *a = b;

       printf("[main1][a]:%p.\n",a);

       printf("[main2][a]:%s.\n",a);

       test(a);

       printf("[main3][a]:%p.\n",a);

       printf("[main4][a]:%s.\n",a);

       return 0;

}

輸出結果:注意:(test函式的pde值已改變,main函式的a的值未改變)

main1][a]:0xbfeaaef6.
[main2][a]:abcde.
[test1][p]:0xbfeaaef6.
[test2][p]:abcde.
[test3]malloc之後.....
[test4][p]:0x8a52008.
[test5][p]:ABCDE.
[main3][a]:0xbfeaaef6.
[main4][a]:abcde.

2

void test(char **p)

{

       printf("[test1][p]:%p.\n",p);

       printf("[test2][*p]:%p.\n",*p);

       *p=(char *)malloc(10);

       strcpy(*p,"ABCDE");

       printf("[test3]malloc之後.....\n");

       printf("[test4][p]:%p.\n",p);

       printf("[test5][*p]:%p.\n",*p);

       printf("[test6][*p]:%s.\n",*p);

       free(*p);

}

int main()

{

       char b[6] = "abcde";

       char *a = b;

       printf("[main1][a]:%p.\n",a);

       printf("[main2][a]:%s.\n",a);

       test(&a);

       printf("[main3][a]:%p.\n",a);

       printf("[main4][a]:%s.\n",a);

       return 0;

}

輸出結果:注意:(test函式的pde值已改變,main函式的a的值也已經改變)

[main1][a]:0xbfaca776.
[main2][a]:abcde.
[test1][p]:0xbfaca770.
[test2][*p]:0xbfaca776.
[test3]malloc之後.....
[test4][p]:0xbfaca770.
[test5][*p]:0x9132008.
[test6][*p]:ABCDE.
[main3][a]:0x9132008.
[main4][a]:ABCDE.

問題:

1、形參和實參的概念?

2、引數傳遞的實質?

3、指標是什麼?為什麼要用指向指標的指標?

4、位什麼第一個程式不能改變a的值,而第二個程式卻可以?

先解釋一下形參和實參的概念:

實參:實實在在的引數,我們自己定義的,比如以上程式中指標a和陣列b都是實參,都是自己定義的,基本程式中花括號內定義的所有引數都是實參

形參:我們定義一個函式時,括號內的引數,比如以上程式中的char *p和char **p中的p就是形參,主要是為了讓實參的資料可以傳遞到函式內,供函式操作

2個問題:

         引數的傳遞分為兩種,一種是值傳遞,另一種是引用;我們這裡說的只主要是值傳遞,暫時不說引用傳遞;值傳遞又分為兩種:一種是實際的值傳遞,int型別的引數傳遞屬於實際值傳遞;另一種就是地址值傳遞,實參比實際地址傳遞給形參,比如指標就是地址值傳遞。

         這裡是引數傳遞的重點,當實參把實際值或者地址值傳遞給形參時,實際上不是直接使用實參,而是在棧去開闢記憶體空間copy一個副本,int a的副本是_a(_a=a),,char *p的副本是_p(_p=p), 所以函式內的操作都是對副本進行操作,改變形參的值不會影響實參的值,函式執行完就釋放副本開闢的空間。

3個問題:

         指標的概念,指標也是一個引數,和int及char類似,int 引數存放整數,char引數存放字元,指標存放的是一個地址而已;指標就是儲存一片記憶體的起始地址,知道這個指標就可以對這個指標指向的記憶體進行操作;指向指標的指標即二級指標儲存的是一級指標的地址,比如:


         p是一級指標,儲存的是a的地址;q是指向指標的指標(二級指標),儲存的是一級指標(p)的地址;q的內容就是0xbfaca770,*q的值即q指向的內容0xbfaca776,即*q仍然是一個地址,也就是指標p的內容,即*q=p,(好好理清楚),對*q操作就是對p指向的記憶體操作;為什麼要使用二級指標呢?下面會有講述:

好了,回到第4個問題,程式本身,為什麼會出現這種現象?

第一個程式:

我們先看看呼叫test函式前後,以及malloc之前和malloc之後的指標p的和指標a的地址以及指向情況:

之前的指向情況:(方塊上面是當前變數的地址,方塊內是當前變數的值)


之後的指向情況:     (方塊上面是當前變數的地址,方塊內是當前變數的值

從之前的情況可以看出,函式進行引數傳遞時,實參把地址傳給了形參p(p即是a的副本(_a),p=_a是為了表達更直觀,並不會產生變數名_a),兩個指標同時指向一片記憶體;使用malloc之後,空出一遍新記憶體並把地址賦給p,即p的指向改變,指向了新地址;所以test內對p的內容進行改變不會改變a的值。

第二個程式:

同樣先看看呼叫test函式前後,以及malloc之前和malloc之後的指標p的和指標a的地址以及指向情況:

之前的指向情況:

之後的情況:

 

         好了,我們來看一下,test函式的形參使用的是二級指標,我們把a的地址傳給了p,即p指向了a;指標a指向的是陣列b,即儲存的是b的首地址,見第二個程式第一張圖;二級指標p指向一級指標a,所以*p的值就是a的首地址,所以改變*p的內容就是改變a的內容,即改變a的指向;當malloc一段記憶體並把首地址儲存在*p的內容中,就是把malloc記憶體的首地址直接替換指標a原來的內容,所以a指標的指向發生了改變,見第二個程式第二張圖;所以改變*p就是改變a的值(要理解*p和a就是同一個變數);所以要對實參傳進來的指標進行直接操作的話,就可以使用二級指標,把實參的地址傳給二級指標,通過二級指標去改變一級指標的值。