對於c語言的引數傳遞都是值傳遞,當傳傳遞乙個指標給函式的時,其實質上還是值傳遞,除非使用雙指標。
在講雙指標之前,還是先講講關於c語言函式呼叫的本質。
函 數呼叫操作包括從一塊**到另一塊**之間的雙向資料傳遞和執行控制轉移。資料傳遞通過函式引數和返回值來進行,包括區域性變數的空間分配與**,都是通過 棧來實現的。絕大多數cpu上的程式實現使用棧來支援函式呼叫操作。棧被用來傳遞函式引數、儲存返回資訊、臨時儲存暫存器原有值以備恢復以及用來儲存區域性 資料。當函式a呼叫函式b的時候,會把a的變數和引數壓入到棧中,然後接著將b的變數和引數,區域性變數壓入到棧中。然後當a呼叫b是,b其實是在棧中取得 a傳遞的引數和值,從而達到值傳遞的效果。
以乙個交換2個數的值的函式呼叫為例。
view plain
copy to clipboard
print?
void
swap ( int *a, int *b )
int
main(int argc, char **argv)
那麼,這段**編譯成組合語言之後,除了會有**段,資料段,堆疊,那麼在呼叫的時候,會把main函式的引數變數壓入main函式的棧幀,然後接著會壓入swap函式的區域性變數和引數
那麼按照剛才上述理論,編譯成組合語言以後,這個圖就是函式呼叫的時候記憶體形態。
有了上面的圖和理論基礎,再來討論雙重指標的問題。當定義的時候,只有乙個*號的時候,我們叫它一級指標。**個星號的叫二級指標。
當我們使用一級指標的時候,我們試圖使用下述錯誤**來實現2個數交換
view plain
copy to clipboard
print?
void
swap ( int *a, int *b )
int
main ( int argc, char **argv )
這種方式按照理論上來說,是想通過呼叫swap函式,在swap函式內部,實現將交換&a,&b,即交換a和b的位址來達到目的。這樣絕對不可以。因為當把a,b的位址傳到swap函式之後,按照上述棧幀圖的結構來看,最終swap函式值通過棧指標來實現的,當swap使用的時候,還是把 a,b的位址複製到暫存器中才能運算。那麼,大家也許就明白了,swap把a,b的位址複製到暫存器中,然後運算,相當於抱著a,b的副本跑了,然後去操作,這些所有針對a,b副本的操作管main函式中的a,b什麼事? 當swap返回之後,這些暫存器或者是棧空間隨著swap的然會而釋放了,而 main函式的a,b沒發生任何變法。所以上述**是錯誤的,無法實現你想要的功能。
當我們用二級指標來實現上述功能的時候有就可以達到效果。
view plain
copy to clipboard
print?
void
swap ( int **a, int **b )
int
main ()
....
....
這個時候,你會發現就能實現達到交換的目的。
這就是雙指標神奇的功能,突破c語言傳值的概念。那麼,雙指標是如何達到效果的呢?
當我們申明 **a之後,其實雙指標變數a其實已經存在了。那麼在記憶體中的效果如下圖
那麼,p中放的是中間橋梁bridge的位址&bridge,則*p就是中間橋梁bridge的內容即是目標運算元的位址&income,從而**p就是目標運算元
再來看這個圖,p就是這裡**a種的a.當我們申明**p之後,p就已經存在了。其實這個bridge也已經存在了,那麼我們要做的就是bridge中放 我們要操作的數的位址。也就是&incom;那麼,其實這樣操作*bridge就是操作&incom也就是&b啊,這個一級指標 沒什麼區別啊。
請注意,對於一級指標,我們要操作的是就是b,那麼按照組合語言的規則,就要把b放到暫存器或者棧中去操作,我們相當於複製了乙個副本去操作,等我們操作完了,返回函式,這些暫存器,棧等都釋放了,main中什麼也沒發生。但是如果我們用雙指標,二級指標就不一樣了。我們操作的是bridge.我們只是機械的複製乙個bridge的內容到暫存器或者到棧中,而沒有實際的去複製imcom的內容,我們只是告訴bridge,你要指向乙個叫incom的位址, 也就是說,bridge的內容就是incom的位址,即&incom,通過bridge這個中間橋梁,所以,就達到目的了。所以,雙指標讓引數傳遞具有穿透力。
雙指標主要用在但我們想向乙個a函式傳遞引數的時候,但是我們希望在a內部對引數做任何修改都能儲存起來,那麼就是用雙指標吧。
舉個例子;
我們在做鍊錶的時候,我們肯定希望在用乙個函式creatlink(...)函式來增加鍊錶節點。那麼我們可以有2種方法來實現
第一種,用一級指標
view plain
copy to clipboard
print?
typedef struct nodelist;
node *create(list *l)
int main(...)
這樣做可以達到刪除增加節點的目的,但是,在任何情況下,我們的操作都得死死地抓住頭指標,也即是我們增加刪除節點後,任何對鍊錶長度的修改,我們都要 煉表頭指標返回,即 return head;所以,我們要通過這個函式最後獲得頭指標,抓住他,死死地抓住他,然後操作。
第二種方法:用雙指標,也即是二級指標。
view plain
copy to clipboard
print?
typedef struct nodelist;
void
create(list **l)
int
main(...)
深入理解指標
指標 是乙個特殊的變數 它裡面儲存 的數值被解釋成為記憶體裡的乙個位址 指標也是一種資料型別,並且也是有值的。要搞清乙個指標需要搞清指標的四方面的內容 指標的型別,指標所指向的型別,指標的值或者叫指標所指向的記憶體區,還有指標本身所佔據的記憶體區。讓我們分別說明。先宣告幾個指標放著做例子 例一 1 ...
深入理解指標
指標 是乙個特殊的變數 它裡面儲存 的數值被解釋成為記憶體裡的乙個位址 指標也是一種資料型別,並且也是有值的。要搞清乙個指標需要搞清指標的四方面的內容 指標的型別,指標所指向的型別,指標的值或者叫指標所指向的記憶體區,還有指標本身所佔據的記憶體區。讓我們分別說明。先宣告幾個指標放著做例子 例一 1 ...
深入理解指標
指標的概念 指標是乙個特殊的變數,它裡面儲存的數值被解釋成為記憶體裡的乙個位址。要搞清乙個指標需要搞清指標的四方面的內容 指標的型別,指標所指向的型別,指標的值或者叫指標所指向的記憶體區,還有指標本身所佔據的記憶體區。讓我們分別說明。先宣告幾個指標做例子 例一 1 in ptr 2 char ptr...