指標引數傳遞實質及二級指標使用
水平有限,如有錯誤,歡迎指正,謝謝。
先看兩個程式:
耐心仔細看,應該能理解。
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就是同一個變數);所以要對實參傳進來的指標進行直接操作的話,就可以使用二級指標,把實參的地址傳給二級指標,通過二級指標去改變一級指標的值。