Go 的原生數據類型能夠分爲基本類型和高級類型,基本類型主要包含 string, bool, int 及 float 系列,高級類型包含 struct,array/slice,map,chan, func 。函數
相比 Java,Python,Javascript 等引用類型的語言,Golang 擁有相似C語言的指針這個相對古老的特性。但不一樣於 C 語言,Golang 的指針是單獨的類型,而不是 C 語言中的 int 類型,並且也不能對指針作整數運算。從這一點看,Golang 的指針基本就是一種引用。學習
那麼 Golang 爲何須要指針?這種指針又能有什麼獨特的用途呢?優化
在學習引用類型語言的時候,老是要先搞清楚,當給一個函數/方法傳參的時候,傳進去的是值仍是引用。實際上,在大部分引用型語言裏,參數爲基本類型時,傳進去的大都是值,也就是另外複製了一份參數到當前的函數調用棧。參數爲高級類型時,傳進去的基本都是引用。這個主要是由於虛擬機的內存管理致使的。spa
內存管理中的內存區域通常包括 heap 和 stack, stack 主要用來存儲當前調用棧用到的簡單類型數據:string,boolean,int,float 等。這些類型的內存佔用小,容易回收,基本上它們的值和指針佔用的空間差很少,所以能夠直接複製,GC也比較容易作針對性的優化。 複雜的高級類型佔用的內存每每相對較大,存儲在 heap 中,GC 回收頻率相對較低,代價也較大,所以傳引用/指針能夠避免進行成本較高的複製操做,而且節省內存,提升程序運行效率。指針
所以,在下列狀況能夠考慮使用指針:1,須要改變參數的值;2,避免複製操做;3,節省內存;對象
而在 Golang 中,具體到高級類型 struct,slice,map,也各有不一樣。實際上,只有 struct 的使用有點複雜,slice,map,chan 均可以直接使用,不用考慮是值仍是指針。ip
struct:內存
對於函數(function),由函數的參數類型指定,傳入的參數的類型不對會報錯,例如:虛擬機
func passValue(s struct){} func passPointer(s *struct){}
對於方法(method),接收者(receiver)能夠是指針,也能夠是值,Golang 會在傳遞參數前自動適配以符合參數的類型。也就是:若是方法的參數是值,那麼按照傳值的方式 ,方法內部對struct的改動沒法做用在外部的變量上,例如:string
package main import "fmt" type MyPoint struct { X int Y int } func printFuncValue(p MyPoint){ p.X = 1 p.Y = 1 fmt.Printf(" -> %v", p) } func printFuncPointer(pp *MyPoint){ pp.X = 1 // 實際上應該寫作 (*pp).X,Golang 給了語法糖,減小了麻煩,可是也致使了 * 的不一致 pp.Y = 1 fmt.Printf(" -> %v", pp) } func (p MyPoint) printMethodValue(){ p.X += 1 p.Y += 1 fmt.Printf(" -> %v", p) } // 建議使用指針做爲方法(method:printMethodPointer)的接收者(receiver:*MyPoint),一是能夠修改接收者的值,二是能夠避免大對象的複製 func (pp *MyPoint) printMethodPointer(){ pp.X += 1 pp.Y += 1 fmt.Printf(" -> %v", pp) } func main(){ p := MyPoint{0, 0} pp := &MyPoint{0, 0} fmt.Printf("\n value to func(value): %v", p) printFuncValue(p) fmt.Printf(" --> %v", p) // Output: value to func(value): {0 0} -> {1 1} --> {0 0} //printFuncValue(pp) // cannot use pp (type *MyPoint) as type MyPoint in argument to printFuncValue //printFuncPointer(p) // cannot use p (type MyPoint) as type *MyPoint in argument to printFuncPointer fmt.Printf("\n pointer to func(pointer): %v", pp) printFuncPointer(pp) fmt.Printf(" --> %v", pp) // Output: pointer to func(pointer): &{0 0} -> &{1 1} --> &{1 1} fmt.Printf("\n value to method(value): %v", p) p.printMethodValue() fmt.Printf(" --> %v", p) // Output: value to method(value): {0 0} -> {1 1} --> {0 0} fmt.Printf("\n value to method(pointer): %v", p) p.printMethodPointer() fmt.Printf(" --> %v", p) // Output: value to method(pointer): {0 0} -> &{1 1} --> {1 1} fmt.Printf("\n pointer to method(value): %v", pp) pp.printMethodValue() fmt.Printf(" --> %v", pp) // Output: pointer to method(value): &{1 1} -> {2 2} --> &{1 1} fmt.Printf("\n pointer to method(pointer): %v", pp) pp.printMethodPointer() fmt.Printf(" --> %v", pp) // Output: pointer to method(pointer): &{1 1} -> &{2 2} --> &{2 2} }
slice :
slice 實際上至關於對其依附的 array 的引用,它不存儲數據,只是對 array 進行描述。所以,修改 slice 中的元素,改變會體如今 array 上,固然也會體如今該 array 的全部 slice 上。
可使用 make([]int) 來建立並初始化 map 。
map :
使用 make(map[string]string) 返回的自己是個引用,能夠直接用來操做:
map["name"]="Jason";
而若是使用 map 的指針,反而會產生錯誤:
*map["name"]="Jason" // invalid indirect of m["title"] (type string) (*map)["name"]="Jason" // invalid indirect of m (type map[string]string)
chan :
make(chan int) 返回的是能夠直接使用的 channel 。
func :
在 Golang 中,func 能夠做爲一種值被返回,所以也可使用相似 Python 的 decorator 的方式來加工函數。