亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Golang > 正文

Go語言中slice作為參數傳遞時遇到的一些“坑”

2020-04-01 18:58:35
字體:
來源:轉載
供稿:網友

前言

相信看到這個題目,可能大家都覺得是一個老生常談的月經topic了。一直以來其實把握一個“值傳遞”基本上就能理解各種情況了,不過最近遇到了更深一點的“小坑”,與大家分享一下。

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

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)}

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

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

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進去?

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

這實際上并不是匪夷所思,因為Go和C不一樣,slice看起來像數組,實際上是一個結構體,在源碼中的數據結構是:

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

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

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

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

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

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

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函數往同一段內存地址append了一個10,那我是不是可以用比較trick的方法去偷看呢?比如找到a[2]的地址,往后挪一個int的長度,就應該是ap函數新增的10了吧?這里需要注意,Go官網的server是32位的,所以在go playground執行這段代碼時,int是4字節。

執行結果和我預想的一樣!

但是問題接踵而至

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)}

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

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

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

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩在线观看免费高清完整版| 97视频在线观看免费| 成人女保姆的销魂服务| 久久亚洲欧美日韩精品专区| 久久精品这里热有精品| 一本一本久久a久久精品综合小说| 成人黄色片网站| 欧美大片免费观看| 亚洲性生活视频在线观看| 欧美大片va欧美在线播放| 国产视频久久久| 亚洲视频自拍偷拍| 一区二区欧美日韩视频| 日韩欧美在线视频日韩欧美在线视频| 精品亚洲一区二区三区在线观看| 亚洲欧美日韩在线高清直播| 91精品国产色综合| 久久影视电视剧免费网站清宫辞电视| 亚洲成人久久网| 国内外成人免费激情在线视频| 亚洲激情中文字幕| 亚洲第一精品久久忘忧草社区| 亚洲欧美中文字幕| 国产精品91久久久久久| 26uuu另类亚洲欧美日本老年| 日韩成人在线视频网站| 一本色道久久88综合日韩精品| 久久久久久久久综合| 久久视频在线视频| 国产一区二区丝袜高跟鞋图片| 91免费电影网站| 国产一区二区三区四区福利| 久久影院资源网| 亚洲一区二区久久久久久| 国产精品久久久久久久久久新婚| 亚洲国产毛片完整版| 91精品国产高清自在线看超| 亚洲第一级黄色片| 国产精品∨欧美精品v日韩精品| 国产激情综合五月久久| 国产精品久久久久一区二区| 国产精品嫩草视频| 美女啪啪无遮挡免费久久网站| 日韩免费观看网站| 日韩视频欧美视频| 国产美女精彩久久| 精品视频www| 亚洲女在线观看| 亚洲人免费视频| 91精品视频播放| 91av在线网站| 国产精品电影网站| 国产精品美女无圣光视频| 国产99久久精品一区二区 夜夜躁日日躁| 欧美一区二区三区免费观看| 日韩视频在线观看免费| 亚洲综合精品一区二区| 一区二区三区四区在线观看视频| 亚洲在线观看视频| 亚洲一区二区三区777| 久久久久久久久久婷婷| 久久精品99无色码中文字幕| 亚洲一区999| 亚洲欧美制服第一页| 777国产偷窥盗摄精品视频| 日韩av电影在线播放| 亚洲石原莉奈一区二区在线观看| 日韩在线播放视频| 另类色图亚洲色图| 97视频在线免费观看| 久久99久久99精品免观看粉嫩| 欧美激情国产精品| 综合网日日天干夜夜久久| 欧美国产日韩精品| 欧美精品18videosex性欧美| 日韩小视频在线观看| 国产精品扒开腿做爽爽爽男男| 亚洲国产私拍精品国模在线观看| 国产精品黄页免费高清在线观看| 国产精品av在线| 亚洲人成电影在线| 国产成人综合一区二区三区| 亚洲精品福利免费在线观看| 国产一区二区三区在线视频| 亚洲人成亚洲人成在线观看| 国产成人精品视频在线| 自拍视频国产精品| 欧美高清视频免费观看| 欧美日韩午夜激情| 中文字幕亚洲字幕| 国产精品一区电影| 国产在线拍揄自揄视频不卡99| 亚洲第一色中文字幕| 国产最新精品视频| 综合136福利视频在线| 亚洲欧洲视频在线| 日韩福利伦理影院免费| 亚洲自拍偷拍色图| 欧美日韩国产在线看| 97在线看免费观看视频在线观看| 欧美精品18videos性欧美| 亚洲精品成人免费| 成人免费视频97| 日韩二区三区在线| 久久在线免费视频| 久久久99久久精品女同性| 91wwwcom在线观看| 一个色综合导航| 在线精品91av| 国产精品久久久精品| 欧美黄色三级网站| 久久精品久久久久久国产 免费| 亚洲精品福利资源站| 98视频在线噜噜噜国产| 日本视频久久久| 亚洲精品99久久久久中文字幕| 日韩av电影院| 蜜臀久久99精品久久久无需会员| 69久久夜色精品国产69乱青草| 久久久www成人免费精品张筱雨| 亚洲大胆人体视频| 亚洲最大激情中文字幕| 亚洲一区二区在线播放| 国产精品色悠悠| 国产成人精品999| 国内精品久久久久久久| 亚洲人成网7777777国产| 国产精品日韩电影| 91超碰中文字幕久久精品| 日韩经典中文字幕在线观看| 国产亚洲人成网站在线观看| 亚洲国产一区二区三区在线观看| 中文字幕日本精品| 成人av在线天堂| 一区二区中文字幕| 亚州成人av在线| 欧美视频免费在线| 一本色道久久88亚洲综合88| 成人乱人伦精品视频在线观看| 亲爱的老师9免费观看全集电视剧| www日韩中文字幕在线看| 美女扒开尿口让男人操亚洲视频网站| 在线视频日本亚洲性| 亚洲精品视频在线播放| 97婷婷大伊香蕉精品视频| 538国产精品一区二区免费视频| 1769国产精品| 亚洲第一av网站| 欧美日韩中文字幕| 日韩一区二区福利| 亚洲欧美制服综合另类| 最近2019免费中文字幕视频三| www高清在线视频日韩欧美| 国产亚洲一区二区精品| 国产精品国产亚洲伊人久久| 亚洲视频999| 色婷婷综合成人av| 草民午夜欧美限制a级福利片| 色午夜这里只有精品| 91po在线观看91精品国产性色| 久久精品国产69国产精品亚洲| 日韩成人在线观看| 亚洲视频免费一区| 中文字幕视频一区二区在线有码|