指针
Last updated
Was this helpful?
Last updated
Was this helpful?
本节我们介绍下Golang中的指针,并了解下Golang中的指针跟其他编程语言比如C、C++之间的区别。
指针是一个存储了其他变量的内存地址的变量。这句话有点绕,怎么理解呢?其实很简单。指针其实也是一个变量,只要是变量就会存储某个值,指针存储的值比较特殊,是另外一个变量的内存地址。
看一下前面这张示例图,变量b
存储的值为156
,其在内存中的地址为0x1040a124
。变量a
存储的值是变量b
的地址。因此这里变量a
指向变量b
,变量a
是变量b
的指针。
一个指向T
类型数据的指针变量声明格式为*T
。 我们写个函数来声明一个指针变量。
Go Playground在线运行 这里的&
运算符用来获取一个变量的地址。我们将变量b
的地址赋值给指针变量a
,其数据类型为*int
。这时指针a
指向变量b
。当我们打印指针变量a
的值时,其实打印的是变量b
的地址。该程序输出如下:
变量b
可能存在于内存中的任何地址,因此你的打印结果可能跟我的不同。
指针变量的零值为nil
。
Go Playground在线运行 这里指针变量b
的初始值为nil
,然后我们给其赋值变量a
的地址。执行结果如下:
new
函数来创建指针Golang提供了一个便捷的new
函数用来创建指针。new
函数的入参为类型T
,返回值为一个指向该类型零值变量的指针。有点绕,我们举个例子看下。
Go Playground在线运行 这里我们通过new
函数创建了一个指向一个临时的int
类型数据的指针,该临时数据分配的值为对应类型的零值,比如这里即为int
类型的零值0
。该程序执行结果如下:
所谓的指针解引用指的是访问指针指向的变量的值。指针a
解引用的语法为*a
。
下面通过一个例子来了解下:
Go Playground在线运行 这里我们通过指针a
解引用来访问其指向的变量b
的值。执行结果如下:
下面修改下前面的程序,我们通过指针来修改变量b
的值:
Go Playground在线运行 这里我们通过*a++
将指针a
指向的变量加1,由于a
指向的变量为b
,因此b
变量的值变为256
。该程序输出如下:
Go Playground在线运行 这里我们将指向变量a
的指针变量b
作为入参传递给函数change
,change
函数内部通过指针解引用改变了变量a
的值。输出结果为:
Golang中允许返回指向局部变量的指针,如果一个指向局部变量的指针作为函数返回值,那么Golang编译器会在堆内存上分配该局部变量。
Go Playground在线运行 这里函数hello
将局部变量i
的指针作为函数返回值。在其他诸如C和C++之类的编程语言中这种写法是错误的,因为变量i
是局部变量,一旦函数返回,局部变量就访问不到了。但是Golang会在局部变量初始化时检查下是否会在函数外部被引用,比如这里的局部变量i
,其指针作为函数返回值返回出去,因此会在函数外部引用,这是Golang就将该局部变量分配在堆内存中。该程序执行结果为:
如果我们把一个指向数组的指针作为函数入参,那么可以在函数内部通过该指针来修改数组内部值,并且该修改在函数外也是可见的。
Go Playground在线运行 这里将数组a
的指针作为函数modify
的入参,并通过该指针来将其指向的数组的第一个元素的值改为90
。输出为:[90 90 91]
。
(*a)[x]
可以简写为a[x]
,因此前面程序中的(*arrP)[0]
可以简写为arrP[0]
。
Go Playground在线运行 执行结果不变,输出:[90 90 91]
。
尽管这种通过数组指针来在函数内部修改数组是行得通的,但是Golang中一般不这么用,Golang中比较惯用的用法一般是使用切片(slice)。
Go Playground在线运行 这里我们将一个切片a[:]
作为函数modify
的入参,然后在函数内部将该切片的第一个元素修改为90
。该程序输出:[90 90 91]
。
因此,我们在Golang中,尽量避免数组指针,而应该多用切片。
Golang跟C和C++不同,不支持指针运算,这其实在一定程度上降低了Golang这门编程语言的复杂度。
该程序执行会报错: