其實,對於c 或者c++ ,最難的一塊地方估計就是指標了。指標是強大的,但也是很多人載在這裡的地方。
前段時間寫了一篇文章《c
++之 陣列與指標的異同 》對c 和c ++中的指標做了乙個初步的講解。這次將講解一下指標作為函式引數傳遞的問題。
很多人對於指標的使用是有所了解的,但還是經常會載在指標的問題上,是因為還不夠了解指標的本質,其實如果了解指標的本質,對指標的使用也就一目了然了。
作為c 的初學者,經常會遇到指標作為函式引數傳遞的兩個經典的問題。這裡,我將透過指標的本質來來講解這兩個問題,這樣以後無論你遇到什麼樣的指標問題,如果你以這樣的方法來分析指標也許就迎刃而解了!
首先,第乙個問題是這樣的:
寫乙個函式,交換兩個引數中的值。
初學者往往會這樣寫:
void exchange(int x, int y)
之後,你會查詢資料了解到應該這樣寫:
void exchange(int *x, int *y)
第二個問題是,寫乙個給某個指標分配記憶體的函式:
初學者往往是這樣寫:
void my_malloc(void* p, int size)
然後又查在資料,知道應該這麼寫:
void my_malloc(void** p, int size)
雖然,網上很多這樣的討論,也有很多人做過很多的解釋,但始終都無法給出乙個令人一目了然,並可以長久記住的說法,這篇文章就是想試**決這樣的問題,給初學者乙個原理性的了解!
首先,一定一定記住一點,
指標和變數一樣,也是有位址的,只不過變數的值被解釋成乙個值,而指標的值被解釋成乙個位址。
下面,我們看一下**:
void main()
我們看這個函式的記憶體結構:
這是乙個函式的棧結構,我們可以看到,變數和指標都占用了4 個位元組。而且,由於我們對它們沒有初始化,所以變數x 和指標p 裡的內容都是隨機的,就是說x 的值是不確定的,p 有可能指向某個記憶體位址,如果現在對p 操作也許會導致程式崩潰。
其實,我們記住了,指標也是有位址的 這個概念,很多問題就迎刃而解了。
下面,我來分析一下,指標作為函式引數傳遞的情況。
如果,我們的**是這樣的,你看會怎麼樣:
int main(int argc, char* argv)
第二個要說的是:當給乙個函式的引數傳遞乙個變數是,這個變數是複製過去的。
對於第二點,我們在理解void exchange(int x, int y) 函式想交換這兩個變數的的值時就應該理解了。
例如:int a;
int b;
exchange(a,b);
不能交換a 和b 的值,因為此時exchange(a,b) 中的a 和b 並不是原來的a 和b 變數,它們只不過是被複製過去了。
有了這兩個概念,就不難理解指標作為函式引數傳遞的問題。
首先,我們來看下上面的**中的a 指標和p 指標的記憶體結構。
我們看到,當我們以a 作為func 函式的引數傳遞進去的時候,函式複製了這個指標,但這兩個指標的內容是一樣的,也就是說是指向同乙個記憶體,即10 。
如果你還不了解的話,我就通過一段**和測試再來說明:
[cpp]view plain
copy
print?
#include
void func(int* p)
int main(int argc, char *argv)
編譯:g++ -g -wall test1.cpp
執行:./a.out
輸出:*a = 10
&a = 0xbfd4447c
*p = 10
&p = 0xbfd44460
我們看到輸出,a 指向的位址的值和p 指向的位址裡的值是一樣的,都是10 。然而,對於指標a 和p 來說,它們自身的位址是不一樣的,所以我們看到,函式func 複製了指標a 給p ,它們的值一樣,但有不同的位址,是不同的指標。
我們再進一步:
[cpp]view plain
copy
print?
#include
void func(int* p)
int main(int argc, char *argv)
編譯輸出:
*a = 10
&a = 0xbfe1c77c
&*a = 0x94b6008
*p = 10
&p = 0xbfe1c760
&*p = 0x94b6008
我們可以進一步看到,a 指標所指向的值的位址和p 指標所指向的值的位址是一樣的,都是 0x94b6008 ,就如同上圖所示,為了加深印象,再看一下這個圖 ,然後再對比一下程式輸出 ,然後在體會一下我在上面提到的兩點 ,一點是:指標是有位址的 。另一點是:函式的引數是複製過去的 。
void exchange(int *x, int *y)
那麼這樣為什麼可以交換:
int a = 2;
int b = 3;
exchange(&a,& b);
上我們以a 和b 的位址傳遞給exchange 函式時,函式複製了這兩個位址,並賦值給x 和y 這個兩個指標,這兩個指標是指向變數a 和b 的,它們的圖形如下:
那麼,當我們反引用指標時:
int *p=x;
*x = *y;
*y = *p;
我們操作的是a 和b 裡面的變數的值,所以,我們交換a 和b 的值就成功了。
我們再來看下第二個問題:
void my_malloc(void* p, int size)
當這樣時:
int *a;
my_malloc(a, 10);
為什麼這個會失敗!
下面,我來分析一下:
當我們呼叫my_malloc(a, 10); 函式,而函式還沒執行到p = malloc(size); 語句時,情況是這樣的:
我們看到a 和p 的指標的值都是一樣的,都是指向某個不確定的位址。
這時,我們執行這個語句:
p = malloc(sizeof(int)*size);
我們把這個語句分開兩部分來看,乙個是先執行malloc(sizeof(int)*size) ,然後在執行賦值語句,把malloc(sizeof(int)*size) 的返回值付給p 。
第一步:先執行malloc(sizeof(int)*size) ;(這裡我們只考慮malloc 分配記憶體成功的情況)
第二步:把執行malloc(sizeof(int)*size) 的返回值付給了p ,如下圖:
由上圖,我們可以知道,這就是為什麼,我們還是不能給a 分配位址的了。
下面我們來分析這個:
void my_malloc(void** p, int size)
int *a;
my_malloc(&a , 10);
這樣執行,為什麼會成功!
我們看到,當執行函式
my_malloc(void** p, int size);
但還沒有執行
*p = malloc(sizeof(int)*size);
語句時,它們的記憶體結構圖如下所示:
其實這裡,我們可以把二維指標和一維指標當成和變數一樣,也是有位址的。只不過它的解釋不一樣而已。
變數:裡面的值是乙個數值。
那麼,我看著圖來解釋p :
p 裡面是乙個位址,這個位址是&a ,即是a 指標的位址值,而a 指標位址裡面的值也是個位址,這個位址是指向乙個不確定的地方,說得坳口,慢慢對比圖來理解就會好了!
執行malloc(size) 後的圖如下:
然後在執行賦值語句:
*p = malloc(sizeof(int)*size);
後,如下圖所示:
然後,我們就給指標a 分配記憶體成功了。
指標作為函式引數,C語言指標作為函式引數詳解
include void swap int a,int b 函式宣告 intmain void void swap int a,int b 大家想一下,執行這個程式是否能互換 i 和 j 的值?不能!i 還是3,j 還是5。因為實參和形參之間的傳遞是單向的,只能由實參向形參傳遞。被調函式呼叫完之後系...
c 之指標作為函式引數傳遞的問題
其實,對於c 或者c 最難的一塊地方估計就是指標了。指標是強大的,但也是很多人載在這裡的地方。前段時間寫了一篇文章 c 之 陣列與指標的異同 對c 和c 中的指標做了乙個初步的講解。這次將講解一下指標作為函式引數傳遞的問題。很多人對於指標的使用是有所了解的,但還是經常會載在指標的問題上,是因為還不夠...
C 指標作為函式引數介紹
void getptr int p,int num void main 我們想通過以上 來為ptr分配記憶體,結果如何呢,大家可以試一下,肯定是失敗的。同樣還有下面一段 來實現上面所述功能。void getptr int p,int num void main 第2段 可以實現為ptr動態分配記憶體...