golang 的引用類型和內置類型變量
golang 中變量類型分爲引用類型和值類型(也叫做內置類型)golang
1.值類型:變量直接存儲值,內存一般在棧中分配。
值類型:基本數據類型int、float、bool、string以及數組和struct數組
2.引用類型:變量存儲的是一個地址,這個地址存儲最終的值。內存一般在 堆上分配。經過GC回收。
引用類型:指針、slice、map、chan等都是引用類型。這類型變量須要經過make構造app
golang中函數傳參只有一種方式
golang中函數傳遞參數,只有值傳遞一種,也就是實參內容按照值copy方式傳遞給形參。
當函數的形參變量類型爲指針,slice,map,chan等類型時,雖然實參和形參地址不一樣,可是內部指向了同一個地址,因此能夠達到修改指定空間數據的目的。
不要着急,接下來我會寫一寫小demo幫助你們理解。
函數
數組
先把這段代碼寫一遍看看結果spa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
//數組聲明方法 var bytearray [8]byte //長度爲8的數組 fmt.Println(bytearray) var pointarray [4]*float64 //指針數組 fmt.Println(pointarray) var mularray [3][5]int fmt.Println(mularray) fmt.Printf(" pointarray len is %v\n", len(pointarray)) //數組遍歷 for i := 0; i < len(pointarray); i++ { fmt.Println("Element", i, "of array is", pointarray[i]) }
//採用range遍歷 for i, v := range pointarray { fmt.Println("Array element [", i, "]=", v) }
|
上邊提供了數組的聲明方式 var 數組名 [數組長度] 元素類型,
同時給出了兩種數組遍歷方式:
1 len(數組名) 能夠獲取數組大小,而後遍歷
2 採用range遍歷,第一個返回值是索引,第二個返回值是對應的內容
int 類型數組初始值爲0,指針類型數組初始值爲nil
結果以下:指針
1 2 3 4 5 6 7 8 9 10 11 12
|
[0 0 0 0 0 0 0 0] [<nil> <nil> <nil> <nil>] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]] pointarray len is 4 Element 0 of array is <nil> Element 1 of array is <nil> Element 2 of array is <nil> Element 3 of array is <nil> Array element [ 0 ]= <nil> Array element [ 1 ]= <nil> Array element [ 2 ]= <nil> Array element [ 3 ]= <nil>
|
前文說過數組是值類型變量,咱們寫個函數,在函數內部修改形參數組的變量內容,看是否會對實參影響code
1 2 3 4 5 6 7 8 9
|
func modify(array [5]int) { array[0] = 200 fmt.Println("In modify(), array values:", array) } func main(){ array := [5]int{1, 2, 3, 4, 5} modify(array) fmt.Println("In main(), array values:", array) }
|
結果以下索引
1 2
|
In modify(), array values: [200 2 3 4 5] In main(), array values: [1 2 3 4 5]
|
說明實參沒有被函數修改。那麼既然golang傳遞變量的方式都是值傳遞,是否是就沒辦法經過函數修改外部變量了呢?
確定不是的,能夠經過引用類型變量修改,好比指針,slice,map,chan等均可以在函數體內修改,從而影響外部實參的內容。
下面經過slice說明這一點內存
slice切片
先看代碼ci
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
array := [5]int{1, 2, 3, 4, 5} //根據數組生成切片 //切片 var mySlice []int = array[:3] fmt.Println("Elements of array") for _, v := range array { fmt.Print(v, " ") } fmt.Println("\nElements of mySlice: ") for _, v := range mySlice { fmt.Print(v, " ") } //直接建立元素個數爲5的數組切片 mkslice := make([]int, 5) fmt.Println("\n", mkslice) //建立初始元素個數爲5的切片,元素都爲0,且預留10個元素存儲空間 mkslice2 := make([]int, 5, 10) fmt.Println("\n", mkslice2) mkslice3 := []int{1, 2, 3, 4, 5} fmt.Println("\n", mkslice3)
//元素遍歷 for i := 0; i < len(mkslice3); i++ { fmt.Println("mkslice3[", i, "] =", mkslice3[i]) }
//range 遍歷 for i, v := range mkslice3 { fmt.Println("mkslice3[", i, "] =", v) }
|
生成切片有三種方式
1 經過數組或者切片截取生成新的切片
2 經過make生成 如mkslice := make([]int, 5)
3 直接初始化 如mkslice3 := []int{1, 2, 3, 4, 5}
切片遍歷和數組遍歷相似,上面結果以下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
Elements of array 1 2 3 4 5 Elements of mySlice: 1 2 3
[0 0 0 0 0]
[0 0 0 0 0]
[1 2 3 4 5] mkslice3[ 0 ] = 1 mkslice3[ 1 ] = 2 mkslice3[ 2 ] = 3 mkslice3[ 3 ] = 4 mkslice3[ 4 ] = 5 mkslice3[ 0 ] = 1 mkslice3[ 1 ] = 2 mkslice3[ 2 ] = 3 mkslice3[ 3 ] = 4 mkslice3[ 4 ] = 5
|
獲取切片大小和容量
1 2 3 4
|
//獲取size和capacity mkslice4 := make([]int, 5, 10) fmt.Println("len(mkslice4):", len(mkslice4)) fmt.Println("cap(mkslice4):", cap(mkslice4))
|
獲取大小採用len,獲取實際開闢的容量用cap
切片添加和刪除
1 2 3 4 5 6
|
//末尾添加三個元素 mkslice4 = append(mkslice4, 1, 2, 3) fmt.Println("mkslice4 is : ", mkslice4)
mkslice4 = append(mkslice4, mkslice3...) fmt.Println("mkslice4 is : ", mkslice4)
|
採用append 方式能夠添加切片數據,可是要注意將append賦值給要存儲結果的slice
append有兩種用法,第一種是多個參數,第一個參數是slice,後邊是要加的多個元素。
第二種是第一個參數爲slice,第二個參數爲slice展開,slice…表示把slice中元素一個個展開加入。
切片的刪除較爲麻煩,好比說刪除第n個元素,就是截取n-1以前的序列和n以後的序列進行拼接。
1 2 3 4 5 6 7 8 9 10 11
|
mkslice4 := make([]int, 0)
//末尾添加三個元素 mkslice4 = append(mkslice4, 1, 2, 3) fmt.Println("mkslice4 is : ", mkslice4) mkslice3 := []int{1, 2, 3, 4, 5} mkslice4 = append(mkslice4, mkslice3...)
fmt.Println("mkslice4 is : ", mkslice4) mkslice4 = append(mkslice4[:4-1], mkslice4[4:]...) fmt.Println("mkslice4 is : ", mkslice4)
|
切片的copy
copy函數提供了切片的深層複製,而賦值操做(=)牢牢是淺拷貝。
看看賦值操做,咱們修改slice內部元素數據,其餘slice是否會受到影響
1 2 3 4 5 6 7 8 9 10
|
oldslice := []int{1, 2, 3, 4, 5} newslice := oldslice[:3] newslice2 := oldslice fmt.Println("newslice is :", newslice) fmt.Println("newslice2 is :", newslice2) fmt.Printf("newslice addr is : %p \n", &newslice) fmt.Printf("newslice2 addr is: %p \n", &newslice2) oldslice[0] = 1024 fmt.Println("newslice is :", newslice) fmt.Println("newslice2 is :", newslice2)
|
輸出一下
1 2 3 4 5 6
|
newslice is : [1 2 3] newslice2 is : [1 2 3 4 5] newslice addr is : 0xc00005a400 newslice2 addr is: 0xc00005a420 newslice is : [1024 2 3] newslice2 is : [1024 2 3 4 5]
|
能夠看到oldslice修改後,newslice和newslice2都受到影響了,即使他們地址不一樣。
爲何呢?這要追溯到slice內部實現
1 2 3 4 5
|
type Slice struct { ptr unsafe.Pointer // Array pointer len int // slice length cap int // slice capacity }
|
Slice 內部其實存放了一個指針ptr,這個ptr指向的地址就是存放數據連續空間的首地址,len表示空間當前長度,cap表示空間實際開闢了多大。
以下圖
那如何深copy元素到另外一個slice呢?就是copy函數了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
slice1 := []int{1, 2, 3, 4, 5} slice2 := []int{5, 4, 3} copy(slice2, slice1) fmt.Println("after copy.....") fmt.Println("slice1: ", slice1) fmt.Println("slice2: ", slice2) slice2[0] = 1024 slice2[1] = 999 slice2[2] = 1099 fmt.Println("after change element slice2...") fmt.Println("slice1: ", slice1) fmt.Println("slice2: ", slice2) copy(slice1, slice2) fmt.Println("after copy.....") fmt.Println("slice1: ", slice1) fmt.Println("slice2: ", slice2)
|
結果以下
1 2 3 4 5 6 7 8 9
|
after copy..... slice1: [1 2 3 4 5] slice2: [1 2 3] after change element slice2.. slice1: [1 2 3 4 5] slice2: [1024 999 1099] after copy..... slice1: [1024 999 1099 4 5] slice2: [1024 999 1099]
|
能夠看到copy(destslice,srcslice),當destslice 大小< srcslice時,只拷貝destslice大小的數據。
也就是說copy的大小取決於destslice和srcslice最小值
另外copy後,修改slice2元素,slice1也不會受到影響,是深copy。
感謝關注個人公衆號