指针指向一个变量的内存地址。使用指针可以使很多操作变得简单,但同样提高了编程的难度和程序的可读性。C语言的指针是使得很多初学者头疼的一个重要点。指针其实并不难理解,我们本科时老师给我们讲过一句话,终身受用。他说:”指针就是地址,你看到指针就自动在大脑里给它替换成地址,就没有什么难理解的了。“
比如,Go和C语言的指针,都有指针的指针这么一个概念,它其实就是地址的地址。一个变量它存了一个值,这个值是一个地址,逻辑上指向另一个地址,而这个地址存的值,还是一个地址。
如图,p1是一个指针,它存在红色方框所代表的内存地址,地址指向绿色方框所代表的内存地址;p2也是一个指针,它存在绿色方框所代表的内存地址,它的地址指向蓝色方框所代表的内存地址。那么p1就是一个指针的指针。
Go语言的指针概念上基本等同于C语言的指针,写法上也完全一致,同样使用
*
标识,同样使用&
作为取地址运算符。我想指针和结构体,是为什么Go被称为类C语言的原因。
指针指向一个变量的内存地址。
Go语言中声明指针类型语法如下:
var variable_name *variable_type
即,使用指针需要在定义时指明它所指向的变量的类型。
实例
var p1 *int
实例:
package main
import "fmt"
func main() {
var a int = 1
var p1 *int
p1 = &a
fmt.Printf("p1指向变量a的地址,该地址为:%x\n", p1)
}
// p1指向变量a的地址,该地址为:c00000a0b0
// 你运行的结果可能和我的不一样 因为变量地址是动态分配的
空指针
空指针即不指向任何地址的指针,Go语言中用nil
表示,等同于Java的null
,C++的NULL
,python的None
,指代零值或空值。
你可以把nil看作一个特殊的值,而不是一种类型。
package main
import "fmt"
func main() {
var p1 *int
fmt.Printf("p1指向的地址为:%x\n", p1)
if p1 == nil {
fmt.Println("p1是空指针")
} else {
fmt.Println("p1不是空指针")
}
}
// p1指向的地址为:0
// p1是空指针
指针数组
指针数组指的是数组的元素类型是指针,即指针也可以作为数组的类型。
实例
package main
import "fmt"
func main() {
arr := []int {1, 2, 3}
var ptr [3]*int
for i:=0; i<3; i++ {
ptr[i] = &arr[i] // 经过这样的赋值之后 *ptr[i]等价于arr[i] *取内容运算符 即根据地址值去取实际存的内容
}
for i:=0; i<3; i++ {
fmt.Printf("arr[%d]的地址是%x,存储的值是%d\n", i, ptr[i], *ptr[i])
}
}
指针的指针
如果一个指针变量存放的是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址:
var p **int
// 两个*号说明需要取两次内容 才能取到值
实例
package main
import "fmt"
func main() {
var a int = 1
var ptr *int
var pptr **int
ptr = &a
pptr = &ptr
fmt.Printf("变量a的地址:%x\n", &a)
fmt.Printf("指针变量ptr:%x\n", ptr)
fmt.Printf("指针的指针变量pptr:%x\n", pptr)
fmt.Printf("指针的指针变量pptr指向的内容:%x\n", *pptr)
}
// 变量a的地址:c00000a0b0
// 指针变量ptr:c00000a0b0
// 指针的指针变量pptr:c000006028
// 指针的指针变量pptr指向的内容:c00000a0b0
指针作为函数参数
指针也可以作为函数参数。
实例:
func swap(x *int, y *int) {
var temp int = *x
*x = *y
*y = temp
}
这是一个经典例子,不使用指针,是无法在函数内部实现对外部变量值的交换的,Java就无法实现这个操作。但在Java里面可以实现对两个数组元素的交换,这是因为,数组做参数,传递的是引用,引用就是地址,也就是指针。