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]
}

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