你不知道的 Go 之 slice

你不知道的 Go 之 slice
最新回答
秋水墨凉

2023-03-01 19:10:23

Go 语言中的切片(slice)是一种动态数组,其底层实现涉及指针、长度和容量。以下是关于 Go 切片的详细解析:

  • 底层结构:切片由三个字段组成:

    array:指向底层数组的指针。

    len:切片的当前长度,可通过 len() 函数获取。

    cap:切片的最大容量,即在不扩容的情况下最多能容纳的元素数量,可通过 cap() 函数获取。

  • 创建方式

    var 声明:创建一个值为 nil 的切片,此时 array 为空指针,len 和 cap 均为 0。

    字面量:直接指定所有元素,此时 len 和 cap 等于元素个数。

    make 函数:可指定长度和容量,格式为 make([]type, len[, cap])。

    切片操作符:从现有数组或切片中切取一部分,格式为 [low:high],注意区间是左闭右开的。

  • 添加元素

    使用 append() 函数向切片添加元素。如果剩余空间足够,则直接添加;否则,进行扩容,分配更大的数组空间,复制旧元素后再添加。

  • 常见问题

    空切片与 nil 的关系

    var s1 []uint32 创建的切片等于 nil。

    s2 := make([]uint32, 0) 创建的切片不等于 nil,因为 make() 为其分配了空间。

    传值还是传引用

    切片传递的是 runtime.slice 结构的值,但底层数组是共享的。因此,修改切片元素会影响所有共享该数组的切片。

    扩容策略

    当前容量小于 1024 时,扩容为原来的 2 倍;大于等于 1024 时,逐次增加 0.25 倍,直到满足需求。

    字符串切片

    切片操作符可以切取字符串,但返回的是字符串而非切片,因为字符串是不可变的。

    底层数据共享

    使用切片操作符时,若上界超过原切片的长度但未超过容量,新切片会与原切片共享底层数组的部分数据,可能导致意外修改。Go 1.2 引入了 [low:high:max] 语法来限制新切片的容量,避免此问题。

  • 总结

    理解切片的底层结构有助于避免常见误区。

    切片传递的是结构体的值,但底层数组是共享的,需注意数据修改的影响。

    使用 append() 时应接收返回值,以更新切片的 array、len 和 cap 字段。

    扩容策略和切片操作符的使用需谨慎,以避免不必要的性能开销或数据覆盖问题。

通过掌握这些细节,可以更高效、安全地使用 Go 切片。