Go-append使用方法及注意事項

來源:互聯網
上載者:User
文法 原型

在go/src/builtin/builtin.go中對append說明如下:

// The append built-in function appends elements to the end of a slice. If// it has sufficient capacity, the destination is resliced to accommodate the// new elements. If it does not, a new underlying array will be allocated.// Append returns the updated slice. It is therefore necessary to store the// result of append, often in the variable holding the slice itself://  slice = append(slice, elem1, elem2)//  slice = append(slice, anotherSlice...)// As a special case, it is legal to append a string to a byte slice, like this://  slice = append([]byte("hello "), "world"...)func append(slice []Type, elems ...Type) []Type
要點 用append把一個或多個元素添加在一個slice的後面; append的slice有一個underlying array,此即slice和array的關係; 另外slice有一個length和capability的概念; 如果slice還有剩餘的空間,可以添加這些新元素,那麼append就將新的元素放在slice後面的空餘空間中; 如果slice的空間不足以放下新增的元素,那麼就需要重現建立一個數組;這時可能是alloc、也可能是realloc的方式分配這個新的數組; 也就是說,這個新的slice可能和之前的slice在同一個起始地址上,也可能不是一個新的地址。——通常而言,是一個新的地址。 分配了新的地址之後,再把原來slice中的元素逐個拷貝到新的slice中,並返回。 類比

這個slice可以和C++的vector作類比。為此,可以參考《C++源碼剖析》4.2.5 vector的構造與記憶體管理。 樣本 代碼

package mainimport (    "fmt")type Employee struct {    name string    city string}func (e *Employee)String() string {    return fmt.Sprintf("Employee[address: %p, name: %s, city: %s]", e, e.name, e.city)}var g_employees = []Employee{}//var g_employees = make([]Employee, 0, 4)func add(employee *Employee) {    g_employees = append(g_employees, *employee)}func debug() {    count := len(g_employees)    for i := 0; i < count; i++ {        fmt.Printf("%d: %s\n", i, &(g_employees[i]))    }    fmt.Println()}func main() {    one := Employee{name: "name1", city:"city1"}    two := Employee{name: "name2", city:"city2"}    three := Employee{name: "name3", city:"city3"}    four := Employee{name: "name4", city:"city4"}    add(&one)    debug()    add(&two)    debug()    add(&three)    debug()    add(&four)    debug()}
運行結果
Case 1: var g_employees = []Employee{}0: Employee[address: 0xc42000e100, name: name1, city: city1]0: Employee[address: 0xc4200101c0, name: name1, city: city1]1: Employee[address: 0xc4200101e0, name: name2, city: city2]0: Employee[address: 0xc420086000, name: name1, city: city1]1: Employee[address: 0xc420086020, name: name2, city: city2]2: Employee[address: 0xc420086040, name: name3, city: city3]0: Employee[address: 0xc420086000, name: name1, city: city1]1: Employee[address: 0xc420086020, name: name2, city: city2]2: Employee[address: 0xc420086040, name: name3, city: city3]3: Employee[address: 0xc420086060, name: name4, city: city4]Case 2: var g_employees = make([]Employee, 0, 4)0: Employee[address: 0xc420074000, name: name1, city: city1]0: Employee[address: 0xc420074000, name: name1, city: city1]1: Employee[address: 0xc420074020, name: name2, city: city2]0: Employee[address: 0xc420074000, name: name1, city: city1]1: Employee[address: 0xc420074020, name: name2, city: city2]2: Employee[address: 0xc420074040, name: name3, city: city3]0: Employee[address: 0xc420074000, name: name1, city: city1]1: Employee[address: 0xc420074020, name: name2, city: city2]2: Employee[address: 0xc420074040, name: name3, city: city3]3: Employee[address: 0xc420074060, name: name4, city: city4]
分析

在調試列印中,特別把對象的地址列印處理。通過輸出,可以有如下結論: 在一個capability為0的空slice上面增加一個元素的時候,加1個、2個、3個的時候會重新分配儲存空間; 同時伴隨對象拷貝。 變化的對象與不變的指標

如果slice中存放的是對象,那麼在用指標處理這些對象的時候就要特別注意。比如現在全域變數採用第一種方式:

var g_employees = []Employee{}

main()代碼改為:

func main() {    one := Employee{name: "name1", city:"city1"}    two := Employee{name: "name2", city:"city2"}    three := Employee{name: "name3", city:"city3"}    four := Employee{name: "name4", city:"city4"}    add(&one)    debug()    var p *Employee    p = &(g_employees[0])    fmt.Printf("p: %s\n\n", p)    add(&two)    debug()    fmt.Printf("p: %s\n\n", p)    add(&three)    debug()    fmt.Printf("p: %s\n\n", p)    add(&four)    debug()    fmt.Printf("p: %s\n\n", p)}

在增加了一個元素之後,就定義一個指標指向該元素。運行結果:

0: Employee[address: 0xc420074000, name: name1, city: city1]p: Employee[address: 0xc420074000, name: name1, city: city1]0: Employee[address: 0xc420076140, name: name1, city: city1]1: Employee[address: 0xc420076160, name: name2, city: city2]p: Employee[address: 0xc420074000, name: name1, city: city1]0: Employee[address: 0xc420082000, name: name1, city: city1]1: Employee[address: 0xc420082020, name: name2, city: city2]2: Employee[address: 0xc420082040, name: name3, city: city3]p: Employee[address: 0xc420074000, name: name1, city: city1]0: Employee[address: 0xc420082000, name: name1, city: city1]1: Employee[address: 0xc420082020, name: name2, city: city2]2: Employee[address: 0xc420082040, name: name3, city: city3]3: Employee[address: 0xc420082060, name: name4, city: city4]p: Employee[address: 0xc420074000, name: name1, city: city1]

可以看到指標指向的地址雖然一直不變,但已經不是我們所預期的對象了。 變通

現在讓slice中儲存物件地址,而不再是對象。 樣本一

package mainimport (    "fmt")type Employee struct {    name string    city string}func (e *Employee)String() string {    return fmt.Sprintf("Employee[address: %p, name: %s, city: %s]", e, e.name, e.city)}var g_employees = []*Employee{}func add(employee *Employee) {    g_employees = append(g_employees, employee)}func debug() {    count := len(g_employees)    for i := 0; i < count; i++ {        fmt.Printf("%d: %s\n", i, g_employees[i])    }    fmt.Println()}func main() {    one := Employee{name: "name1", city:"city1"}    two := Employee{name: "name2", city:"city2"}    three := Employee{name: "name3", city:"city3"}    four := Employee{name: "name4", city:"city4"}    add(&one)    debug()    var p *Employee    p = g_employees[0]    fmt.Printf("p: %s\n\n", p)    add(&two)    debug()    fmt.Printf("p: %s\n\n", p)    add(&three)    debug()    fmt.Printf("p: %s\n\n", p)    add(&four)    debug()    fmt.Printf("p: %s\n\n", p)}

運行結果:

0: Employee[address: 0xc42000e1a0, name: name1, city: city1]p: Employee[address: 0xc42000e1a0, name: name1, city: city1]0: Employee[address: 0xc42000e1a0, name: name1, city: city1]1: Employee[address: 0xc42000e1c0, name: name2, city: city2]p: Employee[address: 0xc42000e1a0, name: name1, city: city1]0: Employee[address: 0xc42000e1a0, name: name1, city: city1]1: Employee[address: 0xc42000e1c0, name: name2, city: city2]2: Employee[address: 0xc42000e1e0, name: name3, city: city3]p: Employee[address: 0xc42000e1a0, name: name1, city: city1]0: Employee[address: 0xc42000e1a0, name: name1, city: city1]1: Employee[address: 0xc42000e1c0, name: name2, city: city2]2: Employee[address: 0xc42000e1e0, name: name3, city: city3]3: Employee[address: 0xc42000e200, name: name4, city: city4]p: Employee[address: 0xc42000e1a0, name: name1, city: city1]
樣本二
package mainimport (    "fmt")type Employee struct {    name string    city string}func (e *Employee)String() string {    return fmt.Sprintf("Employee[address: %p, name: %s, city: %s]", e, e.name, e.city)}var g_employees = []*Employee{}func add(index int) {    g_employees = append(g_employees,        &Employee{            fmt.Sprintf("name%d", index),            fmt.Sprintf("city%d", index)})}func debug() {    count := len(g_employees)    for i := 0; i < count; i++ {        fmt.Printf("%d: %s\n", i, g_employees[i])    }    fmt.Println()}func main() {    add(1)    debug()    var p *Employee    p = g_employees[0]    fmt.Printf("p: %s\n\n", p)    add(2)    debug()    fmt.Printf("p: %s\n\n", p)    add(3)    debug()    fmt.Printf("p: %s\n\n", p)    add(4)    debug()    fmt.Printf("p: %s\n\n", p)}

運行結果不變。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.