按值傳遞 vs 按指標傳遞

2022-02-20 09:54:25 字數 3178 閱讀 5239

變數賦值有兩種方式:按值傳遞、按"指標"傳遞(指標也常稱為"引用")。不同的程式語言賦值的方式不一樣,例如python是按"指標"傳遞的,go是按值傳遞的。

注意,"指標"加了引號,因為它不是真正的按指標拷貝,見下文分析。

引數傳值其實也是變數賦值的過程,只不過引數是函式的本地變數而已。

按值傳遞的意思是每次賦值都拷貝記憶體中完整的資料結構物件,這時在記憶體中會儲存兩份內容完全相同,但位址不同的資料物件。

按"指標"傳遞的意思是每次賦值都只拷貝記憶體中資料結構物件的位址,這個位址占用乙個機器字長(乙個機器字長,在32位cpu上為32bit共4位元組,64位則64bit共8位元組),當然有些資料結構除了指標還包括其它屬性,這時可能會占用數個機器字長。總之,按"指標"傳遞時,由於只拷貝乙份能表示資料物件的屬性(比如位址),拷貝的內容非常少,速度非常快。但必須注意,拷貝"指標"後,記憶體中只有乙份資料物件,但將有兩份完全相同的指向記憶體中資料物件的"指標",無論是通過哪個"指標"去修改資料物件,都會影響另乙個。

對於那些不支援操作指標的語言,通常會將按"指標"傳遞稱為"淺拷貝(shallow copy)",然後額外提供乙個函式或工具實現按指傳遞,這稱為"深拷貝(deep copy)"。

例如:

a=10

b=a

首先會在記憶體中劃分乙個格仔用來建立資料物件10,然後將這個資料物件的位址儲存到變數a中。

如果是按值拷貝的語言,則會在記憶體中拷貝乙份資料物件10的副本,再將這個副本資料物件的位址儲存到b中。

顯然,a和b儲存的位址是不一樣的,記憶體中也有兩份內容完全相同的資料物件10。所以,修改a的值時不會影響b的值,修改b的值時不會影響a。

如果是按"指標"拷貝的語言,則會直接拷貝a中的位址並儲存到b中。

因為a和b的位址都一樣,所以,修改a的值會影響b,修改b的值會影響a。

也許你已經發現了,按"指標"傳遞時,雖然a、b儲存的位址相同,但如果a=11,a將指向新的資料物件,而b仍然指向10,即b=10,修改a並沒有影響b。這是因為數值是不可變的,無法在原始的記憶體位址處修改,也就是無法將10替換成11,所以只要想修改這種不可變的物件就一定會建立新資料物件。對此,有兩方面需要說明。

一方面,有些資料物件是可以在原始記憶體位址處直接進行替換修改的(例如python中的列表)。假設,某程式語言對數值也是可原處修改的,那麼a=11將會在記憶體中將10替換成11,而不會新建立另乙個資料物件11。

另一方面,上面的"按指標傳遞"並非是真正的按指標傳遞,而是按引用傳遞,或者說是按位址傳遞。這就是前文"按指標傳遞"中的"指標"都加上了引號的原因。

真正的指標是額外儲存的,是占用空間的,和變數不同(變數儲存了位址,在棧空間中),它是儲存在堆記憶體中的。對於支援指標操作的語言(如c、c++、go等),需要使用語法獨立生成資料物件的指標,這類語言一般都能直接在原處修改資料物件。例如:

a=10

b=&a

其中b=&a表示生成a所指向(因為a儲存了位址)資料物件的乙個額外的指標,這個指標中儲存了資料物件的位址,然後將這個指標賦值給b,這時b儲存的是指標的位址,而不是資料物件的位址。

這時,修改a,或者修改b都會影響另一方,因為支援指標操作的語言一般都支援原處修改:

a=11

print(*b) /* 輸出11 */

其中*b表示解除指標的引用,也就是取得資料物件的內容。

再回到按"指標"傳遞的拷貝方式,雖然它不是真正的拷貝指標,而是拷貝位址,但對於那些支援原處修改的資料物件,它們達到的效果和真實的指標傳遞是一樣的。例如,陣列、python的列表。

# 以下為python**:

l1=[1,2,3,4]

l2=l1

l2[0]=11

print(l1) # 輸出:[11,2,3,4]

支援指標操作的語言,通過指標修改資料時,是直接在原始位址塊上修改為新資料的。例如:

func main()
結果:

但是python中的可變物件(比如列表),雖然俗稱"原處修改",但並非真的原處修改,而是在堆記憶體中新建立乙個資料物件,並將它作為可變物件的一部分,所以可變物件整體的位址沒有改變,但內部元素的位址已經改變了,也就是舊的元素物件被**。

>>> l=[222,333,444,555]

>>> id(l),id(l[1])

(44652184, 43798256)

>>> l[1]=3333

>>> id(l),id(l[1])

(44652184, 43798240)

JavaScript 按值傳遞 按引用傳遞

1 值的比較 引用的比較 首先,原始值的比較是值的比較 只有在它們值相等的時候它們才相等 比如簡單的 var a1 10 var a2 10 console.log a1 a2 true 其次,物件的比較並非值的比較 物件的比較均是引用的比較,當且僅當它們引用同乙個基物件時,它們才相等。即使兩個物件...

按值傳遞和按引用傳遞

對於基本型別而言,是按值傳遞的 基本型別儲存在棧中 基本型別引數的傳遞和基本型別的複製一樣,傳遞的是值本身 var a 1 function test x test a console.log a 1按值傳遞的意思就是形參是實參的複製,形參實參互不干擾 so,雖然在函式test中a被修改,但是並沒有...

scala 引數傳遞 按值傳遞 按名傳遞

package leetcode import scala.util.random object function args def getrandom call by name 按名傳遞,傳入引數變數名,如果runbyname內部用到,再去求值 f string int long表示 該無參函式的...