真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Go語言中slice作為參數(shù)傳遞時遇到的問題有哪些

這篇文章將為大家詳細(xì)講解有關(guān)Go語言中slice作為參數(shù)傳遞時遇到的問題有哪些,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

創(chuàng)新互聯(lián)專注于臺兒網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供臺兒營銷型網(wǎng)站建設(shè),臺兒網(wǎng)站制作、臺兒網(wǎng)頁設(shè)計、臺兒網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務(wù),打造臺兒網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供臺兒網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

首先還是從最簡單的說起,看下面代碼:

func main() {
 a := []int{7,8,9}
 fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
 ap(a)
 fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
}

func ap(a []int) {
 a = append(a, 10)
}

以上代碼的輸出是什么呢?

我這里不賣關(guān)子了直接說,再調(diào)用ap函數(shù)進(jìn)行append操作后,a依然是[]int{7,8,9}。原因很簡單,Go中沒有引用傳遞全是值傳遞,值傳遞意味著傳遞的是數(shù)據(jù)的拷貝。這句話新手可能稍微有點云里霧里,而實際情況又比較詭異,比如說下面代碼:

func main() {
  a := []int{7,8,9}
  fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
  ap(a)
  fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
}

func ap(a []int) {
  a[0] = 1
  a = append(a, 10)
}

這時ap后再輸出a,會看到a[0]變成了1,但a的cap依然是3,看起來10并沒有被append進(jìn)去?

這看起來就比較匪夷所思了,不是說值傳遞嗎,為什么還是影響外部變量的值了呢?按理說要么都變要么都不變才說得過去啊。

這實際上并不是匪夷所思,因為Go和C不一樣,slice看起來像數(shù)組,實際上是一個結(jié)構(gòu)體,在源碼中的數(shù)據(jù)結(jié)構(gòu)是:

type slice struct {
 array unsafe.Pointer
 len int
 cap int
}

這個結(jié)構(gòu)體其實也很好理解,array是一個真正的數(shù)組指針,指向一段連續(xù)內(nèi)存空間的頭部,len和cap代表長度和容量。
換句話說,你看起來在代碼里傳參時寫的是ap(a []int),實際上在代碼編譯期,這段代碼變成了ap(a runtime.slice)
你可以嘗試這么理解,把ap(a)替換成ap(array: 0x123, len: 3, cap: 3) ??梢院苊黠@的看到,傳遞到ap函數(shù)的三個參數(shù),僅僅是3個數(shù)值,并沒有和外部變量a建立任何引用關(guān)系。這便是值傳遞。

但是,你可能會疑惑,為什么我改了a[0]的值,也會在外面體現(xiàn)呢?其實看到這里你應(yīng)該已經(jīng)可以自己想明白了,因為array是一個地址值(比如0x123),這個地址傳入了ap函數(shù),但是它代表的地址0x123和外部a的0x123是一個內(nèi)存地址,這時候你修改a[0],實際上是修改0x123地址中存放的值,所以外部當(dāng)然會受影響了。

舉個形象點的例子,假設(shè)你是火車站貨物管理員,你管理的是第1到第3節(jié)車廂(車廂是互通的)的裝卸貨貨。有一天你生病了,找個人(叫A)臨時來接手一下。但是火車的貨不是誰想碰就碰的,你得有證明才行。于是你把你手上的證明原件復(fù)印了一份給A,同時把第一節(jié)車廂的鑰匙給A。由于剛好那幾天比較忙,站長又讓A也負(fù)責(zé)第四節(jié)車廂,于是A也得到了車廂4的證明原件。一段時間后,你生病回來,你依然只有1到3節(jié)車廂的證件,你可以看到最近A在1到3車廂搞的事情,但是你沒有資格去4車廂。

以上例子應(yīng)該可以很好的說明slice傳參的場景,記住,Go中只有值傳遞。

是不是就完事兒了呢?然而事情并沒有這么簡單。最近我工作時就遇到這個問題了。按照上面的舉例,雖然你沒有資格去查看4車廂,但是如果你好奇,你可以偷看啊,因為它們是連續(xù)的互通的,正如數(shù)組也是一段連續(xù)的內(nèi)存,于是就有這樣的代碼:

func main() {
  a := []int{}
  a = append(a, 7,8,9)
  fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
  ap(a)
  fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
  p := unsafe.Pointer(&a[2])
  q := uintptr(p)+8
  t := (*int)(unsafe.Pointer(q))
  fmt.Println(*t)
}

func ap(a []int) {
  a = append(a, 10)
}

雖然外部的cap和len并沒有改變,但是ap函數(shù)往同一段內(nèi)存地址append了一個10,那我是不是可以用比較trick的方法去偷看呢?比如找到a[2]的地址,往后挪一個int的長度,就應(yīng)該是ap函數(shù)新增的10了吧?這里需要注意,Go官網(wǎng)的server是32位的,所以在go playground執(zhí)行這段代碼時,int是4字節(jié)。

執(zhí)行結(jié)果和我預(yù)想的一樣!

但是問題接踵而至

func main() {
  a := []int{7,8,9}
  fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
  ap(a)
  fmt.Printf("len: %d cap:%d data:%+v\n", len(a), cap(a), a)
  p := unsafe.Pointer(&a[2])
  q := uintptr(p)+8
  t := (*int)(unsafe.Pointer(q))
  fmt.Println(*t)
}

func ap(a []int) {
  a = append(a, 10)
}

這和上面一個例子唯一的區(qū)別就是slice一開始是用[]int{7,8,9}這種方式初始化。執(zhí)行結(jié)果*t是3而不是10,這就比較困惑了。為啥?不是一段連續(xù)的內(nèi)存空間嗎?

這里其實涉及到的問題是slice的growth問題,當(dāng)append時發(fā)現(xiàn)cap不夠了,會重新分配空間,具體源碼參見 runtime/slice.go中的growslice函數(shù)。我這里就不講太多細(xì)節(jié),只講結(jié)果。當(dāng)發(fā)生growslice時,會給slice重新分配一段更大的內(nèi)存,然后把原來的數(shù)據(jù)copy過去,把slice的array指針指向新內(nèi)存。也就是說,假如之前的數(shù)據(jù)是存放到內(nèi)存地址 0x0 0x8 0x10,當(dāng)不發(fā)生growslice,新append的數(shù)值會存到0x18,然而當(dāng)發(fā)生growslice,以前的所有數(shù)據(jù)被copy到新的地址0x1000 0x1008 0x1010,新append的值放到0x1018了。

這時候你就可以理解為什么有時候用unsafe能拿到數(shù)據(jù),有時候拿不到了?;蛟S你可以理解為什么這個包叫做unsafe了。不過unsafe不是真的unsafe,是說如果你使用的姿勢不對就非常容易unsafe。但是如果姿勢優(yōu)雅,其實很safe。對于slice操作,如果要使用unsafe,千萬記得關(guān)注cap是否發(fā)送變化,它意味著內(nèi)存的遷移

關(guān)于“Go語言中slice作為參數(shù)傳遞時遇到的問題有哪些”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。


當(dāng)前題目:Go語言中slice作為參數(shù)傳遞時遇到的問題有哪些
網(wǎng)址分享:http://weahome.cn/article/ippchh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部