Go陷阱|第1章: append

  • 原创
  • Madman
  • /
  • /
  • 0
  • 5909 次阅读

Golang.jpg

Synopsis: 使用内置函数 append() 在切片末尾追加元素并返回新切片时,要注意是否会创建新的底层数组

1. 问题现象

package main

import "fmt"

func main() {
    s := []byte("ca")
    fmt.Printf("%v len=%d cap=%d\n", s, len(s), cap(s)) // [99 97] len=2 cap=2

    s1 := append(s, 'r')
    s2 := append(s, 't')

    fmt.Println(string(s1))
    fmt.Printf("%v len=%d cap=%d\n", s1, len(s1), cap(s1))

    fmt.Println(string(s2))
    fmt.Printf("%v len=%d cap=%d\n", s2, len(s2), cap(s2))
}

用我的电脑上的 cmd 执行输出结果正常:

[99 97] len=2 cap=2

car
[99 97 114] len=3 cap=8

cat
[99 97 116] len=3 cap=8

但是,用 Golang Playground 执行输出结果不正常:

[99 97] len=2 cap=8

cat
[99 97 116] len=3 cap=8

cat
[99 97 116] len=3 cap=8

2. 原因分析

如果切片引用的底层数组还有足够的可用容量时,用 append() 函数不会创建新的底层数组

在无需扩容时,append() 函数返回的是指向原底层数组的新切片;而在需要扩容时,append() 函数返回的是指向新底层数组的新切片。只要新长度不会超过当前切片的原容量,那么使用 append() 函数对其追加元素时就不会引起扩容,这只会使紧邻切片窗口右边的(原底层数组中的)元素被新的元素替换掉!

给类型分配多少内存与编译代码的机器的体系结构有关。例如,根据编译所在的机器的体系结构,一个 int 值的大小可能是 8 字节(64 位),也可能是 4 字节(32 位)

由于我的电脑给切片 []byte("ca") 所引用的底层数组分配了 2 个字节(它的容量为 2),所以 s1 和 s2 使用 append() 函数时都会各自创建一个新的底层数组;而 Playground 给切片 []byte("ca") 所引用的底层数组分配了 8 个字节(它的容量为 8),所以 s1 和 s2 使用 append() 函数时还是引用原来的同一个底层数组

3. 解决方案

s1 和 s2 分别对不同的切片(不同的底层数组)进行 append

package main

import (
    "fmt"
)

func main() {
    const str = "ca"

    s1 := append([]byte(str), 'r')
    s2 := append([]byte(str), 't')

    fmt.Println(string(s1))
    fmt.Printf("%v len=%d cap=%d\n", s1, len(s1), cap(s1))

    fmt.Println(string(s2))
    fmt.Printf("%v len=%d cap=%d\n", s2, len(s2), cap(s2))
}
分类: Go 陷阱
标签: append() slice golang
未经允许不得转载: LIFE & SHARE - 王颜公子 » Go陷阱|第1章: append

分享

作者

作者头像

Madman

如需 Linux / Python 相关问题付费解答,请按如下方式联系我

0 条评论

暂时还没有评论.