Golang 中的指针 - Pointer
本文内容纲要:
http://www.cnblogs.com/jasonxuli/p/6802289.html
Go的原生数据类型可以分为基本类型和高级类型,基本类型主要包含string,bool,int及float系列,高级类型包含struct,array/slice,map,chan,func。
相比Java,Python,Javascript等引用类型的语言,Golang拥有类似C语言的指针这个相对古老的特性。但不同于C语言,Golang的指针是单独的类型,而不是C语言中的int类型,而且也不能对指针做整数运算。从这一点看,Golang的指针基本就是一种引用。
那么Golang为什么需要指针?这种指针又能有什么独特的用途呢?
在学习引用类型语言的时候,总是要先搞清楚,当给一个函数/方法传参的时候,传进去的是值还是引用。实际上,在大部分引用型语言里,参数为基本类型时,传进去的大都是值,也就是另外复制了一份参数到当前的函数调用栈。参数为高级类型时,传进去的基本都是引用。这个主要是因为虚拟机的内存管理导致的。
内存管理中的内存区域一般包括heap和stack,stack主要用来存储当前调用栈用到的简单类型数据:string,boolean,int,float等。这些类型的内存占用小,容易回收,基本上它们的值和指针占用的空间差不多,因此可以直接复制,GC也比较容易做针对性的优化。复杂的高级类型占用的内存往往相对较大,存储在heap中,GC回收频率相对较低,代价也较大,因此传引用/指针可以避免进行成本较高的复制操作,并且节省内存,提高程序运行效率。
因此,在下列情况可以考虑使用指针:1,需要改变参数的值;2,避免复制操作;3,节省内存;
而在Golang中,具体到高级类型struct,slice,map,也各有不同。实际上,只有struct的使用有点复杂,slice,map,chan都可以直接使用,不用考虑是值还是指针。
struct:
对于函数(function),由函数的参数类型指定,传入的参数的类型不对会报错,例如:
funcpassValue(sstruct){}
funcpassPointer(s*struct){}
对于方法(method),接收者(receiver)可以是指针,也可以是值,Golang会在传递参数前自动适配以符合参数的类型。也就是:如果方法的参数是值,那么按照传值的方式,方法内部对struct的改动无法作用在外部的变量上,例如:
packagemain
import"fmt"
typeMyPointstruct{
Xint
Yint
}
funcprintFuncValue(pMyPoint){
p.X=1
p.Y=1
fmt.Printf("->%v",p)
}
funcprintFuncPointer(pp*MyPoint){
pp.X=1//实际上应该写做(*pp).X,Golang给了语法糖,减少了麻烦,但是也导致了*的不一致
pp.Y=1
fmt.Printf("->%v",pp)
}
func(pMyPoint)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)
}
funcmain(){
p:=MyPoint{0,0}
pp:=&MyPoint{0,0}
fmt.Printf("\nvaluetofunc(value):%v",p)
printFuncValue(p)
fmt.Printf("-->%v",p)
//Output:valuetofunc(value):{00}->{11}-->{00}
//printFuncValue(pp)//cannotusepp(type*MyPoint)astypeMyPointinargumenttoprintFuncValue
//printFuncPointer(p)//cannotusep(typeMyPoint)astype*MyPointinargumenttoprintFuncPointer
fmt.Printf("\npointertofunc(pointer):%v",pp)
printFuncPointer(pp)
fmt.Printf("-->%v",pp)
//Output:pointertofunc(pointer):&{00}->&{11}-->&{11}
fmt.Printf("\nvaluetomethod(value):%v",p)
p.printMethodValue()
fmt.Printf("-->%v",p)
//Output:valuetomethod(value):{00}->{11}-->{00}
fmt.Printf("\nvaluetomethod(pointer):%v",p)
p.printMethodPointer()
fmt.Printf("-->%v",p)
//Output:valuetomethod(pointer):{00}->&{11}-->{11}
fmt.Printf("\npointertomethod(value):%v",pp)
pp.printMethodValue()
fmt.Printf("-->%v",pp)
//Output:pointertomethod(value):&{11}->{22}-->&{11}
fmt.Printf("\npointertomethod(pointer):%v",pp)
pp.printMethodPointer()
fmt.Printf("-->%v",pp)
//Output:pointertomethod(pointer):&{11}->&{22}-->&{22}
}
slice:
slice实际上相当于对其依附的array的引用,它不存储数据,只是对array进行描述。因此,修改slice中的元素,改变会体现在array上,当然也会体现在该array的所有slice上。
可以使用make([]int)来创建并初始化map。
map:
使用make(map[string]string)返回的本身是个引用,可以直接用来操作:
map["name"]="Jason";
而如果使用map的指针,反而会产生错误:
*map["name"]="Jason"//invalidindirectofm["title"](typestring)
(*map)["name"]="Jason"//invalidindirectofm(typemap[string]string)
chan:
make(chanint)返回的是可以直接使用的channel。
func:
在Golang中,func可以作为一种值被返回,因此也可以使用类似Python的decorator的方式来加工函数。
本文内容总结:
原文链接:https://www.cnblogs.com/jasonxuli/p/6802289.html