值傳遞和引用傳遞傳的到底是啥?

2021-01-08 CSDN

作者 | 編程指北 責編 | 張文

在網上看到過很多討論 Java、C++、Python 是值傳遞還是引用傳遞這類文章,所以這一篇呢就是想從原理講明白關於函數參數傳遞的幾種形式。

參數傳遞無外乎就是傳值(pass by value),傳引用(pass by reference)或者說是傳指針。

傳值還是傳引用可能在 Java、Python 這種語言中常常會困擾一些初學者,但是如果你有 C/C++背景的話,那這個理解起來就是 so easy。

今天我就從 C 語言出發,一次性把 Java、Python 這些都給大家講明白。

不過呀,要想徹底搞懂這個,需要了解兩個背景知識:

堆、棧函數調用棧

堆、棧

要注意,這「堆」和「棧」並不是數據結構意義上的堆(Heap,一個可看成完全二叉樹的數組對象)和 棧(Stack,先進後出的線性結構)。

這裡說的堆棧是指內存的兩種組織形式,堆是指動態分配內存的一塊區域,一般由程式設計師手動分配,比如 Java 中的 new、C/C++ 中的 malloc 等,都是將創建的對象或者內存塊放置在堆區。

而棧是則是由編譯器自動分配釋放(大概就是你申明一個變量就分配一塊相應大小的內存),用於存放函數的參數值,局部變量等。

就拿 Java 來說吧,基本類型(int、double、long 這種)是直接將存儲在棧上的,而引用類型(類)則是值存儲在堆上,棧上只存儲一個對對象的引用。

舉個慄子:

int age = 22;String name = newString("shuaibei");

這兩個變量存儲圖如下:

如果,我們分別對 age、name 變量賦值,會發生什麼呢?

age = 18;name = new String("xiaobei");

如下圖:

age 僅僅是將棧上的值修改為 18,而 name 由於是 String 引用類型,所以會重新創建一個 String 對象,並且修改 name,讓其指向新的堆對象。(細心的話,你會發現,圖中 name 執行的地址我做了修改)

然後,之前那個對象如果沒有其它變量引用的話,就會被垃圾回收器回收掉。

這裡也要注意一點,我創建 String 的時候,使用的是 new,如果直接採用字符串賦值,比如:

String name = "shuaibei"

那麼是會放到 JVM 的常量池去,不會被回收掉,這是字符串兩種創建對象的區別,不過這裡我們不關注。

Java 中引用這東西,和 C/C++ 的指針就是一模一樣的嘛。只不過 Java 做了語義層包裝和一些限制,讓你覺得這是一個引用,實際上就是指針。

好,讓我繼續了解下函數調用棧。

函數調用棧

一個函數需要在內存上存儲哪些信息呢?

參數、局部變量,理論上這兩個就夠了,但是當多個函數相互調用的時候,就還需要機制來保證它們順利的返回和恢復主調函數的棧結構信息。

那這部分就包括返回地址、ebp 寄存器(基址指針寄存器,指向當前堆棧底部) 以及其它需要保存的寄存器。

所以一個完整的函數調用棧大概長得像下面這個樣子:

那,多個函數調用的時候呢?

簡單來說就是疊羅漢,這是兩個函數棧:

今天,我們不會去詳細了解函數調用過程 ebp、ebp 如何變化,返回地址又是如何起作用的。

今天的任務就是搞明白參數傳遞,所以其它的都是非主線的知識,忽略即可。

順便插點題外話:

學習新知識有時候需要刨根問底,有時候卻需要及時回頭,尤其是計算機,你要是一直刨根問題,我能給你整到矽的提純去,這就是失去了學習的意義。

最好的方式是,在一個恰到好處的地方建立一個抽象層,並且認可這個抽象層提供的功能/接口,不去探究這一層下面是什麼,怎麼實現的。

比如,學習 HTTP,我就只需要認 TCP 提供穩定、可靠傳輸就夠了,暫時就不需要去看 TCP 如何做到的。

好了,繼續說回函數傳參,舉個例子,下面這段代碼在 main 函數內調用了 func_a 函數

intfunc_a(int a, int *b){ a = 5; *b = 5;};intmain(void){int a = 10;int b = 10; func_a(a, &b);printf("a=%d, b=%d\n", a, b);return0;}// 輸出a=10, b=5

那麼 func_a(a, &b) 這個過程,在函數調用棧上究竟是怎麼樣的呢?

就像上圖所示,編譯器會生成一段函數調用代碼。

將 main 函數內變量 a 的值拷貝到 func_a 函數參數 a 位置。

將變量 b 的地址,拷貝到 func_a 函數參數 b 的位置。

記住這張圖,這是函數參數傳遞的本質,沒有其它方式,just copy!

copy 意味著是副本,也就是在子函數的參數永遠是主調函數內的副本。

決定是值傳遞還是所謂的引用傳遞,在於你 copy 的到底是一個值,還是一個引用(的值)。

其實引用也是值......不要覺得引用就是那種玄乎的東西。

所以會有一種聲音說,是不存在所謂的引用傳遞的,一切傳引用的本質還是傳值。

也就是 pass pointer by value 或者 pass reference by value,哈哈哈有點意思。

今天,我們不討論到底有沒有傳引用這個東西,這是一個個仁者見仁智者見智的問題。我的目的呢,就是把參數傳遞這個過程給大家剖析下,至於到底是傳值還是傳引用,那就看大家怎麼思考了。

pass by value in java

舉個最簡單的例子來說明下:

publicclassHelloWorld {publicstaticvoidChangeRef(String name) { name = new String("xiaobei"); }publicstaticvoidmain(String[] args) { String name = new String("shuaibei"); ChangeRef(name); System.out.println(name.equals("shuaibei")); }}

上面,ChangeRef 函數實際上並沒有改變到 main 函數內的 name 對象,看圖就明白了:

根據我們前面所講,參數傳遞實際就是複製棧上的值本身,這裡 name 的值就一串地址,所以 ChangeRef 接收到的也是這串地址,但是在 ChangeRef 函數內將 name 的指向改成了一個新的 String 對象,但是這裡不會對 main 函數中的 name 對象產生任何的影響。

咦,不是說引用類型都是引用傳遞嗎?為什麼還不會對主調函數產生影響呢?

我們都把引用的指向改變了,還能影響個啥,如果想通過引用傳遞修改外部傳進來的值,一般是採用成員方法。

假設 String 類有一個方法叫做 changeStr(String value),那麼我們就可以在 ChangeRef 內調用這個方法,修改 name 的值,並且會同步修改到 main 函數裡的值。

Python

其實和Java 挺像的,但是 Python 有個特點就是所有變量本身只是一個引用,真正的類型信息都是和對象存儲在一起的。這裡就不再過多展開。

以上,希望能幫到大家。

相關焦點

  • 突觸傳遞的機理和特點
    化學突觸傳遞的分子機制是極其複雜的,一般認為包括以下內容。1.突觸的代謝過程。包括突觸小泡和遞質的合成、運輸和儲存,突觸末梢各種成分的裝配。在末梢處,突觸小泡和遞質的量只夠維持幾秒鐘至幾分鐘的突觸傳遞,因此它們都必須很快重新合成,以維持突觸傳遞的正常進行。突觸小泡是經過多種途徑,在神經元的不同部位形成的。
  • 模擬電路設計系列講座二:拉普拉斯變換和傳遞函數
    本文將簡單介紹信號處理技術最基本的知識,拉普拉斯變換,傳遞函數以及零極點。任何線性時不變系統的傳遞函數以及零極點都可以用電子元器件在拉普拉斯變換域(或者s域)內的阻抗形式進行表示。從電路到拉普拉斯變換域(或者s域)內的轉換形式如下表所示:
  • 了解傳遞函數中的極點和零點
    簡要回顧一些基本概念:傳遞函數在數學上表示濾波器的頻域輸入到輸出行為;可以用變量s來表示傳遞函數,它代表複雜的頻率,當需要計算特定頻率的幅度和相位響應時可以用jω代替s ;傳遞函數的標準化形式就像一個模板,可以幫助我們快速確定濾波器的定義特徵;對標準化一階傳遞函數的數學處理使我們能夠證明濾波器的截止頻率是幅度減小3dB並且相位偏移-45°的頻率。
  • 實測萬有引力傳遞速度項目建議書
    編制人:彭曉韜 編制日期:2020.04.25 一、項目目的萬有引力的傳遞速度到底是多少目前並沒有任何的實際測量數據。只有依據所謂的引力波觀測事件與天文觀測事件人為配對推算出的結果:與光速基本相同。
  • 火炬傳遞採用騎馬傳遞等方式 境外傳遞包機進行
    北京奧組委領導和火炬接力中心負責人發布北京2008年奧運會火炬接力總體安排和北京奧組委為火炬接力提供的媒體服務,並回答中外記者提問。中國網現場直播。在談到火炬接力路線,傳遞的亮點以及方式等有關情況時,蔣效愚說,第一,有關傳遞路線。
  • 『必出沾福氣的福字』2020年支付寶福滿全球傳福卡傳遞方法
    傳遞福卡,點亮實景地標,那麼怎麼才能福滿全球呢?今天小編就為玩家帶來支付寶福滿全球傳福卡方法,下面一起來看看吧。   支付寶福滿全球傳福卡傳遞方法   從1月13日開始,五福可以通過傳遞,傳遞的城市有北京八達嶺長城、杭州環球天幕、成都天府熊貓塔、上海東方明珠、廣州廣州塔、阿聯哈利法塔、西班牙巴特羅之家、澳大利亞墨爾本之星到達最後的南極長城科考站。
  • 電磁力通過光子傳遞,引力是通過「引力子」傳遞的嗎?
    #宇宙大揭秘#1915年,愛因斯坦發表了著名的廣義相對論,啟發人們認識到空間和時間是可以扭曲的他們在探索了微觀世界之後才發現,自然界的三種基本力在本質上是相同的,力的傳遞過程其實都隱含著物質粒子的交換——電磁力是通過光子傳遞的,強核力是通過膠子傳遞的,弱核力則是W和Z玻色子。
  • 「流星飛馬來報」,古代的信息怎麼傳遞的
    古代,交通極為不便,各類信息的傳遞幾乎全靠人工,而建立有組織的傳遞信息的系統將會極大得提高信息傳遞的速度,確保信息傳遞的安全。而我國就是世界上最早建立有組織的傳遞信息的國家之一。在商代時候,我國已有馹傳制度。那時傳遞信息主要是通過乘車和騎馬兩種方式,前者叫馹或傳,後者叫遞或驛。
  • 氣體傳遞原理和曝氣池
    氣體傳遞原理和曝氣池  一、活性汙泥法基本要素  構成活性汙泥法有三個基本要素,一是引起吸附和氧化分解作用的微生物,也就是活性汙泥;二是廢水中的有機物,它是處理對象,也是微生物的食料;三是溶解氧,沒有充足的溶解氧,好氧微生物既不能生存也不能發揮氧化分解作用。
  • 帶參數的無返回值(void)函數如何返回處理結果?(附代碼)
    在軟體開發過程中有一種很常見的編程方法,就是即使將參數傳給無返回值的void函數也能實現對原始參數值的修改,這句話到底說的什麼意思,且看示例。另一個例子:無返回值的帶參void函數如何做返回值?(附代碼)void函數是沒有返回值的,那麼對於傳入的參數在計算處理之後,怎麼把結果返回呢?這裡給出兩種最常用的方式:指針和引用!這也是軟體開發中實際使用的方式。
  • 知火而行|燃燒學基礎(3)—熱傳遞
    燃燒過程總是伴隨著熱量傳遞,本期我們來聊一聊熱傳遞的相關知識。熱傳遞有熱傳導、熱對流和熱輻射三種基本方式。熱傳導與熱對流都需要一個中間介質,而熱輻射則不需要。熱傳導屬於接觸傳熱,是由於大量分子、原子或電子的互相撞擊,使能量從物體溫度較高部分傳至溫度較低部分的過程。同時加熱一段銅管(左)、鋼管(右),7分鐘後,左邊的銅管溫度上升至96℃,右邊的鋼管上升至30.6℃,這主要是因為這兩種材料的導熱係數不相同。導熱係數 導熱係數是是物質導熱能力的量度,又稱熱導率。例如,銅的導熱係數是387 W/mK。
  • 《物理提高》熱量傳遞的三種方式是什麼
    熱傳遞的三種基本形式為:熱傳導、熱輻射和熱對流。只要在物體內部或物體間有溫度差存在,熱能就必然以以上三種方式中的一種或多種從高溫到低溫處傳遞。
  • 我的世界:讓紅石信號能垂直傳遞!除了螺旋階梯還能使用觀察者?
    我的世界的紅石建築不可能都是只需要電流橫向傳遞的,有的是需要紅石信號向上或者向下傳遞的建築。這種時候,玩家大多會先選擇最好想到的方法,就是樓梯或者螺旋階梯的模式,再之後慢慢改掉。那第一種就先說這種用樓梯、旋梯給紅石粉搭路的方法。
  • 迷你世界傳遞漏鬥怎麼做 傳遞漏鬥作用
    導 讀 迷你世界傳遞漏鬥怎麼製作?下面為大家帶來的是傳遞漏鬥的合成表以及作用。
  • 我們能夠超越光速傳遞信息嗎?
    雖然我不太確定「信息」到底是什麼意思,但是我想到了一個與她描述相悖的思想實驗。假設我住在地球上,我有個朋友住在冥王星上。如果我要向他發一條信息,我通常可以用無線電與他聯繫,但是無線電的傳播速度不能超過光速,所以信息需要好幾分鐘才能到達冥王星。然而,假設我有一根很長很長的杆子,從地球一直連接到冥王星。
  • 我們能夠超越光速傳遞信息嗎?
    雖然我不太確定「信息」到底是什麼意思,但是我想到了一個與她描述相悖的思想實驗。假設我住在地球上,我有個朋友住在冥王星上。如果我要向他發一條信息,我通常可以用無線電與他聯繫,但是無線電的傳播速度不能超過光速,所以信息需要好幾分鐘才能到達冥王星。然而,假設我有一根很長很長的杆子,從地球一直連接到冥王星。
  • 了解一階高通濾波器傳遞函數
    簡要回顧一下:通過對S域電路的分析,可以得到低通濾波器的輸入輸出特性表達式;電路的VOUT/VIN表達式是濾波器的傳遞函數,如果將該表達式與標準化形式進行比較,可以快速確定兩個關鍵參數,即截止頻率和最大增益;傳遞函數可以寫成分子多項式除以分母多項式,分子多項式的根是傳遞函數的零,分母多項式的根是傳遞函數的極點。
  • 「懷柔一號」這樣傳遞引力波「情報」
    「懷柔一號」GECAM衛星科學應用系統副總師鄭世界介紹,通常情況下,衛星上探測得到的天文現象和天文數據,必須通過地面的接收天線下傳,地面科研人員才能進一步分析處理。對GECAM衛星來說,它的軌道周期是90分鐘,即約90分鐘繞地球一圈,但每圈中最多只有10分鐘會經過中國境內的數傳接收站。
  • 人類能在食物鏈頂端立足,不只是因為智慧,信息傳遞同樣不可或缺
    所以,社會化的定義其實就是在具備一定「智慧」的基礎上還能夠同時擁有一套獨特的「信息傳遞」方式。不過人類的精明之處就是憑藉因「智慧」而衍生出的「創造力」,對這種「先天本能行為」進行了一次次的改良和升級。由此也就衍生出了「動物傳播」和「人類傳播」的分化。
  • 電子傳遞鏈
    呼吸鏈上電子傳遞載體的排列是有一定順序和方向的,電子傳遞的方向是從氧還電勢較負的化合物流向氧化還原電勢較正的化合物,直到氧。氧是氧化還原電勢最高的受體,最後氧被還原成水。電子傳遞鏈在原核細胞存在於質膜上,在真核細胞存在於線粒體的內膜上。