go閉坑指南
內容
1 切片與數組
2 defer
3 make與new
4 方法與函數
1 切片和數組
- 數組和結構體都是值變量,即:如果把一個數組變量和結構體變量賦值給另外的變量,是拷貝了一份值,兩者的修改互不影響;
2 . go通過切片生成另外一個切片時,兩個切片共享同一個底層數組,對其中一個修改元素時,兩個都會改變;
例如: a=[]int{1,2,3} b:=a[:] b[0] = 2 printLn(a[0]) —》輸出:2
- 特別注意初始化切片時,如果指定了切片的長度,go會用nil來填充這個切片, 如果是基本類型則用基本類型零值填充,此後對切片通過append操作時,會在後面進行填充;所以在初始化一個切片並且指定了容量時,要注意長度初始化爲0;
a := []int{1,2,3} b := make([]int, 0, len(a)) //Yes B := make([]int, len(a), len(a)) //No B1 := make([]int, 0) //Yes b1 := make([]int, 4) //No
2 defer
1.對於defer,當代碼運行到defer語句時,defer後要運行的函數的入參此時已經確定了(即:defer函數的入參函數此時就被執行了,而不是到了調用是才被執行),defer下面的語句對該參數做的修改對於函數無效;;
2.如果同一個函數中有多個defer,被推遲的函數按照先進後出的順序執行(壓棧出棧),即:最後一個defer會被第一個執行;
func main(){ b() } func un(s string) { fmt.Println("leaving:", s) } func trace(s string) string { fmt.Println("enter:", s) return s } func b() { defer un(trace("b")) fmt.Println("in:", "b") a() } func a() { defer un(trace("a")) fmt.Println("in:", "a") } 打印: enter: b in: b enter: a in: a leaving: a leaving: b
3.defer 原理
- 編譯期;
- 將
defer
關鍵字被轉換runtime.deferproc
; - 在調用
defer
關鍵字的函數返回之前插入runtime.deferreturn
;
- 將
- 運行時:
-
runtime.deferproc
會將一個新的runtime._defer
結構體追加到當前 Goroutine 的鏈表頭; -
runtime.deferreturn
會從 Goroutine 的鏈表中取出runtime._defer
結構並依次執行;
-
- 後調用的
defer
函數會先執行:- 後調用的
defer
函數會被追加到 Goroutine_defer
鏈表的最前面; - 運行
runtime._defer
時是從前到後依次執行;
- 後調用的
- 函數的參數會被預先計算;
- 調用
runtime.deferproc
函數創建新的延遲調用時就會立刻拷貝函數的參數,函數的參數不會等到真正執行時計算;
- 調用
3 make與new
make和new的區別:make只能用於slice map和channel,返回的是該類型初始化後的引用;new(T)返回的是指向該類型的指針。
type File struct{ name string } &File{} 《=》 new(File) ``` make只用於映射、切片和管道,並且不返回指針,如果要得到指針請使用new ``` var p *[]int = new([]int) // 得到指針 var v []int = make([]int, 0, 100) var p *[]int = new([]int) *p = make([]int, 100)
4 方法和函數
1、函數中如果入參是值參數,那麼該函數只能接收值入參;如果方法中的接收器是值接收器,那麼該方法可以接收值接收器和指針接收器,即:可以通過該類型接收器的值變量和指針變量調用方法;
2、函數中如果入參是指針參數,那麼只能接收指針參數;如果方法中申明的接收器是指針接收器,那麼該方法可以接收值接收器和指針接收器;
引用:
- 《go語言實現與設計》 https://draveness.me/golang/
- 《effective go》 https://www.kancloud.cn/kancloud/effective/72214
歡迎關注我們的微信公衆號,每天學習Go知識