go 语言切片 深入理解实例
go语言切片 深入理解实例
参考https://blog.csdn.net/qq_63730529/article/details/138169657#:~:text=%E4%BD%BF%E7%94%A8%E5%88%87%E7%89%87%E5%AD%97%E9%9D%A2%E9%87%8F%E5%88%9B%E5%BB%BA%E5%88%87
package mainimport "fmt"func SliceRise(s []int) {s = append(s, 0)for i := range s {s[i]++}
}func main() {s1 := []int{1, 2}s2 := s1s2 = append(s2, 3)SliceRise(s1)SliceRise(s2)fmt.Println(s1,s2)
}
1. SliceRise
函数
- 参数:s []int 是一个整数切片。
- 功能:
s = append(s, 0) 将一个 0 追加到切片 s 的末尾。
for i := range s 遍历切片 s 的每一个元素,并将每个元素的值加 1。
2. main
函数
- 初始化切片:
s1 := []int{1, 2} 创建了一个包含两个元素的切片 s1,初始值为 [1, 2]。
s2 := s1 将 s1 的引用赋值给 s2,这意味着 s1 和 s2 指向同一个内存地址。 - 修改切片:
s2 = append(s2, 3) 将 3 追加到 s2 的末尾。由于切片的动态特性,这会导致 s2 分配新的内存地址,但 s1 仍然指向原来的内存地址。因此,s1 和 s2 不再共享相同的内存地址。
3. 结果
[1, 2] [2, 3, 4]
SliceRise 函数在内部对切片进行了修改,但由于 Go 语言的切片传递机制,这些修改不会影响到外部的切片变量。
- 切片传递机制:
在 Go 中,切片是按值传递的,这意味着传递给函数的是切片的副本。虽然这个副本可以修改底层的数组,但对切片本身的任何修改(如 append 操作)不会影响到原始切片。
💡 💡 💡 个人理解:
调用SliceRise函数时s和s1、s2是独立的,len(s1)始终是2,len(s2)始终是3
4. AI解答
1. 初始状态
s1 := []int{1, 2} // len=2, cap=2
s2 := s1 // s2与s1共享底层数组
此时内存布局:
s1 := []int{1, 2} // len=2, cap=2
s2 := s1 // s2与s1共享底层数组
2. 第一次append
s2 = append(s2, 3) // 触发扩容
由于原切片容量不足(cap=2,len=2),append会触发扩容,创建新底层数组:
s1 → [1, 2] (原数组不变)
s2 → [1, 2, 3] (新数组,容量=4)
3. 第一次调用SliceRise(s1)
SliceRise(s1) // 传入s1 = [1, 2]
函数内:
- append(s, 0)触发扩容(原容量已满)
- 创建新数组 [1, 2, 0]
- 参数s是副本,不影响外部s1
- 遍历增加元素值 → [2, 3, 1]
- 函数返回后,s1仍保持 [1, 2]
4. 第一次调用SliceRise(s2)
SliceRise(s2) // 传入s2 = [1, 2, 3]
函数内:
- append(s, 0)不扩容(容量足够)
- 直接修改原数组 → [1, 2, 3, 0]
- s和s2共享底层数组
- 遍历增加元素值:
- s[0]++ → 1→2
- s[1]++ → 2→3
- s[2]++ → 3→4
- s[3]++ → 0→1
- 但s2的len仍是3(因为传入时len=3)
- 所以s2看到的是 [2, 3, 4]
- 最后一个元素1不可见(因为len未增加)
5. 最终结果
fmt.Println(s1, s2) // 输出: [1 2] [2 3 4]
🔥🔥🔥 关键点总结
- len不自动扩展:虽然底层数组被修改为 [2, 3, 4, 1],但s2的len仍然是3
- 可见范围:s2只能看到前3个元素 [2, 3, 4]
- 值传递本质:函数内对切片头的修改(包括len)不会影响外部
⚠️ 这个例子很好地展示了Go切片在函数传递和append操作时的行为特点,是面试中考察切片理解的经典案例。
5. 改进
1. 接收返回值
func SliceRise(s []int) []int {s = append(s, 0)for i := range s {s[i]++}return s
}func main() {s1 := []int{1, 2}s2 := s1s2 = append(s2, 3)s1 = SliceRise(s1)s2 = SliceRise(s2)fmt.Println(s1, s2)
}
结果:
[2 3 1] [2 3 4 1]
2. 传指针
package mainimport "fmt"func SliceRise(s *[]int) {*s = append(*s, 0)for i := range *s {(*s)[i]++}
}func main() {s1 := []int{1, 2}s2 := s1s2 = append(s2, 3)SliceRise(&s1)SliceRise(&s2)fmt.Println(s1, s2)
}
结果:
[2 3 1] [2 3 4 1]