<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    咖啡伴侶

    呆在上海
    posts - 163, comments - 156, trackbacks - 0, articles - 2

    Go語(yǔ)言的傳參和傳引用

    Posted on 2013-09-16 09:23 oathleo 閱讀(4009) 評(píng)論(1)  編輯  收藏 所屬分類: Golang

    Go語(yǔ)言的傳參和傳引用[OSC源創(chuàng)會(huì)主題補(bǔ)充1]

    66人收藏此文章, 我要收藏發(fā)表于2天前(2013-09-14 22:10) , 已有1496次閱讀 ,共12個(gè)評(píng)論

    OSC源創(chuàng)會(huì)主題補(bǔ)充系列:

    1. Go語(yǔ)言的傳參和傳引用
    2. Go語(yǔ)言的類型轉(zhuǎn)換和類型斷言

    Go語(yǔ)言規(guī)范雖然很簡(jiǎn)單, 但是深入掌握Go語(yǔ)言卻需要很多底層知識(shí).

    本來(lái)第20期的武漢OSC源創(chuàng)會(huì)有Go語(yǔ)言的專題講座, 誰(shuí)知道說(shuō)取消就取消了.

    我最近也整理了一些Go語(yǔ)言資料, 有Go語(yǔ)言的歷史/現(xiàn)狀/未來(lái)發(fā)展的八卦和Go語(yǔ)言常見(jiàn)的問(wèn)題和陷阱兩個(gè)部分, 本來(lái)打算OSC源創(chuàng)會(huì)能和武漢的Gopher分享 下的, 誰(shuí)知道(由于不是贊助商也不是微軟的大牛)主辦方根本不給任何的機(jī)會(huì).

    100+人數(shù)的交流會(huì)基本都是扯淡, 還是小規(guī)模的討論沙龍比較靠譜, 以后再也不會(huì)去OSC源創(chuàng)會(huì)當(dāng)聽(tīng)眾了.

    現(xiàn)在計(jì)劃將各個(gè)小問(wèn)題暫時(shí)作為博客發(fā)表.

    傳參和傳引用的問(wèn)題

    很多非官方的文檔和教材(包括一些已經(jīng)出版的圖書(shū)), 對(duì)Go語(yǔ)言的傳參和引用的講解 都有很多問(wèn)題. 導(dǎo)致眾多Go語(yǔ)言新手對(duì)Go的函數(shù)參數(shù)傳參有很多誤解.

    而傳參和傳引用是編程語(yǔ)言的根本問(wèn)題, 如果這個(gè)問(wèn)題理解錯(cuò)誤可能會(huì)導(dǎo)致很多問(wèn)題.

    slice不是引用!

    首先, Go語(yǔ)言的函數(shù)調(diào)用參數(shù)全部是傳值的, 包括 slice/map/chan 在內(nèi)所有類型, 沒(méi)有傳引用的說(shuō)法.

    具體請(qǐng)看Go語(yǔ)言的規(guī)范:

    After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.

    from: http://golang.org/ref/spec#Calls

    什么叫引用?

    比如有以下代碼:

    var a Object doSomething(a) // 修改a的值 print(a) 

    如果函數(shù)doSomething修改a的值, 然后print打印出來(lái)的也是修改后的值, 那么就可以認(rèn)為doSomething是通過(guò)引用的方式使用了參數(shù)a.

    為什么slice不是引用?

    我們構(gòu)造以下的代碼:

    func main() {     a := []int{1,2,3}     fmt.Println(a)     modifySlice(a)     fmt.Println(a) }  func modifySlice(data []int) {     data = nil } 

    其中modifySlice修改了切片a, 輸出結(jié)果如下:

    [1 2 3] [1 2 3] 

    說(shuō)明a在調(diào)用modifySlice前后并沒(méi)有任何變化, 因此a必然是傳值的!

    為什么很多人誤以為slice是引用呢?

    可能是 因?yàn)楹芏嘈陆佑|Go語(yǔ)言的新手, 看到Go語(yǔ)言的文檔說(shuō)Go的切片和C語(yǔ)言的數(shù)組類型, 而C語(yǔ)言的數(shù)組是傳地址的(注意: 不是傳引用!).

    下面這個(gè)代碼可能是錯(cuò)誤的根源:

    func main() {     a := []int{1,2,3}     fmt.Println(a)     modifySliceData(a)     fmt.Println(a) }  func modifySliceData(data []int) {     data[0] = 0 } 

    輸出為:

    [1 2 3] [0 2 3] 

    函數(shù)modifySliceData確實(shí)通過(guò)參數(shù)修改了切片的內(nèi)容.

    但是請(qǐng)注意: 修改通過(guò)函數(shù)修改參數(shù)內(nèi)容的機(jī)制有很多, 其中傳參數(shù)的地址就可以修改參數(shù)的值(其實(shí)是修改參數(shù)中指針指向的數(shù)據(jù)), 并不是只有引用一種方式!

    傳指針和傳引用是等價(jià)的嗎?

    比如有以下代碼:

    func main() {     a := new(int)     fmt.Println(a)     modify(a)     fmt.Println(a) }  func modify(a *int) {     a = nil } 

    輸出為:

    0xc010000000 0xc010000000 

    可以看出指針a本身并沒(méi)有變化. 傳指針或傳地址也只能修改指針指向的內(nèi)存的值, 并不能改變指針本身在值.

    因此, 函數(shù)參數(shù)傳傳指針也是傳值的, 并不是傳引用!

    所有類型的函數(shù)參數(shù)都是傳值的!

    包括slice/map/chan等基礎(chǔ)類型和自定義的類型都是傳值的.

    但是因?yàn)?code style="padding: 2px 5px; margin: 0px 2px; border: 1px solid #dddddd; background-color: #f6f6f6; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: break-all;">slice和map/chan底層結(jié)構(gòu)的差異, 又導(dǎo)致了它們傳值的影響并不完全等同.

    重點(diǎn)歸納如下:

    • GoSpec: the parameters of the call are passed by value!
    • map/slice/chan 都是傳值, 不是傳引用
    • map/chan 對(duì)應(yīng)指針, 和引用類似
    • slice 是結(jié)構(gòu)體和指針的混合體

    • slice 含 values/count/capacity 等信息, 是按值傳遞

    • slice 中的 values 是指針, 按值傳遞
    • 按值傳遞的 slice 只能修改values指向的數(shù)據(jù), 其他都不能修改

    • 以指針或結(jié)構(gòu)體的角度看, 都是值傳遞!

    那Go語(yǔ)言有傳引用的說(shuō)法嗎?

    Go語(yǔ)言其實(shí)也是有傳引用的地方的, 但是不是函數(shù)的參數(shù), 而是閉包對(duì)外部環(huán)境是通過(guò)引用訪問(wèn)的.

    查看以下的代碼:

    func main() {     a := new(int)     fmt.Println(a)     func() {         a = nil     }()     fmt.Println(a) } 

    輸出為:

    0xc010000000 <nil> 

    因?yàn)殚]包是通過(guò)引用的方式使用外部環(huán)境的a變量, 因此可以直接修改a的值.

    比如下面2段代碼的輸出是截然不同的, 原因就是第二個(gè)代碼是通過(guò)閉包引用的方式輸出i變量:

    for i := 0; i < 5; i++ {     defer fmt.Printf("%d ", i)     // Output: 4 3 2 1 0 }  fmt.Printf("\n")     for i := 0; i < 5; i++ {     defer func(){ fmt.Printf("%d ", i) } ()     // Output: 5 5 5 5 5 } 

    像第二個(gè)代碼就是于閉包引用導(dǎo)致的副作用, 回避這個(gè)副作用的辦法是通過(guò)參數(shù)傳值或每次閉包構(gòu)造不同的臨時(shí)變量:

    // 方法1: 每次循環(huán)構(gòu)造一個(gè)臨時(shí)變量 i for i := 0; i < 5; i++ {     i := i     defer func(){ fmt.Printf("%d ", i) } ()     // Output: 4 3 2 1 0 } // 方法2: 通過(guò)函數(shù)參數(shù)傳慘 for i := 0; i < 5; i++ {     defer func(i int){ fmt.Printf("%d ", i) } (i)     // Output: 4 3 2 1 0 } 

    總結(jié)

    • 函數(shù)參數(shù)傳值, 閉包傳引用!
    • slice 含 values/count/capacity 等信息, 是按值傳遞
    • 按值傳遞的 slice 只能修改values指向的數(shù)據(jù), 其他都不能修改
    • slice 是結(jié)構(gòu)體和指針的混合體

    Feedback

    # re: Go語(yǔ)言的傳參和傳引用  回復(fù)  更多評(píng)論   

    2014-09-19 00:34 by ranh
    結(jié)論正確,過(guò)程不對(duì),slice是因?yàn)榈讓訑?shù)組相同所有有傳引用的效果,你舉例的slice是array好吧
    主站蜘蛛池模板: 免费无遮挡无码永久在线观看视频| jizz免费在线影视观看网站| 99re6热视频精品免费观看| 色噜噜亚洲精品中文字幕| 一级毛片a女人刺激视频免费| 亚洲精品国产福利一二区| 国产亚洲精品美女2020久久| 免费一级毛片一级毛片aa| 青青草国产免费国产是公开| 亚洲av区一区二区三| 亚洲精品黄色视频在线观看免费资源 | 亚洲一级在线观看| 成年人免费视频观看| 国产亚洲欧美在线观看| 亚洲精品视频久久久| 热久久这里是精品6免费观看| 亚洲国产精品无码中文字| 日本视频在线观看永久免费| 久久久久久久亚洲Av无码| 日本成年免费网站| 亚洲欧美在线x视频| 国产亚洲精品福利在线无卡一| 无码av免费网站| 亚洲av成人一区二区三区| 色吊丝最新永久免费观看网站| 又硬又粗又长又爽免费看 | 亚洲另类自拍丝袜第1页| 免费高清在线爱做视频| 五月天婷婷免费视频| 亚洲三级电影网站| 日本大片在线看黄a∨免费| 美女无遮挡拍拍拍免费视频| 久久精品国产亚洲AV麻豆网站| 日本高清免费网站| 在线观看片免费人成视频无码| 中文文字幕文字幕亚洲色| 久久国产成人精品国产成人亚洲| 18禁止观看免费私人影院| 一级午夜a毛片免费视频| 亚洲中文无码线在线观看| 亚洲精品无码成人片在线观看|