【golang】深入理解切片(slice)函数传递

问题

  1. 第一段代码
go 复制代码
func main() {
	s := make([]int, 0, 5)
	s = append(s, 1)
	fmt.Println("append slice:", s, len(s), cap(s))
}

这段代码中为长度为0,容量为5的切片append了一个1,切片未发生扩容,所以代码运行结果为切片第一个元素为1,长度为1,容量为5的切片。

运行结果:

go 复制代码
append slice: [1] 1 5
  1. 第二段代码
go 复制代码
func main() {
	s := make([]int, 0, 5)
	s = append(s, 1)
	fmt.Println("append slice:", s, len(s), cap(s))
	appendFunc(s)
	fmt.Println("appendFuncMain slice:", s, len(s), cap(s))
}

func appendFunc(s []int) {
	s = append(s, 1, 2, 3)
}

这段代码添加了一个appendFunc函数,函数功能为切片增加1,2,3三个元素。在主函数中调用了appendFunc函数并把切片s作为参数传递。

运行结果:

go 复制代码
append slice: [1] 1 5
appendFuncMain slice: [1] 1 5

为什么切片s的长度和值都没有变呢?

  1. 第三段代码
go 复制代码
func main() {
	s := make([]int, 0, 5)
	s = append(s, 1)
	fmt.Println("append slice:", s, len(s), cap(s))
	appendFunc(s)
	fmt.Println("appendFuncMain slice:", s, len(s), cap(s))

}

func appendFunc(s []int) {
	s = append(s, 1, 2, 3)
	fmt.Println("appendFunc slice:", s, len(s), cap(s))
}

这段代码新加了一个输出在appendFunc函数中,我们来看运行结果:

go 复制代码
append slice: [1] 1 5
appendFunc slice: [1 1 2 3] 4 5
appendFuncMain slice: [1] 1 5 

可以看到,在appendFunc函数中打印传递的切片s是正常变化了的,这是怎么回事呢?切片不是引用类型吗?

解答

切片在go语言中属于引用类型,但是在go中都是值传递(拷贝副本),那切片是传递的什么呢?

切片的底层其实是一个结构体类型,在src/runtime/slice.go下可以看到。

go 复制代码
type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

array是指向底层数组,len为切片长度,cap为切片容量。在函数传递中就是把这个结构体拷贝了一份副本进行传递。

所以上面问题就得到了解答,因为拷贝的是slice结构体的副本,而append实际上是增加了副本的len,而传递的原slice结构体是没有发生改变的。

我们可以加个指针继续验证一下:

go 复制代码
func main() {
	s := make([]int, 0, 5)
	s = append(s, 1)
	fmt.Println("append slice:", s, len(s), cap(s))
	appendFunc(&s)
	fmt.Println("appendFuncMain slice:", s, len(s), cap(s))

}

func appendFunc(s *[]int) {
	*s = append(*s, 1, 2, 3, 5, 6, 7, 5)
	fmt.Println("appendFunc slice:", s, len(*s), cap(*s))
}

输出结果:

go 复制代码
append slice: [1] 1 5
appendFunc slice: &[1 1 2 3 5 6 7 5] 8 10   
appendFuncMain slice: [1 1 2 3 5 6 7 5] 8 10

加上指针后,函数传递就是slice切片指针地址值,作append增加或修改操作都是对一个slice 对象修改的,所以在不同作用域下修改的对象都是一致的。

相关推荐
Hanniel5 分钟前
Python 元类(下):进阶与实战建议
开发语言·python
会编程的土豆8 分钟前
Go interface 底层的 itab 到底是什么
开发语言·后端·golang
千纸鹤の脉搏13 分钟前
多线程的初步了解---进程与线程
java·开发语言·学习·线程
秋田君35 分钟前
Qt 5.12.8 下载与安装教程(附网盘资源)
开发语言·qt
故事和你9139 分钟前
洛谷-【动态规划2】线性状态动态规划4
开发语言·数据结构·c++·算法·动态规划·图论
不吃土豆的马铃薯42 分钟前
Socket 网络编程实战教程
linux·服务器·开发语言·网络·c++·算法
小成202303202651 小时前
从C到C++
开发语言·c++
折哥的程序人生 · 物流技术专研1 小时前
《Java 100 天进阶之路》第39篇:Java泛型方法的定义和使用
java·开发语言·后端·面试·求职招聘
天天进步20151 小时前
Tunnelto 源码解析 #1:从 tunnelto --port 8000 看内网穿透的完整链路
开发语言
啄缘之间1 小时前
8.【学习】工业级详细接口约束&覆盖率
开发语言·笔记·学习·uvm·sv