有這麼一道面試題,題目如下:
view plain
using
system;
public
class
test1
,", p.name, num);
} static
void
a1(person p,
intnum)
} public
class
person
} 說說上面的程式產生的結果,以及產生這個結果的原因是什麼?
我在以前過過一篇關於引用傳遞和值傳遞的部落格,位址如下:
今天再就上面的面試題說一說,一方面鞏固自己,另一方面方便心裡沒底的面試者徹底了解;
首先我們得清楚,在c#中,資料型別分為引用型別和值型別,值型別儲存在堆疊中,引用型別稍微複雜點,引用型別分為兩部分儲存,引用型別的值儲存在託管堆中,對該值的引用儲存在堆疊中,值和值引用構成了乙個完整的引用型別變數;我們經常使用下面的語法宣告變數:
view plain
inti=0;
string
str =
"new string"
;
在i的宣告過程中,系統做了兩件事情,一件事情是在記憶體的堆疊中找到乙個4位元組的位置(int型別的長度為4位元組),轉換成**應該這麼表示:int i= new int();第二件事情是將0賦予i,轉換成**應該這麼表示:i=0;綜合下來實際**應該如下所示:
view plain
inti=
newint
();
i=0;
在str的宣告過程中,系統也是做了兩件事情,只不過兩件事情的手段不一致,第一件事情是在記憶體中找了兩個地方,乙個地方在託管堆中,用於儲存str的值,另外乙個地方在堆疊中,用於指向託管堆的儲存位置;轉換成**也是一句話:string str=new string();第二件事也是將new sting這個字串賦予變數值,但是new string是記錄在託管堆中的,這是與值型別的區別,轉換成**應該這麼表示:str=「new string」;綜合下來**是一樣的,但是在分配記憶體時存在的差異就比較大了:
view plain
string
str=
newstriing();
str="new string"
;
弄清楚了值型別和引用在記憶體中的儲存方式,我們再來看看方法的引數傳遞,在c#中,所有的引數都是通過值來傳遞的,被呼叫的方法得到的都是該值的副本;這裡一定要注意:被呼叫的方法得到的都是該值的副本,也就是說,我們看下面這個方法:
view plain
public
void
methdouble(
inti)
下面我們在程式中呼叫該方法,**如下:
int i=3;
methdouble(i);
在以上的**中,系統做了如下事情:首先將變數在記憶體堆疊中copy乙個副本,這個副本的變數名我們假設稱為copy_i,得到副本後,再將副本copy_i傳遞給方法methdouble執行;所以在以上的過程中,methdouble方法內部所做的任何事情都不會對變數i產生任何影響;我們再來看看下面的方法:
view plain
"white-space:pre"
>
public
void
strdouble(
string
str)
下面我們在程式中呼叫該方法,**如下:
view plain
string
str =
"mystring"
; strdouble(str);
同理,在以上的**中,系統做了如下事情:首先將str在堆疊中的值引用變數copy乙個副本,這個副本變數名我們假設稱為copy_str,這個副本是乙個引用,該引用指向的位址就是str引用指向的託管堆的位址,也就是託管堆中「mystring」值,得到副本後,再將副本copy_str傳遞給方法執行,所以在操作過程中,方法對副本所做的修改都會直接修改託管堆中的值,從而影響方法外的str變數;
明白了以上的原理,我們再來看看開篇的面試題:
view plain
intnum = 0;
person p = new
person(
"li"
);
宣告了值型別變數num,引用型別變數p;
view plain
a1(p, num);
將num的副本和p的引用副本傳遞給方法;
這裡我們可能有疑問了,p的引用傳遞進去了,那麼對p所做的修改應該會影響託管堆中的值啊,我們這裡看看方法a1方法的實現
view plain
static
void
a1(person p,
intnum)
view plain
static
void
a1(person person,
intnum)
沒改之前的方法裡面一句p=new person("wang");大家就以為p在堆疊中的值改變了,其實不然,p僅僅是乙個堆疊中的副本,在方法a1中,是用了p=new person("wang"),此時系統做了兩件事情:一件是在託管堆中找乙個地方,用於儲存p(實際上是下面方法的person)的值,然後將a1方法中的p引用指向該堆疊的位置,此時對a1方法的p所做的任何修改都是在修改第一件事情中找到的託管堆,因為a1方法中的引用指向更改了,所以主函式內的變數p此時指向的託管堆與a1方法內p指向的託管堆分別為不同的位置;所以此時a1方法所做的任何事情對主函式內的變數p都不會造成影響;
所以面試題的結果應該為:li,0
值傳遞和引用傳遞的區別
以值傳遞引數 當實參當作值來傳遞時,就產生了乙個新的拷貝。class test x static void fo int p p p 1 console.writeline p p 程式執行結果為 p 9,x 8 即x的值不會受p影響,給p賦乙個新值並不會改變x的內容,因為p和x存在於記憶體中不同的...
值傳遞和引用傳遞的區別
public class test 執行 得到的當前的結果如下 int1 10int2 10改變之後 int1 10int2 20process finished with exit code 0根據結果會發現int型別的傳遞,當int2值改變的時候,int1的值是沒有任何變化的,所以基本型別都是根...
值傳遞和引用傳遞的區別
值傳遞 值傳遞是指在呼叫函式時將實際引數複製乙份傳遞到函式中,這樣在函式中如果對引數進行修改,將不會影響到實際引數。引用傳遞 引用傳遞是指在呼叫函式時將實際引數的位址傳遞到函式中,那麼在函式中對引數所進行的修改,將影響到實際引數。陣列作為方法引數傳遞時,傳遞的引數是陣列記憶體的位址.陣列作為方法的返...