理解 Go 中的切片:append 操作的深入分析(篇1)

理解 Go 语言中 slice 的性质对于编程非常有益。下面,我将通过两个代码示例来解释切片在不同函数之间传递并执行 append 操作时的具体表现。

本篇为第 1 篇,当切片的容量 cap 充足时

第一份代码

slice1 的初始长度为 3,容量为 10

go 复制代码
func main() {
	slice1 := make([]int, 3, 10)
	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1))

	test1(slice1)
  
	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1))
  
  // 此时若访问 slice1[3] 则 panic,因为 len = 3,不可越界 len
  // fmt.Println( slice[3] )
}

func test1(slice2 []int) {
	// slice 2 对切片进行 append 操作
	slice2 = append(slice2, 1)
	fmt.Println("slice 2:", slice2, len(slice2), cap(slice2))
}

输出:

shell 复制代码
slice 1: [0 0 0] 3 10
slice 2: [0 0 0 1] 4 10
slice 1: [0 0 0] 3 10

我们可以观察到,在 main 函数中,slice2 对切片的改动并没有体现在 slice1 上,尽管它们明显操作的是同一底层数组。为什么会这样呢?

原来,在上述第一份代码中,slice2 = append(slice2, 1) 这一行操作是在函数 test1 内部执行的,所以它不会改变调用该函数的 main 函数中的 slice1 的长度 len = 3,只会改变 test1 函数内部的 slice2 长度 len = 4。因此,在 main 函数中,由于 slice1 的长度仍然是 3,我们自然无法"看到"第 4 个元素。

那么,如果在 main 函数中继续对 slice1 执行 append 操作会发生什么呢?答案是,它会直接覆盖掉 test1 函数中对第 4 个元素的赋值!具体的情况如下图所示:

第二份代码

有了上面的解释后,我们可以自然而然的写出下面代码验证上述逻辑:

go 复制代码
// 以下注释为执行时机,按顺序为 1 2 3:
func main() {
	slice1 := make([]int, 3, 10)
	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1))

	go test1(slice1)
	time.Sleep(1 * time.Second)

	slice1 = append(slice1, 2) // 2

	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1)) // 2

	time.Sleep(4 * time.Second)
}

func test1(slice2 []int) {
	slice2 = append(slice2, 1) // 1
	time.Sleep(2 * time.Second)
	fmt.Println("slice 2:", slice2, len(slice2), cap(slice2)) // 3
}

输出:

shell 复制代码
slice 1: [0 0 0] 3 10
slice 1: [0 0 0 2] 4 10
slice 2: [0 0 0 2] 4 10

结论

  • 当我们在函数 A 中将 slice1 传递给函数 B 并在 B 中执行 append 操作时,只要底层数组没有扩张,就会在原数组的基础上进行追加,此时 B 函数中的 len 为 4。
  • 虽然 A 和 B 函数共享一个底层数组,但由于 A 函数的 len 保持为 3,因此我们不能访问数组的第 4 位元素,否则会引发 panic。
  • 当我们在 A 函数中也执行 append 操作时,A 函数会直接覆盖底层数组的第 4 位数值,从而直接覆盖了 B 函数所赋的值。
相关推荐
勤奋的知更鸟10 分钟前
Java编程之原型模式
java·开发语言·原型模式
冒泡的肥皂13 分钟前
强大的ANTLR4语法解析器入门demo
后端·搜索引擎·编程语言
珂朵莉MM23 分钟前
2021 RoboCom 世界机器人开发者大赛-高职组(初赛)解题报告 | 珂学家
java·开发语言·人工智能·算法·职场和发展·机器人
香蕉炒肉38 分钟前
Java优化:双重for循环
java·开发语言
IT_陈寒38 分钟前
Element Plus 2.10.0 重磅发布!新增Splitter组件
前端·人工智能·后端
傍晚冰川1 小时前
FreeRTOS任务调度过程vTaskStartScheduler()&任务设计和划分
开发语言·笔记·stm32·单片机·嵌入式硬件·学习
PingdiGuo_guo1 小时前
C++智能指针的知识!
开发语言·c++
黄雪超1 小时前
JVM——打开JVM后门的钥匙:反射机制
java·开发语言·jvm
有梦想的攻城狮1 小时前
spring中的@RabbitListener注解详解
java·后端·spring·rabbitlistener
Java水解1 小时前
MySQL DQL全面解析:从入门到精通
后端·mysql