go—slice传递给函数过程解析

slice传递给函数理解

  1. slice的底层实现是
type IntSlice struct {
	ptr *int
	len, cap int
}

这里的ptr是真实的指向slice的第一个元素地址的指针。
len表示的是slice当前包含的元素的数目。
cap表示slice的容量。

slice实际上是基于底层数组实现的,从它的结构体定义中可以得知。

  1. 下面讲解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。

  1. List item

为什么main函数中的args没有变化?

因为args传入函数的时候,是按照值传递的,main函数中的args和use_array函数中的args并不是同一个args,use_array中的args是对main函数的args的一个拷贝,但它底层数组同样也是arg,以及ptr指针也指向的是arg的第一个元素。按值传递,main函数的args并不会改变。

  1. 对原来的程序改变一个地方,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]
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章