Go 数组如何工作以及在 for-range 中如何变得棘手

这篇文章主要介绍了 Go 语言中数组的工作原理及相关特性:

  • 基本概念:Go 数组是固定大小的,元素类型相同且存储在连续的内存位置,通过起始地址和元素索引可快速访问每个元素。例如[5]byte{0, 1, 2, 3, 4},数组地址与首元素地址相同,元素地址间隔为 1 字节(元素类型为byte)。
  • 内存布局:以[5]byte{0, 1, 2, 3, 4}为例,展示了数组在内存中的布局,栈从高地址向低地址增长,从arr[4]arr[0]依次排列。通过unsafe包可直接访问内存,但仅用于教育目的,生产环境中需谨慎使用。
  • 数组类型:类型为T的数组本身不是一种类型,具有特定大小和类型T的数组才被视为一种类型,如[5]byte[4]byte在 Go 编译器中被视为不同类型。
  • 数组字面量初始化

    • 有多种初始化方式,如var arr1 [10]int(默认值初始化)、arr2 := [...]int{1, 2, 3, 4, 5}(根据值推断长度初始化)等。
    • 对于小于 4 个元素的数组,Go 采用局部代码初始化策略,逐个将值放入数组;对于大于 4 个元素的数组,编译器在二进制文件中创建静态表示,值存储在只读段。
    • 对于部分初始化的数组,如[5]int{1,2,3},Go 也会逐个放入值。
  • 数组操作

    • 数组长度编码在类型中,可通过len(a)获取长度,编译器在编译时就知道长度,实际在后台视为常量。
    • 切片是从数组获取的一部分,形式为[start:end:capacity],通常有[start:end]等变体。切片的新长度为end - start,新容量根据情况确定。例如b := a[1:3]b.array = &a[1]b.len = 3 - 1b.cap = 5 - 1
    • 超出边界的切片索引会导致 panic,如a[4:5]可获取长度为 1 的切片,a[5:]会得到空切片,但数组底层仍指向a[0]
  • 数组作为值类型:Go 中数组是值类型,将数组传递给函数时会复制整个数组,即使打印&a&a[0]地址相同。在for-range循环中,会复制数组,但v可看到复制的数组,原数组在循环后仍保持不变。若要在循环中修改数组,可使用指向数组的指针。
  • 相关资源与联系:作者是 VictoriaMetrics 的软件工程师 Phuong Le,文章风格注重清晰简单,若有疑问可在X(@func25)联系。还介绍了相关的 Go 系列文章及 VictoriaMetrics 监控服务,VictoriaMetrics 是开源的、节省成本的监控基础设施。
阅读 13
0 条评论