亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Golang:使用或不使用分配附加切片

Golang:使用或不使用分配附加切片

Go
慕森王 2021-11-22 18:08:32
Go 的append()函數僅在給定切片的容量不足時分配新的切片數據。這可能會導致意外行為(至少對我作為 golang 新手而言):package mainimport (    "fmt")func main() {    a1 := make([][]int, 3)    a2 := make([][]int, 3)    b := [][]int{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}    common1 := make([]int, 0)    common2 := make([]int, 0, 12) // provide sufficient capacity    common1 = append(common1, []int{10, 20}...)    common2 = append(common2, []int{10, 20}...)    idx := 0    for _, k := range b {        a1[idx] = append(common1, k...) // new slice is allocated        a2[idx] = append(common2, k...) // no allocation        idx++    }    fmt.Println(a1)    fmt.Println(a2) // surprise!!!}輸出:[[10 20 1 1 1] [10 20 2 2 2] [10 20 3 3 3]][[10 20 3 3 3] [10 20 3 3 3] [10 20 3 3 3]]https://play.golang.org/p/8PEqFxAsMt那么,Go 中強制分配新切片數據或更精確地確保切片參數 toappend()保持不變的(慣用的)方式是什么?
查看完整描述

2 回答

?
慕斯709654

TA貢獻1840條經驗 獲得超5個贊

你可能對 Go 中切片的工作方式有一個錯誤的看法。


當您將元素附加到切片時,調用會append()返回一個新切片。如果沒有發生重新分配,兩個切片值——你調用的值append()和它返回的值——共享相同的后備數組,但它們的長度不同;觀察:


package main


import "fmt"


func main() {

    a := make([]int, 0, 10)

    b := append(a, 1, 2, 3)

    c := append(a, 4, 3, 2)

    fmt.Printf("a=%#v\nb=%#v\nc=%#v\n", a, b, c)

}

輸出:


a=[]int{}

b=[]int{4, 3, 2}

c=[]int{4, 3, 2}

所以, len(a) == 0, len(b) == 3, len(c) == 3, 和第二次調用append()owerw 寫了第一個所做的事情,因為所有的切片共享相同的底層數組。


關于后備數組的重新分配,規范很明確:


如果 s 的容量不足以容納附加值,則 append 分配一個新的、足夠大的底層數組,該數組既適合現有切片元素又適合其他值。否則, append 會重新使用底層數組。

由此可知:

  1. append() 如果被附加到的切片的容量足夠,則永遠不要復制底層存儲。

  2. 如果沒有足夠的容量,陣列將被重新分配。

也就是說,給定一個s要向其附加N元素的切片,如果 iff 則不會進行重新分配cap(s) - len(s) ≥ N。

因此,我懷疑您的問題不在于意外的重新分配結果,而在于 Go 中實現的切片概念。要吸收的代碼想法是append() 返回結果切片值,除非您完全了解影響,否則您應該在調用后使用該值。

我建議從這里開始以完全理解它們。


查看完整回答
反對 回復 2021-11-22
?
忽然笑

TA貢獻1806條經驗 獲得超5個贊

因此,控制內存分配的解決方案是明確地進行(這讓我想起 Go 比其他(腳本)語言更像是一種系統語言):


package main


import (

    "fmt"

)


func main() {


    a1 := make([][]int, 3)

    a2 := make([][]int, 3)

    b := [][]int{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}

    common1 := make([]int, 0)

    common2 := make([]int, 0, 12) // provide sufficient capacity

    common1 = append(common1, []int{10, 20}...)

    common2 = append(common2, []int{10, 20}...)


    idx := 0

    for _, k := range b {

        a1[idx] = append(common1, k...) // new slice is allocated

        

        a2[idx] = make([]int, len(common2), len(common2)+len(k))

        copy(a2[idx], common2)      // copy & append could probably be

        a2[idx] = append(a2[idx], k...) // combined into a single copy step

        

        idx++

    }


    fmt.Println(a1)

    fmt.Println(a2)

}

輸出:


[[10 20 1 1 1] [10 20 2 2 2] [10 20 3 3 3]]


[[10 20 1 1 1] [10 20 2 2 2] [10 20 3 3 3]]


https://play.golang.org/p/Id_wSZwb84


查看完整回答
反對 回復 2021-11-22
  • 2 回答
  • 0 關注
  • 198 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號