程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 從一道面試題說說方法的引用傳遞和值傳遞

從一道面試題說說方法的引用傳遞和值傳遞

編輯:C#入門知識

今天偶然在csdn論壇看到這麼一篇帖子,就是說有這麼一道面試題,題目如下:


using System; 
public class Test1 

    public static void Main() 
    { 
        int num = 0; 
        Person p = new Person("Li"); 
        A1(p, num); 
        Console.WriteLine("{0},{1}", p.name, num); 
    } 
    static void A1(Person p, int num) 
    { 
        p = new Person("Wang"); 
        num = 1; 
    } 

public class Person 

    public string name; 
    public Person(string name) 
    { 
        this.name = name; 
    } 

using System;
public class Test1
{
    public static void Main()
    {
        int num = 0;
        Person p = new Person("Li");
        A1(p, num);
        Console.WriteLine("{0},{1}", p.name, num);
    }
    static void A1(Person p, int num)
    {
        p = new Person("Wang");
        num = 1;
    }
}
public class Person
{
    public string name;
    public Person(string name)
    {
        this.name = name;
    }
}


說說上面的程序產生的結果,以及產生這個結果的原因是什麼?

帖子上面說面試了十個人,居然有十個人答錯了,貌似非常不正常啊,但是說白了,這裡面主要就是想了解一下面試者對引用傳遞和值傳遞的理解;

我在以前過過一篇關於引用傳遞和值傳遞的博客,地址如下:http://www.BkJia.com/kf/201201/116158.html

今天再就上面的面試題說一說,一方面鞏固自己,另一方面方便心裡沒底的面試者徹底了解;


首先我們得清楚,在C#中,數據類型分為引用類型和值類型,值類型保存在堆棧中,引用類型稍微復雜點,引用類型分為兩部分保存,引用類型的值保存在托管堆中,對該值的引用保存在堆棧中,值和值引用構成了一個完整的引用類型變量;我們經常使用下面的語法聲明變量:

int i=0; 
string str = "new string"; 
 int i=0;
 string str = "new string";

在i的聲明過程中,系統做了兩件事情,一件事情是在內存的堆棧中找到一個4字節的位置(int類型的長度為4字節),轉換成代碼應該這麼表示:int i= new int();第二件事情是將0賦予i,轉換成代碼應該這麼表示:i=0;綜合下來實際代碼應該如下所示:

int i=new int(); 
i=0; 
 int i=new int();
 i=0;

在str的聲明過程中,系統也是做了兩件事情,只不過兩件事情的手段不一致,第一件事情是在內存中找了兩個地方,一個地方在托管堆中,用於存儲str的值,另外一個地方在堆棧中,用於指向托管堆的存儲位置;轉換成代碼也是一句話:string str=new string();第二件事也是將new sting這個字符串賦予變量值,但是new string是記錄在托管堆中的,這是與值類型的區別,轉換成代碼應該這麼表示:str=“new string”;綜合下來代碼是一樣的,但是在分配內存時存在的差異就比較大了:

string str=new striing(); 
str="new string"; 
 string str=new striing();
 str="new string";

弄清楚了值類型和引用在內存中的保存方式,我們再來看看方法的參數傳遞,在C#中,所有的參數都是通過值來傳遞的,被調用的方法得到的都是該值的副本;這裡一定要注意:被調用的方法得到的都是該值的副本,也就是說,我們看下面這個方法:

public void MethDouble(int i) 

    return i*2; 

 public void MethDouble(int i)
 {
  return i*2;
 }

下面我們在程序中調用該方法,代碼如下:

int i=3;

MethDouble(i);

在以上的代碼中,系統做了如下事情:首先將變量在內存堆棧中copy一個副本,這個副本的變量名我們假設稱為copy_i,得到副本後,再將副本copy_i傳遞給方法MethDouble執行;所以在以上的過程中,MethDouble方法內部所做的任何事情都不會對變量i產生任何影響;我們再來看看下面的方法:

<span style="white-space:pre">    </span>public void StrDouble(string str) 
    { 
        return str+str; 
    } 
<span style="white-space:pre"> </span>public void StrDouble(string str)
 {
  return str+str;
 }


下面我們在程序中調用該方法,代碼如下:

string str = "myString"; 
StrDouble(str); 
 string str = "myString";
 StrDouble(str);

同理,在以上的代碼中,系統做了如下事情:首先將str在堆棧中的值引用變量copy一個副本,這個副本變量名我們假設稱為copy_str,這個副本是一個引用,該引用指向的地址就是str引用指向的托管堆的地址,也就是托管堆中“myString”值,得到副本後,再將副本copy_str傳遞給方法執行,所以在操作過程中,方法對副本所做的修改都會直接修改托管堆中的值,從而影響方法外的str變量;

明白了以上的原理,我們再來看看開篇的面試題:

int num = 0; 
Person p = new Person("Li"); 
        int num = 0;
        Person p = new Person("Li");聲明了值類型變量num,引用類型變量p;

A1(p, num); 
        A1(p, num); 將num的副本和p的引用副本傳遞給方法;

這裡我們可能有疑問了,p的引用傳遞進去了,那麼對p所做的修改應該會影響托管堆中的值啊,我們這裡看看方法A1方法的實現

static void A1(Person p, int num) 

    p = new Person("Wang"); 
    num = 1; 

    static void A1(Person p, int num)
    {
        p = new Person("Wang");
        num = 1;
    } 注意了,上面的方法其實是用了一個障眼法,我把方法改改,大家再看看:

static void A1(Person person, int num) 

    person = new Person("Wang"); 
    num = 1; 

    static void A1(Person person, int num)
    {
        person = new Person("Wang");
        num = 1;
    } 沒改之前的方法裡面一句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

 


int num = 0; 
Person p = new Person("Li"); 
        int num = 0;
        Person p = new Person("Li");

摘自 魯信的專欄

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved