Go切片內容丟失問題

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

title: "Go切片內容丟失問題"
date: 2017-10-24
draft: false
categories: ["Golang"]
tags: [""]

感覺這篇文章有點標題黨的嫌疑, 文中要說的其實還是Go的值傳遞的問題, 不是發現了Go的某些驚天大bug,誤入的大神可以不用看下面的內容啦 !

起因是在編寫公司一個項目的過程中, 發現如果在結構體的不同方法之間調用其包含的切片,會出現切片內容不一致的情況.代碼類似這樣:

type T struct {    slice []int}func (t T) Append() {    t.slice = append(t.slice, 1)    fmt.Println("#1 slice:",t.slice)}func (t T) Show() {    fmt.Println("#2 slice:", t.slice)}func main() {    t := T{}    t.slice = make([]int, 0, 1)    t.Append()    t.Show()}

執行上面的代碼會輸出:

#1 slice: [1]#2 slice: []

按照正常的思維, 切片應該都是輸出[1]才對,但其實因為Go的值傳遞模型, 導致了結果與預料的出現了偏差.之前以為自己對這個問題徹底搞懂了,沒想到還是會糾結了一下, 下面記錄下解決問題的過程,希望自己徹底理解Go的值傳遞模型,也希望能對你有協助.

為上面的代碼添加一些列印輸出:

type T struct {    slice []int}func (t T) Append() {    fmt.Printf("Append: %p\n", &t)    t.slice = append(t.slice, 1)}func (t T) Show() {        fmt.Printf("Show: %p\n", &t)}func main() {    t := T{}    fmt.Printf("main: %p\n", &t)    t.slice = make([]int, 0, 1)    t.Append()    t.Show()}

輸出為

main: 0x1040a0b0Append: 0x1040a0c0Show: 0x1040a0d0

可以看到, 三個變數t的地址都不相同, 所以在調用某個類型的方法的時候, Go是會拷貝一個新的值,確實是使用值傳遞.

接下來列印slice的地址出來看看(我在這裡犯了一個錯誤)

type T struct {    slice []int}func (t T) Append() {    t.slice = append(t.slice, 1)    fmt.Printf("Append slice: %p\n", t.slice)}func (t T) Show() {         fmt.Printf("Show slice: %p\n", t.slice)}func main() {    t := T{}    t.slice = make([]int, 0, 1)    fmt.Printf("main slice: %p\n", t.slice)    t.Append()    t.Show()}

輸出為:

main slice: 0x10410020Append slice: 0x10410020Show slice: 0x10410020

看到這裡輸出的地址相同, 我誤以為調用類型方法的時候,對切片沒有使用值傳遞.但其實不然

Go的切片本質是也是一個結構體, 在源碼中可以看到它的定義:

// src/runtime/slice.gotype slice struct {    array unsafe.Pointer    len   int    cap   int}

我們上面輸出的3個地址都是這個結構體的地址.但不是說Go是值傳遞的嗎,為什麼這裡又傳了引用?
其實Go在這裡還是使用了值傳遞, 但對於slice, map, channel這三種應用類型, Go又不會做全部的複製處理, 如果這樣做顯然開銷太大.
Go默默得為我們建立了一個變數, 然後將這個變數的值指向了切片.說起來有點繞, 用一張圖來表示會更直白:


image.png

我們列印看中 t ' 對應的slice的地址看看:

type T struct {    slice []int}func (t T) Append() {    t.slice = append(t.slice, 1)    fmt.Printf("Append slice: %p\n", &t.slice)}func (t T) Show() {         fmt.Printf("Show slice: %p\n", &t.slice)}func main() {    t := T{}    t.slice = make([]int, 0, 1)    fmt.Printf("main slice: %p\n", &t.slice)    t.Append()    t.Show()}

輸出的3個地址確實都不同:

main slice: 0x1040a0b0Append slice: 0x1040a0c0Show slice: 0x1040a0d0

總結:
Go裡面的參數傳遞其實都是按照值傳遞來進行的, 理解這點可以解決很多開發中遇到的問題.至於如何解決上面代碼中切片元素不一致的問題 ? 很簡單, 把類型方法中的T改為指標類型就可以了.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.