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 的方式来加工函数。