聊聊后端面試那些事
Go中都是值傳遞-【公粽號:堆棧future】
我看在技術(shù)大群里面有人問我,到底值類型,引用類型以及指針類型有什么區(qū)別?為什么別人說函數(shù)傳參是引用傳遞或者值傳遞,有的人說是指針傳遞?
想要徹底搞清楚這個問題,你得先通過表現(xiàn)來理解一下,然后在深入源碼理解下你很快就會明白的。
所以網(wǎng)上說的有可能都是錯誤的,大家千萬別被誤導!
我們接下來重點解決函數(shù)傳遞參數(shù)到底是值傳遞還是其他類型傳遞!
三者區(qū)別
值類型 就是變量賦值的時候?qū)⒅苯荧@得一個真實的數(shù)據(jù)副本,請大家再次看清楚,真實的"數(shù)據(jù)"副本。比如
var int a = 10,b:=a這就相當于a持有10,b持有a的數(shù)據(jù)的副本 那就將10拷貝到b指向的內(nèi)存。引用類型 就是僅僅是把對象的引用賦給變量,這樣就可能導致多個變量引用到一個實際對象實例上。比如
a := make([]int, 0),b:=a[2:],那么a和b持有的就是底層對象的引用,說白了a和b就是底層對象的別名。指針類型 就是賦給變量的是一個內(nèi)存地址,這個地址指向真實的數(shù)據(jù),比如
var int a = 10,p:=&a,這個p就是指針變量,它存儲的就是a的地址。
注意:在Go中弱化了指針,所以一般只需要關(guān)注值和引用類型就可以。
函數(shù)傳參
首先聲明一點:在Go中除了slice,map,channel類型之外的變量都是值類型。
那就好說了,引用類型就三種:slice, map和channel,其他都是值類型。
在Go中引用類型的變量初始化默認是make,但是new也是可以的,比如:
<pre data-tool="mdnice編輯器" style="box-sizing: border-box !important; margin: 10px auto 1rem; padding: 0px; outline: 0px; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12.25px; overflow: auto; display: block; color: rgb(33, 37, 41); max-width: 100%; overflow-wrap: break-word !important; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px;">`package main
import "fmt"
type A []int
func main() {
a := new(A)
a = append(a, 1)
fmt.Println(*a) //[1]
a1 := make([]int, 0)
a1 = append(a1, 1)
fmt.Println(a1) //[1]
}` </pre>
當然map以及channel也是可以用make和new的,大家下去試試哈。
至于make和new的區(qū)別大家下去自己看哈,這不是本文的重點。
接下來我們重點來聊聊函數(shù)傳參的問題,我們都知道值傳遞是copy一份,所以這個大家都能理解,我們重點講解大家疑惑的問題,引用類型的傳參問題。
首先大家看一個例子,即看看它的表現(xiàn)是什么:
<pre data-tool="mdnice編輯器" style="box-sizing: border-box !important; margin: 10px auto 1rem; padding: 0px; outline: 0px; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12.25px; overflow: auto; display: block; color: rgb(33, 37, 41); max-width: 100%; overflow-wrap: break-word !important; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px;">`package main
import "fmt"
func CopyList(a []int) {
a = append(a, 2)
fmt.Printf("copylist is %p \n", &a)
fmt.Println("copylist value is ", a)
}
func main() {
a := make([]int, 0)
a = append(a, 1)
fmt.Println("main value is ", a)
fmt.Printf("main address is %p \n", &a)
CopyList(a)
}
輸出:
main value is [1]
main address is 0xc0000a6018
copylist is 0xc0000a6048
copylist value is [1 2]` </pre>
我們通過結(jié)果分析下引用傳遞會有什么影響。
- 首先函數(shù)參數(shù)是
a []int- main中定義的引用類型變量是
a并且也做了初始化- 然后調(diào)用
CopyList(a)- 看在main中a的地址和在CopyList中a的地址,發(fā)現(xiàn)不一樣?為什么不一樣呢?因為Go語言在設(shè)計的時候明確表示
函數(shù)參數(shù)傳遞是值傳遞,所以相當于函數(shù)CopyList拷貝了一份變量,這個時候次a非比a。但是那你可能說:為什么拷貝了一份,但是從結(jié)果來看就是引用啊,因為函數(shù)CopyList改變了外面main中a的值。
答案就是Go在設(shè)計引用類型的時候,比如這三者map,slice和channel,他們的結(jié)構(gòu)體中并沒有直接保存數(shù)據(jù),而是保存了指向數(shù)據(jù)的指針,那么在函數(shù)中傳遞這三種的變量其實只是拷貝了一份它們自己的一份結(jié)構(gòu)體,但是結(jié)構(gòu)體里面具體保存的指針指向的地址還是同一份,沒有變化的。所以這也就說明函數(shù)中為什么可以改變引用類型變量的值了,因為底層指針指向的地址是同一個。
分析完了slice,其它兩個都比較簡單,這里就不再演示了,大家下去自己嘗試。
<pre data-tool="mdnice編輯器" style="box-sizing: border-box !important; margin: 10px auto 1rem; padding: 0px; outline: 0px; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12.25px; overflow: auto; display: block; color: rgb(33, 37, 41); max-width: 100%; overflow-wrap: break-word !important; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px;">type slice struct { array unsafe.Pointer //指針 到時候slice初始化的時候會用mallocgc分配內(nèi)存,把這塊內(nèi)存地址保存到array中。 len int cap int } </pre>
map和channel里面也是有指針,指向一塊內(nèi)存空間。
小結(jié)
大家一定要記住在Go中函數(shù)傳遞參數(shù)一定是值傳遞,千萬別搞混淆了,至于為什么上面也說清楚了,如果大家還不懂就進群,大牛比較多為你繼續(xù)解惑哈。