C#.net core 基礎(chǔ) - 值傳遞、引用傳遞
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
不知道你在開發(fā)過程中有沒有遇到過這樣的困惑:這個(gè)變量怎么值被改?這個(gè)值怎么沒變? 今天就來和大家分享可能導(dǎo)致這個(gè)問題的根本原因值傳遞 vs 引用傳遞。 在此之前我們先回顧兩組基本概念: 值類型 vs 引用類型 值類型: 直接存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)在棧上; 引用類型: 存儲(chǔ)數(shù)據(jù)對(duì)象的引用,數(shù)據(jù)實(shí)際存儲(chǔ)在堆上。 形參 vs 實(shí)參 形參: 即形式參數(shù),表示調(diào)用方法時(shí),方法需要你傳遞的值。方法聲明定義了其形參。也就是說在定義方法時(shí),緊跟在方法名后面括號(hào)中的參數(shù)列表就是形參。 實(shí)參: 即實(shí)際參數(shù),表示調(diào)用方法時(shí),你傳遞給方法形參的值。調(diào)用代碼在調(diào)用過程時(shí)提供實(shí)參。也就是說在調(diào)用方法時(shí),緊跟在方法名后面括號(hào)中的參數(shù)列表就是實(shí)參。 再來回顧一下值類型和引用類型在內(nèi)存中是怎么存儲(chǔ)的呢? 對(duì)于值類型變量的值直接存儲(chǔ)在棧中,如下圖的int a=10,10就直接存在棧空間中,而其棧空間對(duì)應(yīng)的內(nèi)存地址為0x66666668;對(duì)于引用類型變量本身存儲(chǔ)的是實(shí)例對(duì)象的引用,即實(shí)例對(duì)象在堆中的實(shí)際內(nèi)存地址,因此引用類型變量是存儲(chǔ)其實(shí)例對(duì)象的引用于棧上,如下圖中變量Test a在棧中實(shí)際存儲(chǔ)的是實(shí)例對(duì)象Test a在堆中的內(nèi)存地址0x88888880,而棧空間對(duì)應(yīng)的內(nèi)存地址為0x66666668。 棧也是有內(nèi)存地址的,這一點(diǎn)很重要,無論棧空間上存儲(chǔ)的是值還是引用地址,這個(gè)棧空間本身也有自己對(duì)應(yīng)的內(nèi)存地址。 什么是值傳遞?什么是引用傳遞? 值傳遞:如果變量按值傳遞給方法,則會(huì)把變量的副本傳遞給方法。對(duì)于值類型則把變量的副本傳遞給方法,對(duì)于引用類型則把變量的引用的副本傳遞給方法。因此被調(diào)用方法參數(shù)會(huì)創(chuàng)建一個(gè)新的內(nèi)存地址用于接收存儲(chǔ)變量,因此在方法內(nèi)部對(duì)變量修改并不會(huì)影響原來的值。 引用傳遞:如果變量按引用傳遞給方法,則會(huì)把變量的引用傳遞給方法,對(duì)于值類型則把變量的棧空間地址傳遞給方法,對(duì)于引用類型則把變量的引用的棧空間地址傳遞給方法。因此被調(diào)用方法參數(shù)不會(huì)創(chuàng)建一個(gè)新的內(nèi)存地址用于接收存儲(chǔ)變量,意味著形參與實(shí)參共同指向相同的內(nèi)存地址,因此在方法內(nèi)部修對(duì)變量修改會(huì)影響原來的值。 上面的描述可能有點(diǎn)拗口,下面我們?cè)诨谥殿愋汀⒁妙愋汀⒅祩鬟f、引用傳遞各種組合進(jìn)行一個(gè)詳細(xì)說明。 01、值類型按值傳遞當(dāng)值類型按值傳遞時(shí),調(diào)用者會(huì)把值類型變量的副本傳遞給方法,因此被調(diào)用方法參數(shù)會(huì)創(chuàng)建一個(gè)新的內(nèi)存地址用于接收存儲(chǔ)變量,因此當(dāng)在方法內(nèi)部對(duì)參數(shù)進(jìn)行修改時(shí)并不會(huì)影響調(diào)用者調(diào)用處的值類型變量。 傳遞值類型變量的副本就是相當(dāng)于在棧上,又復(fù)制了一個(gè)同樣的值,而且內(nèi)存地址還不一樣,所以互不影響。如下圖把a(bǔ)賦值給b,則b直接新開辟了一個(gè)棧空間,雖然a和b都是10,但是它們?cè)诓煌牡刂房臻g中,因此如果他們各自被修改了,也互不影響。 下面我們寫個(gè)例子演示一下,這個(gè)例子就是定義個(gè)變量a并賦值,然后調(diào)用一個(gè)方法此方法內(nèi)對(duì)傳進(jìn)來的參數(shù)a進(jìn)行加1,具體代碼如下:
運(yùn)行結(jié)果如下: 通過代碼執(zhí)行結(jié)果可以發(fā)現(xiàn),方法內(nèi)對(duì)變量的修改已經(jīng)生效,但是不沒有影響到調(diào)用者調(diào)用處的變量值。 02、引用類型按值傳遞當(dāng)引用類型按值傳遞時(shí),調(diào)用者會(huì)把引用類型變量的引用副本傳遞給方法,因此被調(diào)用方法參數(shù)會(huì)創(chuàng)建一個(gè)新的內(nèi)存地址用于接收存儲(chǔ)變量,而對(duì)于一個(gè)引用類型變量來說其本身存儲(chǔ)的就是引用類型實(shí)例對(duì)象的引用副本,而方法接收到的也是此變量引用的副本,所以調(diào)用者參數(shù)和被調(diào)用方法參數(shù)是引用了同一個(gè)實(shí)例對(duì)象的兩個(gè)引用副本。如下圖Test a可以理解為調(diào)用者傳的實(shí)參,Test b可以理解為被調(diào)用方法定義的形參,這兩個(gè)參數(shù)都只是指向堆中Test a的引用副本。 因此可以得出兩個(gè)結(jié)論: 1、變量a和b都是指向?qū)嵗龑?duì)象Test a的引用,所以無論變量a或b,只要有一個(gè)更新了實(shí)例成員則另一個(gè)變量也會(huì)同步發(fā)生變化。 2、雖然變量a和b都是指向?qū)嵗龑?duì)象Test a的引用,但是他們存儲(chǔ)在棧上的內(nèi)存地址卻不同,因此如果他們各種重新分配實(shí)例也就是new一個(gè)新對(duì)象,則另一個(gè)變量無法感知到還是保持原因狀態(tài)不變。 我們先用代碼說明第一個(gè)結(jié)論:
運(yùn)行結(jié)果如下: 可以看到被調(diào)用方法中a實(shí)例對(duì)象的Age屬性發(fā)生變化后,調(diào)用者中變量也同步發(fā)生了變化。 對(duì)于第二個(gè)結(jié)論我們這樣論證,在方法中直接對(duì)參數(shù)new一個(gè)新對(duì)象,看看原變量是否發(fā)生變化,代碼如下:
執(zhí)行結(jié)果如下: 可以發(fā)現(xiàn)當(dāng)在方法中對(duì)變量執(zhí)行new操作后,調(diào)用者處的變量并沒有發(fā)生變化。 為什么會(huì)這樣呢?因?yàn)閷?duì)于引用類型來說,形參和實(shí)參是對(duì)引用類型的實(shí)例對(duì)象引用的兩個(gè)副本,而這兩個(gè)副本存儲(chǔ)在棧上又分別在不同的內(nèi)存地址空間上,而new主要就是重新分配內(nèi)存,這就導(dǎo)致形參變量a=new后,棧上形參變量a指向了Test新的實(shí)例對(duì)象的引用,而實(shí)參變量a還是保持原有實(shí)例對(duì)象引用不變。 如下圖所示。 03、值類型按引用傳遞當(dāng)值類型按引用傳遞時(shí),調(diào)用者會(huì)把值類型變量對(duì)應(yīng)的棧空間地址傳遞給方法,因此被調(diào)用方法參數(shù)不會(huì)創(chuàng)建一個(gè)新的內(nèi)存地址用于接收存儲(chǔ)變量,因此當(dāng)在方法內(nèi)部對(duì)參數(shù)進(jìn)行修改時(shí)并同樣會(huì)影響調(diào)用者調(diào)用處的值類型變量。 傳遞值類型變量對(duì)應(yīng)的棧空間地址就意味著形參與實(shí)參共同指向相同的內(nèi)存地址,所以才導(dǎo)致對(duì)形參修改時(shí),實(shí)參也會(huì)同步發(fā)生變化。 我們用一個(gè)小例子演示一下:
執(zhí)行結(jié)果如下: 可以發(fā)現(xiàn)調(diào)用者處的值類型變量已經(jīng)發(fā)生改變。 04、引用類型按引用傳遞當(dāng)引用類型按引用傳遞時(shí),調(diào)用者會(huì)把引用類型變量對(duì)應(yīng)的棧空間地址傳遞給方法,因此被調(diào)用方法參數(shù)不會(huì)創(chuàng)建一個(gè)新的內(nèi)存地址用于接收存儲(chǔ)變量,因此當(dāng)在方法內(nèi)部對(duì)參數(shù)進(jìn)行修改時(shí)并同樣會(huì)影響調(diào)用者調(diào)用處的引用類型變量。 傳遞引用類型變量對(duì)應(yīng)的棧空間地址就意味著形參與實(shí)參共同指向相同的內(nèi)存地址,因此對(duì)形參修改時(shí),實(shí)參也會(huì)同步發(fā)生變化,而且這個(gè)里的修改不單單指修改實(shí)例成員,還包括new一個(gè)新實(shí)例對(duì)象。 下面我們看一個(gè)修改實(shí)例成員的例子:
執(zhí)行結(jié)果如下: 再看看new一個(gè)新對(duì)象的例子:
執(zhí)行結(jié)果如下: 另外string是一個(gè)特殊的引用類型,string類型變量的按值傳遞和按引用傳遞和值類型是一致的,也就是要把string類型當(dāng)值類型一樣看待就行。string類型的特殊性我們后面會(huì)單獨(dú)具體介紹。 在C#中以下修飾符可應(yīng)用與參數(shù)聲明,并且會(huì)使得參數(shù)按引用傳遞:ref、out、readonly ref、in。對(duì)于每個(gè)修飾符具體怎么使用就不再這里細(xì)說了。 相信到這里你應(yīng)該就可以回答我之前在《LeetCode題集-2 - 兩數(shù)相加》最后提的問題了。 注:測(cè)試方法代碼以及示例源碼都已經(jīng)上傳至代碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner 轉(zhuǎn)自https://www.cnblogs.com/hugogoos/p/18419656 該文章在 2025/1/2 8:42:30 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |