[奔跑的 Go] 教程十二、深入学习 Go 语言的指针
指针是一个变量,它存储另一个变量的内存地址。指针有能力改变它们所指向的数据。
在我们开始讨论指针之前,让我们先学习一两个关于十六进制数的知识。十六进制数是以16
为基数的数。如果你是一个web开发人员,那么你经常会接触到它们,因为大多数情况下;颜色通常以十六进制的格式来表示。例如,白色表示为#FFFFFF
,黑色表示为#000000
。
在go中,你可以在变量中保存十六进制数字,go将提供一个字面表达式来表示十六进制数。如果数字以0x(数字0,字母x)
开头,那么它就是一个十六进制数。
https://play.golang.org/p/NnAJ5go4dfz
从上面的例子中,我们发现即使以十六进制形式来表示变量的值,但数据依然是用十进制数来保存,数据类型为int(整形)
。
那当我们讨论指针时,为什么我们还要学习十六进制数呢。这就要从内存地址说起。
当你声明一个变量时,go的编译器会根据这个变量的类型在RAM(主存)
中为它分配一些内存,它会分配一个特定大小的内存来存储数据。在底层这个内存将会有内存地址,方便go通过内存地址来找到变量的值。内存地址以十六进制值来表示。
☛ 怎样获取一个变量的内存地址?
为了获取一个变量的内存地址, go 提供了一个简单的操作符 &
(与) ,如果在一个变量名称前面使用这个符号,就会返回该变量的内存地址。
https://play.golang.org/p/15P6SHOBavA
在 切片
课程中,当我们证明同一个数组的两个切片如何传递值的时候见到过这个符号。不管怎样,在上面的例子中, 使用 &
操作符, 我们可以获取变量 a
, b
和 c
的内存地址。
你可能看到不同的内存地址, 我就没必要解释了,相信聪明的你可以找到原因 。
☛ 什么是指针
指针是指向另一个变量内存地址的变量
前面我们知道, 我们可以将十六进制数保存在一个变量中, 但是在变量中保存的是一个类型为 int 的十进制值。 但是有一个陷阱,即使你可以保存一个内存地址在变量中,他不是一个指针或者指向另一个变量内存地址的位置。
它仅仅是一个值 并且 不能记住
之前是指向那个变量。
这就是指针的作用。它不仅保存了另一个变量的内存地址,
而且还知道该内存位于何处以及如何查找存储在该内存中的值。
与将十六进制值保存在int类型的变量中不同,
指针的数据类型是*int
(指向整数数据的内存地址),*string
(指向字符串数据的内存地址)。
创建指针或定义指针的语法是 var p *Type
,其中 Type
是数据类型。让我们创建一个简单的指针。
https://play.golang.org/p/77BocN1gVXu
这就证明了指针指向的数据类型为int,指针的零值为nil,
也就是说,指针此时没有指向任何变量或内存。
我们在切片课上讨论了指针的零值。
让我们来创建一个类型为int(整形)
的变量,并创建一个名为pa
的指针来指向它。
https://play.golang.org/p/LHlGgDwSpH6
上面的程序也可以使用简写语法来编写
https://play.golang.org/p/la95cObwviy
go会解释我们正在创建的指针pa
,因为我们将a
变量的内存地址分配给了一个指针变量pa
(使用&
运算符来取得变更的内存地址)。
这表明指针pa
不是空的,它指向内存的某块位置。虽然它没有明确表示它指向哪个变量。但是它可以通过内存地址找到所保存的值。
☛ 解引用指针
为了找出指针指向的值,我们需要使用*
操作符,也称为解引用操作符,如果*
操作符放在指针变量之前(像*&*
操作符获取内存地址一样),它将返回指针所对应的值。
https://play.golang.org/p/TnS5HySFnA9
☛ 改变指针的值
正如我们上面所看到的,我们可以访问由“指针”指向的内存位置的数据,
我们还可以更改该内存位置存储的值,
而不是为变量分配新值。
https://play.golang.org/p/yv9QmgmVQCK
为什么变量 a
的值会改变,因为我们在变量 a
的内存位置改变了值
☛ new
函数
go 提供了 new
函数,该函数可以分配一个内存地址并返回指针 。new
函数的语法是
func new(Type) *Type
第一个参数是数据类型而不是一个值,返回的值是指向该数据类型新分配的零值的指针。我们可以使用该返回值保存在一个指针中。
https://play.golang.org/p/WCVmwQCS6cG
您是否会觉得 go 创建在内存上的数据是 nil
。由于指针的 零值
是 nil
,这意味着该指针没有指向任何内存,但当指针指向内存地址时,内存不能为空(或是 nil
),它必须包含一些数据。go 存储传入 new
函数的数据类型的 零值
,并返回它的内存地址。所以,如果你只是对 pointer
感兴趣,你可以直接使用 new
函数而不是创建新的变量并创建指针指向它。
因此,
指针是一个保存另一个变量的内存地址的变量
的定义严格来说并不正确。指针是保存一个保存内存地址的变量
更加准确。
☛ 指针作为函数参数
您可以像变量一样使用指针作为函数的参数。有两种方式可以实现。创建一个指针,然后传递给函数,或者直接传递变量的内存地址给函数。
https://play.golang.org/p/CZof2zNT9Jo
我们可以像这样编写上面的程序
https://play.golang.org/p/kr-LTGyjZs0
在上面的程序中,函数 changeValue
的语法,尤其是参数部分的 *int
指示 go,我们期望一个指针 p
作为参数。
您可以将复合数据类型如 array
的指针作为参数传递给函数。
https://play.golang.org/p/wp2QxYOSSBC
我们也可以使用 go 提供的简写语法编写上面的程序来使用指针访问数组。
https://play.golang.org/p/GLa6BJ7bvc_G
在 go 语言中,将数组指针作为函数的参数并不是惯用的做法。 正如我们在 slice
中看到的,在函数内部修改一个切片时,原始切片也发生了改变。如果上面的程序结果正是您想要的,那就这样去做。
☛ 指针运算
与指针可以递增或递减的 C
语言不同, go
不允许指针运算。如果您尝试运算,go 将抛出编译错误。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: