傳值?還是傳引用?
(Wang hailong)
關于編程的參數傳遞問題,總是存在著這樣的爭論。傳值?還是傳引用?(還是傳指針?還是傳地址?)這些提法,經常出現在C++, java, C#的編程技術文檔里面。這個問題也經常引起開發人員的爭論,徒耗人力物力。實際上,這根本不成為問題,只是由于人為加入的概念,混淆了人們的視聽。
從程序運行的角度來看,參數傳遞,只有傳值,從不傳遞其它的東西。只不過,值的內容有可能是數據,也有可能是一個內存地址。
開發人員應該了解程序的編譯結果是怎樣在計算機中運行的。程序運行的時候,使用的空間可以分為兩個部分,棧和堆。棧是指運行棧,局部變量,參數,都分配在棧上。程序運行的時候,新生成的對象,都分配在堆里,堆里分配的對象,棧里的數據參數,或局部變量。
下面舉一個C++的例子。
public class Object{
int i;
public Object(int i){
this.i = i;
}
public int getValue(){
return i;
}
public void setValue(int i){
this.i = i;
}
};
class A {
Void func1(int a, Object b){
Object * c = new Object( a );
b = c;
}
public void main(){
Object * param = new Object( 1 );
func1( 2, param );
// what is value of parram now ?
// it is still 1.
}
};
我們來看一下,當調用到func1函數時,運行到Object * c = new Object( a ); 棧和堆的狀態。不同編譯器生成的代碼運行的結果可能會稍有不同。但參數和局部變量的大致排放順序都是相同的。
這時候,我們來看,param變量被壓入運行棧的時候,只是進行了簡單的復制。把param里面的內容拷貝到b里面。這時候,b就指向了Object(1)。這里的參數傳遞,是把param的值傳遞給b。
下面我們來看,程序執行到b = c;時候的堆棧狀態。
我們可以看到,b現在指向了Object(2)。但是對param的值毫無影響。param的值還是Object(1)。
所以,我們說,對參數的賦值不會影響到外層函數的數據,但是,調用參數的操作方法,卻等于直接操作外層函數的數據。比如,如果我們在func1()函數中,不調用b=c;而調用b.setValue(3),那么Object(1)的數據就會變為3,param的數據也會改變為3。
在java和C#中的情況,也都是一樣。
C++還有一種變量定義方法,表面上看起來,不符合上面的說明,這里進行說明。
Object * a = new Object(1);
Object & * b = a;
這里的b就等于是a的另外一個別名,b就是a。對b賦值就等于對a賦值。甚至作為參數傳遞時,也是如此。對這種類型的參數的賦值,就等于對外層函數數據的賦值。
public class B{
void func1(Object & * b){
b = new Object(4);
}
public void main(){
Object * a = new Object(1);
func1(a);
// a is changed to Object(4) now.
}
}
當運行完func1(a);時,a的值變化為Object(4)。這是因為編譯器實際把參數Object & * b編譯為Object ** b_addr,b_addr的值是b的地址,也就是a的地址。
當調用func1()的時候,實際上是把b_addr作為參數壓到棧里,b_addr的值是a的地址。
當執行b = new Object(4); 時,實際執行了 b_addr->b = new Object(4); 也就是執行了 b_addr->a = new Object(4); a的值當然變化了。
還有一點需要說明,當使用COM,CORBA等中間件規范進行開發時,我們需要定義IDL語言。參數的類型分為,[in],[out],[in, out],其中的RPC遠程調用的參數打包規范,就更復雜了,但原理卻是一樣的。
posted on 2005-11-17 09:43
安德爾斯 閱讀(389)
評論(0) 編輯 收藏