切片和數組
var a [2]int 定義的是一個2長度的數組
var b []int 定義的是一個切片
b=a 這個賦值在語法上是不成立的
var matrixA [][2]int
a=matrixA[0] 因為是數組的拷貝,這里進行值拷貝
var matrixB [][]int
b = matrixB[0] 因為是切片的拷貝,這里只是一個指針引用,修改數組中的值會影響b
不定長參數/variadic function
參數定義形式 func sum(nums ...int)
這里的nums可以傳入不定量的int:
sum(1,2,3)
也可以直接傳slice,但要加...符號:
var comb = []int{1,2,3}
sum(comb...)
這兩種傳參方式只能二選一,不能同時有int和[]int作為參數。
異常機制 defer
defer func example(arg int) {***} (parent_var)
這里example函數無論寫在父函數的什么位置(當然前提是這句defer確實被執行到了,寫在return后面根本沒執行到還是不行的),都只會在發生異常或者父函數正常執行完成后再被執行,相當于c++的finally。
defer函數邏輯上是被壓入棧中暫存的,因此有多個defer的場合,最后聲明的最先執行。
defer函數的參數(parent_var)會在defer聲明的位置立刻求值,如果參數本身就是函數,那么立刻調用該函數求取聲明時間點的返回值。
閉包
閉包是因為golang的變量逃逸機制產生的,閉包就是一個函數與其相關的引用環境組成的一個整體。閉包本質其實是一個函數,但是這個函數會用到函數外的變量,它們共同組成的整體我們叫做閉包。例如:
func AddUpper () func (int) int {
var n int = 10
return func (x int) {
n++
}
}
這個返回的匿名函數引用了父函數的局部變量n,它就是一個閉包,ret=AddUpper()獲取到的ret不僅是一個函數,還隱藏著一份獨立的變量空間(其中只有一個n)。此后反復調用ret(1)的話可以讓這個獨立空間中的n持續增大。另外很顯然若再獲取一個ret_2=AddUpper(),那么ret_2也將保留一份獨立的變量空間,與ret保留的那份互不干涉。而如果n變成全局變量,ret_2才會和ret干涉同一個變量。
類型斷言
判斷一個interface的類型,注意和類型轉換的語法正好相反,例如
var x interface{}
value, ok := x.(int)
還可以和switch結合
switch a.(type) {
case int:
...
case string:
...
}
類型轉換
和類型斷言格式相反,例如
var array = make([]byte,8)
str := string(array)
unsafe pointer
任何類型的指針都可以和unsafe.Pointer互轉,但是這個類型依然不能進行偏移加減,需要進一步轉換成uintptr類型才能做加減運算
uintptr可以再轉回unsafe.Pointer,以此為跳板再轉成需要的指針類型。
GC機制在搬移變量存儲位置時會維護指向它的指針,但uintptr只是一個地址數值,不是指針。所以如果有uintptr類型的變量,在GC搬移過變量后可能變成一個無效值。程序中不要用中間變量存儲uintptr的值。
pointer receiver
例:
func (m *MyType) String() string { return m.value }
其中(m *MyType)就是pointer receiver,所謂receiver本質上就是一個輸入參數,所以和參數一樣,指針型可以修改m的值,而(m MyType)的形式就是在m的拷貝副本上調用函數,修改不會影響到m本身。
因上述區別,值類型的MyType變量其方法集不包含這個receiver是指針的string方法,var mt MyType; mt.string()是非法的。
相反,如果方法receiver是值類型,那么即使變量本身是值變量,也可以調用該方法。
channel
for range作用在channel上可以持續監聽該通道,能讀到信息則進行一次循環,直到通道被關閉循環結束。