slice传递给函数理解
- slice的底层实现是
type IntSlice struct {
ptr *int
len, cap int
}
这里的ptr是真实的指向slice的第一个元素地址的指针。
len表示的是slice当前包含的元素的数目。
cap表示slice的容量。
slice实际上是基于底层数组实现的,从它的结构体定义中可以得知。
- 下面讲解slice传入一个函数时的情况,
看下面的一段代码
func use_array(args []int) {
fmt.Println(cap(args))
args[0]=5 //修改slice之后,arg会变成[1,5,3,4,5,6]
args = append(args, 11) //slice增加一个元素,
//由于cap初始值等于原数组元素的个数-slice第一个元素的索引,
//所以cap等于5.len(args)从4变为5,依然小于等于cap(args)
//不发生扩增,所以append的改变
//仍然应用在原数组上。
fmt.Println(args) //[5,3,4,5,11]
}
func main() {
var arg = [...]int{1, 2, 3, 4, 5, 6}
var args = arg[1:5] // [2,3,4,5]
use_array(args)
fmt.Println(args) // [5,3,4,5]
fmt.Println(arg) // [1,5,3,4,5,11]
}
把slice传入函数后,由于slice保存了它的第一个元素在底层数组中的地址,这里就是数组arg中的2的地址。所以args[0]修改的是原数组arg的arg[1]这个元素,变成了5,。然后根据我上面对与append的注释,改变应用在原来的数组上,所以现在的arg变成了[1,5,3,4,5,11]. append 将args[4] 置为11,应用到底层数组上后,args[4]的地址也就是arg[5]的地址,所以arg[5]变成了11。
- List item
为什么main函数中的args没有变化?
因为args传入函数的时候,是按照值传递的,main函数中的args和use_array函数中的args并不是同一个args,use_array中的args是对main函数的args的一个拷贝,但它底层数组同样也是arg,以及ptr指针也指向的是arg的第一个元素。按值传递,main函数的args并不会改变。
- 对原来的程序改变一个地方,append函数发生变化,如下
func use_array(args []int) {
fmt.Println(cap(args))
args[0]=5 //修改slice之后,arg会变成[1,5,3,4,5,6]
args = append(args, 11, 10) //slice增加2个元素,
//由于cap初始值等于原数组元素的个数-slice第一个元素的索引,
//所以cap等于5.len(args)从4变为6,大于cap(args)
//底层数组转移到一个新的数组上,原数组不应用改变后的元素
//新的底层数组cap等于10。原底层数组main函数的arg不发生改变。
fmt.Println(args) //[5 3 4 5 11 10]
}
func main() {
var arg = [...]int{1, 2, 3, 4, 5, 6}
var args = arg[1:5] // [1,2,3,4]
use_array(args)
fmt.Println(args) // [1,2,3,4]
fmt.Println(arg) // [1,5,3,4,5,6]
}
发生的变化,请看我对于append方法的注释。另外的是main函数中的slice args依然没有变化。因为是值传递的原因。转成指针传递的话,main中的args也就会变化了!
下面提供一个指针传递的版本:
func use_array(args *[]int) {
fmt.Println(cap(*args)) //5
(*args)[0] = 5
*args = append(*args, 11, 10)
fmt.Println(*args) //[5,3,4,5,11,10]
fmt.Println(cap(*args)) //10
}
func main() {
var arg = [...]int{1, 2, 3, 4, 5, 6}
var args = arg[1:5]
fmt.Println(args) //[2,3,4,5]
use_array(&args)
fmt.Println(args) //[5,3,4,5,11,10]
fmt.Println(arg) //[1,5,3,4,5,6]
}