1. 程式人生 > >傳值與傳地址的區別

傳值與傳地址的區別

引數是呼叫函式的程式碼,傳給函式的資料,在C,C++中,引數有兩種傳遞方式:傳值方式(它是程式中最常見的傳遞引數的方法)和傳址方式(函式對引數的操作,將直接改變實參的值)。這兩個名詞分別指:傳遞“引數的值”和傳遞“引數的地址”。

“引數的傳遞方式”,“引數的傳遞過程”,方式和過程有何區別?中學時我對前桌的女生“有意思”,想給人家傳遞點資訊,是往她家打個電話呢?還是來個“小紙條”?這就是“傳遞方式”的不同。我選擇了後者。至於傳遞過程:剛開始時我把紙條裹在她的頭髮裡,下課時假裝關心地“喂,你的頭髮裡掉了張紙……”。後來大家熟了,上課時我輕輕動一下她的後背,她就會不自在,然後在一個合適時機,自動把手別過來取走桌沿的紙條……這就是傳遞過程的不同吧?(以上故事純屬虛構)

程式是在記憶體裡執行的。所以無論引數以哪一種方式傳遞,都是在記憶體中“傳來傳去”。在一個程式執行時,程式會專門為引數開闢一個記憶體空間,稱為“棧”。棧所在記憶體空間位於整個程式所佔記憶體的頂部(為了直觀,我們將地址較小的記憶體畫在示意圖頂部,如果依照記憶體地址由下而上遞增規則,則棧區應該在底部),如圖:

當程式需要傳遞引數時,將一個個引數“壓入”棧區記憶體的底部,然後,函式再從棧區一個個讀出引數。

如果一個函式需要返回值,那麼呼叫者首先需要在棧區留出一個大小正好可以儲存返回值的記憶體空間,然後再執行引數的入棧操作。

假設有一函式:int AddTwoNum(int n1, int n2)     然後在程式碼某處呼叫:

....

int a = 1;

int b = 2;

int c = AddTwoNum(a,b);

當執行上面黑體部分,即呼叫函式的動作發生時,棧區出現下面的操作:

圖中標明為返回值預留的空間大小是4個位元組,當然不是每個函式都這個大小。它由函式返回值的資料型別決定,本函式AddTwoNum返回值是int型別,所以為4個位元組。其它的a,b引數也是int型別,所以同樣各佔4位元組大小的記憶體空間。

至於引數是a還是b先入棧,這依編譯器而定,大都數編譯器採用“從右到左的次序”將引數一個個壓入。所以本示意圖,引數b被先“壓”入在底部,然後才是a。這樣就完成了引數的入棧過程。根據前面講的不同“傳遞方式”,被實際壓入棧的資料也就不同。

一、如果是“傳值”,則棧中的a,b就是“複製品”,對二者的操作,僅僅是改變此處棧區的記憶體,和呼叫處的實參:a,b毫不關聯:

二、而在“傳址”方式時,編譯器會將呼叫處的a,b的記憶體地址寫入棧區,並且將函式中所有對該棧區記憶體的操作,都轉向呼叫處a,b的記憶體地址。請看:看起來二的圖比一要複雜得多。其實實質的區別並不多。

在一圖中,傳給函式的是a,b的值,即1,2;

在二圖中,傳給函式的是a,b的地址,即:00129980,00129984。

“引數的傳遞過程”說到最後,還是和“引數的傳遞方式”糾纏在一起。我個人認為,在剛開始學習C++時,並不需要--或者甚至就是最好不要--去太糾纏語言內部實現的機制,而重在於運用。下面我們就來舉一個使用“傳址”方式的例子。

題目是:寫一函式,實現將兩個整型變數的值互換。

幸好實現它也非常的簡單和直觀。典型的方法是使用“第三者”你可能感到不解:交換兩個變數的值,就讓這兩個變數自個互換就得了,比如小明有個蘋果,小光有個梨子,兩人你給我給你就好了啊,要小兵來做什麼?

呵,你看吧:

int a = 1, b = 2;

//不要“第三者”的交換(失敗)

a = b;

b = a;

好好看看,好好想想吧。當執行交換的第一句:a = b;時,看去工作得不錯,a的值確實由1變成了2。然後再下去呢?等輪到b想要得到a的值時,a現在和b其實相等,都是2,結果b=a;後,b的值還是2.沒變。

只好讓“第三者”插足了……反正程式沒有婚姻法。

int a = 1, b =2;

int c ; //“第三者”

//交換開始:

c = a;

a = b;

b = c;