我们都知道,Java、C#、Python 都 因为指针的复杂而避开了指针的用法,改成了引用。
Go 语言作为 21 世纪的 C 语言,自然保留了 C 语言的许多特性,指针就是其一。但相比于 C 的指针,Go 对指针做了很多限制。
这一篇,就来学习 Go 指针的各种相关知识。
地址与指针
变量是存储值的地方。利用声明的变量名来区分各种变量,例如 x。而指针的值是一个变量的地址。一个指针是指向值所保存的位置,不是所有的值都有地址,但是所有的变量都有。
将会得到如下结果(不同的电脑地址可能不一样):
2021
0xc0000120a8
那么这些 '地址 '究竟是什么呢?好吧,如果你想在拥挤的城市中找到一个特定的 的房子,你就用它的地址......
就像一个城市,你的计算机为你的程序留出的内存是一个拥挤的地方。它充满了变量值:布尔值、整数、字符串,以及更多。就像房子的地址一样,如果你有一个变量的地址,你就可以 用它来找到该变量所包含的值。
代表变量地址的值被称为指针,因为它们指向可以找到变量的位置。它们指向可以找到该变量的位置。
NilNil
nil 指针
利用指针更改函数参数的值
使用指针,可以在无须知道变量名字的情况下,间接读取或更新变量的值。当我们调用一个带参数的函数时,该参数被复制到函数中。
func zeroValue(x int) {
x = 0
}
func main() {
x := 5
zeroValue(x)
fmt.Println(x) // x is still 5
}
zeroValue
*intzeroValue
* 运算符 和 & 操作符
*
var x int // 声明一个变量
x := 2 // 赋值变量
p := &x // p 是整型指针,指向 x
fmt.Println(*p) // '2'
*p = 22
fmt.Println(x) // 结果 “22”
var x int&x (x 的地址)
&x*int
使用 new() 函数创建指针
newnew
new
newnew
Go 指针的一些限制
Go 指针在使用上有一些限制,通过这些限制,减少了很多危险的操作。
Go 指针不支持算术运算
p++p-2
*p++(*p)++
package main
import 'fmt'
func main() {
x := int64(2021)
p := &x
*p++ // *p为2021,然后 ++ ,变成2022,指针p指向2022,同时x更改为2022
fmt.Println(*p, x)
fmt.Println('p == &x is ', p == &x)
*&x++ // x is 2023
*&*&x++ // 2024
**&p++ // 2025
*&*p++ // 2026
fmt.Println(*p, x)
}
运行结果:
一个指针类型的值不能被随意转换为另一个指针类型
在Go中,只有如下某个条件被满足的情况下,一个类型为 T1 的指针值才能被显式转换为另一个指针类型 T2 :
类型 T1 和 T2 的底层类型必须一致(忽略结构体字段的标签)。特别地,如果类型 T1 和 T2 中只要有一个是非定义类型,并且它们的底层类型一致(考虑结构体类型的标签),则此转换可以是隐式的。
类型 T1 和 T2 都为非定义类型并且它们的基类型的底层类型一致(忽略结构体类型字段的标签)。
type MyInt int64
type Ta *int64
type Tb *MyInt
对于上述的指针类型,下面的结论成立:
Tb((*MyInt)((*int64)(ta)))
这些指针类型的任何值都无法被转换到类型 *uint64。
一个指针值不能和其它任一指针类型的值进行比较
Go指针值是支持(使用比较运算符==和!=)比较的。但是,两个指针只有在下列任一条件被满足的时候才可以比较:
nil
一个指针值不能被赋值给其他任意类型的指针值
一个指针值可以被赋值给另一个指针值的条件和这两个指针值可以比较的条件是一致的。
总结
最后,我们总结一下这篇文章:
Go 语言保留了 C 语言的指针,指针用来指向变量的位置
可以利用指针更改函数参数的值
使用 * (星号)来定义一个指针,后面跟指针指向变量的类型,使用 & (取地址操作)来访问一个变量的位置
可以使用 new 函数来创建一个指针
Go 指针不支持算术元素,不能随意更改类型,不能随意比较
最后感谢这几本本文参考的书,谢谢作者们:
Head First Go
Go语言101
An Introduction to Programming In Go