golang 中slice 、map、chan作爲函數參數分析

寫這篇文章之前考慮一個問題:

  • go裏面都是值傳遞,不存在引用傳遞?
    https://cloud.tencent.com/developer/article/1416563

先來總結一下slice、map、chan的特性:
slice:

func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

其實makeslice64返回是的[]int

  1. slice本身是一個結構體,而不是一個指針,其底層實現是指向數組的指針。
  2. 三要素:type(指針)、len、cap

map:

func makemap(t *maptype, hint int, h *hmap) *hmap
  1. makemap返回的一個指針
  2. go map底層實現是hashmap,並採用鏈地址方法解決hash衝突的
  3. map的擴容機制:2倍擴容,漸進式擴容。

slice作爲參數

先看看例子:

package main

import (
	"fmt"
	"reflect"
)

func modify1(slice []int)  {
	for i:=0;i<len(slice);i++{
		slice[i] = 0
	}

	fmt.Println("Inside  modify1 after append: ", len(slice))
	fmt.Println("Inside  modify1 after append: ", cap(slice))
	fmt.Println("Inside  modify1 after append: ", slice)
}

func modify2(slice []int)  {
	length := len(slice)
	for i:=0;i<length;i++{
		slice = append(slice, 1)
	}

	fmt.Println("Inside  modify2 after append: ", len(slice))
	fmt.Println("Inside  modify2 after append: ", cap(slice))
	fmt.Println("Inside  modify2 after append: ", slice)
}

func main(){

	s1 := make([]int,10,10)
	for i:=0;i<10;i++{
		s1[i] = i
	}
	fmt.Println("makeslcie return type: ", reflect.TypeOf(s1))

	fmt.Println("before modify slice: ", s1)

	modify1(s1)
	fmt.Println("after modify1 slice: ", s1)

    for i:=0;i<10;i++{
		s1[i] = i
	}
	modify2(s1)
	fmt.Println("after modify2 slice: ", len(s1))
	fmt.Println("after modify2 slice: ", cap(s1))
	fmt.Println("after modify2 slice: ", s1)

}

運行看看輸出:

makeslcie return type:  []int
before modify slice:  [0 1 2 3 4 5 6 7 8 9]
Inside  modify1 after append:  10
Inside  modify1 after append:  10
Inside  modify1 after append:  [0 0 0 0 0 0 0 0 0 0]
after modify1 slice:  [0 0 0 0 0 0 0 0 0 0]
Inside  modify2 after append:  20
Inside  modify2 after append:  20
Inside  modify2 after append:  [0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1 1 1 1 1]
after modify2 slice:  10
after modify2 slice:  10
after modify2 slice:  [0 1 2 3 4 5 6 7 8 9]

modify1看,slice作爲參數,在函數內部能修改slice,表面看確實能在函數內部修改slice

modify2看,在函數modify2內部用appen操作擴容了slice,len和cap都變成了20,但是再看看後面的輸出,modify2並沒有修改slice,外部的slice依然沒變 len和cap都沒變化。

這是怎麼回事,函數內部修改slice並沒有影響外部slice。 其實go裏面都是值傳遞,makeslice返回的是[]int,傳入函數內部會對其拷貝一份,slice內部實現是指向數組的指針的,拷貝的副本部分底層實現也是指向同一內存地址的指針數組。所以內部修改slice的值是能修改的,但是append的並沒有修改傳入的slice的數組,而是返回一個新的slice的,這要去看看slice的實現和其append的擴容機制。實際上當函數內部不擴容slice,如果修改slice也是修改其指向的底層數組。如果發生擴容會發生數據拷貝,並不會修改其指向的array數組。

如果想在函數內部修改可以傳遞數組指針就可以了,類似下面這樣

func modify2(slice *[]int)

參考資料:https://www.cnblogs.com/junneyang/p/6074786.html

map作爲參數

func makemap(t *maptype, hint int, h *hmap) *hmap

在函數內部可以修改map

package main

import "fmt"

func modify1(m map[string]string){

	for key,_ := range m{
		m[key] = "chen"
	}

	m["chen"] = "xun"
	//fmt.Println("修改之後的map:", m)
}


func main(){

	m := map[string]string{ // :=創建
		"name": "小明",
		"age":  "18",
	}

	fmt.Println("修改之前的map:", len(m),  m)
	modify1(m)
	fmt.Println("修改之前的map:", len(m),  m)

}

chan作爲參數

func makechan(t *chantype, size int) *hchan 

也就是make() chan的返回值爲一個hchan類型的指針,因此當我們的業務代碼在函數內對channel操作的同時,也會影響到函數外的數值。

package main

import "fmt"

func test_chan2(ch chan string){
	fmt.Printf("inner: %v, %v\n",ch, len(ch))
	ch<-"b"
	fmt.Printf("inner: %v, %v\n",ch, len(ch))
}

func main() {
	ch := make(chan string, 10)
	ch<- "a"

	fmt.Printf("outer: %v, %v\n",ch, len(ch))
	test_chan2(ch)
	fmt.Printf("outer: %v, %v\n",ch, len(ch))
}

參考資料:https://zhuanlan.zhihu.com/p/54988753

發佈了331 篇原創文章 · 獲贊 132 · 訪問量 75萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章