首页 文章资讯内容详情

Golang 中的指针 - Pointer

2026-06-01 3 花语

本文内容纲要:

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