1. 程式人生 > >從一道面試題說說方法的引用傳遞和值傳遞

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

就是說有這麼一道面試題,題目如下:

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;
    }
}

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

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

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

首先我們得清楚,在C#中,資料型別分為引用型別和值型別,值型別儲存在堆疊中,引用型別稍微複雜點,引用型別分為兩部分儲存,引用型別的值儲存在託管堆中,對該值的引用儲存在堆疊中,值和值引用構成了一個完整的引用型別變數;我們經常使用下面的語法宣告變數:

	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;

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

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

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

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

下面我們在程式中呼叫該方法,程式碼如下:

int i=3;

MethDouble(i);

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

public void StrDouble(string str)
	{
		return str+str;
	}

下面我們在程式中呼叫該方法,程式碼如下:

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

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

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

        int num = 0;
        Person p = new Person("Li");
聲明瞭值型別變數num,引用型別變數p;
        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 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");

相關推薦

一道試題說說方法引用傳遞傳遞

就是說有這麼一道面試題,題目如下: using System; public class Test1 { public static void Main() { int num = 0; Person p = new Pe

一道試題簡單談談釋出訂閱觀察者模式

今天的話題是javascript中常被提及的「釋出訂閱模式和觀察者模式」,提到這,我不由得想起了一次面試。記得在去年的一次求職面試過程中,面試官問我,“你在專案中是怎麼處理非父子元件之間的通訊的?”。我答道,“有用到vuex,有的場景也會用EventEmitter2”。面試官繼續問,“那你能手寫程式碼,實現一

一道試題來認識java類加載時機與過程【轉】

包含 布局 hello 印象 大致 周期 default () itl 說明:本文的內容是看了《深入理解Java虛擬機:JVM高級特性與最佳實踐》後為加印象和理解,便記錄了重要的內容。 1 開門見山 以前曾經看到過一個java的面試題,當時覺得此題很簡單,可是自己

有關java類、對象初始化的話題,一道試題切入

() 深入理解java 補充 [] base sna 字體 都是 spa 最近在整理東西時,剛好碰到以前看的一道有關java類、對象初始化相關題目,覺得答案並不是非常好(記憶點比較差,不是很連貫)。加上剛好復習完類加載全過程的五個階段(加載-驗證-準備-解析-初始化),所以

一道試題:說說程序執行緒的區別

在理解這些概念之前首選要對併發有一定的感性認識,如果伺服器同一時間內只能服務於一個客戶端,其他客戶端都再那裡傻等的話,可見其效能的低下估計會被客戶罵出翔來,因此併發程式設計應運而生,併發是網路程式設計中必須考慮的問題。實現併發的方式有多種:比如多程序、多執行緒、IO多路複用。 在理解這些概念

一道試題徹底搞懂hashCode與equals的作用與區別及應當注意的細節

public class HashCodeTest { public static void main(String[] args) { Collection set = new HashSet(); Point p1 = new Point(1, 1); Point p2 = new Poin

一道試題來認識java類載入時機與過程

說明:本文的內容是看了《深入理解Java虛擬機器:JVM高階特性與最佳實踐》後為加印象和理解,便記錄了重要的內容。 1  開門見山 以前曾經看到過一個java的面試題,當時覺得此題很簡單,可是自己把程式碼執行起來,可是結果並不是自己想象的那樣。題目如下: class SingleTon {

一道試題看ES6箭頭函式

前幾天頭條面試碰到了這樣一道面試題,讓我寫出每行程式碼的執行結果: var f = x => x; f(1); //return 1 var f = x => {x}; f(1); //function(x)={x}; var f = x =

Android多執行緒研究(4)——一道試題說起

有一道這樣的面試題:開啟一個子執行緒和主執行緒同時執行,子執行緒輸出10次後接著主執行緒輸出100次,如此反覆50次。先看下面程式碼:package com.maso.test; /** * * @author Administrator * 兩個執行緒,其中是一個

一道試題談js函式宣告

愛奇藝前端面試題有個題目如下: a(); function a(){ console.log('a'); } b(); var b = function(){ console.log('b'); } 那麼函式的執行結果是? 熟悉函式變數

每天一道試題-執行時異常非執行時異常-你分清楚了嗎?

面試中,經常會遇到這樣的情況,問:談談執行時異常和非執行時異常?答:執行時異常是指程式執行時發生的異常問:那麼非執行時異常又是什麼時候發生?答:。。。。。。問:請羅列幾個常見的執行時異常?答:空指標異常,IOException,SQLException,陣列越界異常。。。。以

一道試題:通過waitnotify的兩個執行緒互動輸出thread1-1...thread1-5,thread2-6...thread2-10...

這是一道關於多執行緒的面試題,好久沒有做過這種多執行緒的題了,手有點生,那麼就來敲一敲 package threadDemo; /* * 多執行緒的交叉列印 */ public class

一道試題開始說起 列舉、動態代理的原理

本文已在我的公眾號hongyangAndroid原創釋出。 轉載請標明出處: 本文出自:漲鴻洋的部落格 前段時間在dota群,一哥們出去面試,回顧面試題的時候,說問到了列舉。 作為一名Android選手,談到列舉,那肯定是: An

一道試題談linux下fork的執行機制

今天一位朋友去一個不錯的外企面試linux開發職位,面試官出了一個如下的題目:     

一道試題深入瞭解java虛擬機器記憶體結構

記得剛大學畢業時,為了應付面試,瘋狂的在網上刷JAVA的面試題,很多都靠死記硬背。其中有道面試題,給我的印象非常之深刻,有個大廠的面試官,順著這道題目,一直往下問,問到java虛擬機器的知識,最後把我給問住了。 我當時的表情是這樣的: 後來我有機會面試別人了,也按照他的思路出面試題,很多已經工作了2年的程式設

理解Java中的引用傳遞傳遞

包裝類 pri ble buffer 聲明 change cnblogs padding ber 關於Java傳參時是引用傳遞還是值傳遞,一直是一個討論比較多的話題,有論壇說Java中只有值傳遞,也有些地方說引用傳遞和值傳遞都存在,比較容易讓人迷惑。關於值傳遞和引用傳遞其

java 引用傳遞傳遞

方式 con 並不是 一個 基本類型 依賴 tro 結束 事務 1.為什麽要分值傳遞和引用傳遞:   基本類型存在在棧中,復合類型(對象)存在堆中。操作棧的速度要快於堆,且對象的復制相比基本類型不僅浪費內存而且速度比較慢。 從這裏就可以看出來:對象是按照引用傳遞(數據庫事務

java引用傳遞傳遞

pos += 基本 test 特殊 今天 對象類型 改變 ring 關於Java傳參時是引用傳遞還是值傳遞,一直是一個討論比較多的話題,有論壇說Java中只有值傳遞,也有些地方說引用傳遞和值傳遞都存在,比較容易讓人迷惑。關於值傳遞和引用傳遞其實需要分情況看待,今天學習和分析

Java中的引用傳遞傳遞

基本類型包裝類 new except pri 引用類型 otsu 靜態 數據 app   1.基本類型和引用類型在內存中的保存  Java中數據類型分為兩大類,基本類型和對象類型。相應的,變量也有兩種類型:基本類型和引用類型。  基本類型的變量保存原始值,即它代表的值就是數

java 裏面的引用傳遞傳遞問題

system nal 新建 類型 tostring 問題 沒有 引用 操作 1.先從值引用:String a ="God";//a存放在stack 中 God存放在head(堆中)創建了兩個對象String b=a;a指向堆中的God對象b=a;說明b也