17370845950

如何在Golang中使用数组和切片_Golang数组切片创建与操作
Go中数组是值类型、长度固定,切片是引用类型、底层为三字段结构体;数组传参复制全部数据,切片传参仅复制结构体;append必须接收返回值,nil切片可安全append。

Go 语言里没有“动态数组”这种东西,[]T 是切片(slice),[N]T 才是数组(array);二者底层不同、语义不同、传参行为也不同——混用会导致静默错误或性能意外。

数组是值类型,赋值和传参会复制整个内存块

声明 [3]int 表示一个固定长度为 3 的整数数组,它在栈上分配,大小确定。一旦声明,长度不可变。

常见误用:把大数组当作参数传给函数,结果每次调用都拷贝全部数据。

  • [1024]byte 作为函数参数?实际拷贝 1KB 内存
  • == 比较两个数组?可以,但逐元素比,慢且易被忽略长度差异
  • 想“扩容”数组?做不到。只能新建更大数组,再手动 copy
var a [3]int = [3]int{1, 2, 3}
b := a // b 是 a 的完整副本,修改 b 不影响 a
a[0] = 999
fmt.Println(a, b) // [999 2 3] [1 2 3]

切片是引用类型,底层指向数组,但自身是结构体

[]T 实际是运行时的一个三字段结构体:ptr(指向底层数组)、len(当前长度)、cap(容量)。它不拥有数据,只描述一段视图。

立即学习“go语言免费学习笔记(深入)”;

创建切片的常用方式有三种,行为差异明显:

  • make([]int, 3) → len=3, cap=3,底层数组长度至少为 3
  • make([]int, 3, 10) → len=3, cap=10,预留空间,后续 append 不触发扩容
  • a[1:4](从数组或切片截取)→ 新切片共享底层数组,修改可能互相影响
data := [5]int{0, 1, 2, 3, 4}
s1 := data[1:3]   // [1 2], cap=4(从索引1到数组尾)
s2 := data[2:4]   // [2 3], 与 s1 共享底层数组
s2[0] = 99
fmt.Println(s1) // [1 99] —— 被意外改了

append 不一定 realloc,但永远返回新切片

append 是切片唯一安全的“增长”操作。它检查容量是否足够:够就直接填,不够就分配新底层数组并 copy。关键点是——append 总是返回新切片,原变量不变。

典型错误:忘记接收返回值,导致追加失效。

  • s = append(s, x) ✅ 必须赋值回去
  • append(s, x) ❌ 原 s 不变,新切片被丢弃
  • 对从数组截取的切片 append,若超出原数组容量,会自动 malloc 新底层数组
s := []int{1, 2}
s = append(s, 3) // 必须赋值
s = append(s, 4, 5) // 支持多值
fmt.Println(s) // [1 2 3 4 5]

切片的零值是 nil,但 len/cap 都为 0,可直接 append

var s []int 声明的是 nil 切片,不是空切片。但它和 []int{}(空切片)在大多数场景下行为一致:len=0、cap=0、可 append、可遍历(不进循环体)。

区别只在底层:nil 切片的 ptr 为 nil;空切片的 ptr 指向某个有效地址(如内部零长数组)。极少需要区分,除非做指针比较或 JSON 序列化(nil 切片序列化为 null,空切片为 [])。

  • len(s) == 0 判断是否为空,而不是 s == nil
  • json.Marshal(nilSlice)nulljson.Marshal(emptySlice)[]
  • 向 nil 切片 append 是安全的,Go 会自动分配底层数组
var s1 []int
s2 := []int{}
fmt.Println(len(s1), cap(s1), s1 == nil) // 0 0 true
fmt.Println(len(s2), cap(s2), s2 == nil) // 0 0 false
s1 = append(s1, 1) // OK

最常被忽略的是:切片的 cap 不是“还能加多少”,而是“从当前起始位置开始,底层数组还剩多少可用空间”。截取、传递、append 时若没留意 cap 变化,可能意外复用旧内存,引发难以调试的数据污染。