0
点赞
收藏
分享

微信扫一扫

Go 语言系列13:指针


指针 是一种存储变量内存地址的变量,简单点讲就是地址变量,地址变量存放的是地址,通过该地址我们能获取该地址存放的数据。例如,下图所示。

Go 语言系列13:指针_内存地址

变量 ​​y​​​ 的值为 ​​100​​​ ,而变量 ​​y​​​ 的内存地址为 ​​0x10203040​​​ 。变量 ​​x​​​ 存储的是变量 ​​y​​​ 的地址,即 ​​0x10203040​​​ ,所以我们称 ​​x​​​ 指向了 ​​y​​​ , ​​x​​ 变量为指针变量,简称指针。


Go 语言系列13:指针_内存地址_02

创建指针

Go 语言系列13:指针_指针变量_03


指针变量的类型为 ​​*Type​​​ ,该指针指向一个 ​​Type​​ 类型的变量。创建指针有三种方法。

第一种方法

首先定义普通变量,再通过获取该普通变量的地址创建指针:

// 定义普通变量 x
x := 100
// 取普通变量 x 的地址创建指针 p
p := &x

第二种方法

先创建指针并分配好内存,再给指针指向的内存地址写入对应的值:

// 创建指针
p := new(int)
// 给指针指向的内存地址写入对应的值
*p = 100

第三种方法

首先声明一个指针变量,再从其他变量获取内存地址给指针变量:

// 定义变量 x
x := 100
// 声明指针变量
var p *int
// 指针初始化
p = &x

上面举的创建指针的三种方法对学过 C 语言的人来说可能很简单,但没学过指针相关知识的人可能不太明白,特别是上面代码中出现的指针操作符 ​​&​​​ 和 ​​*​​ 。

  • ​&​​ 操作符可以从一个变量中取到其内存地址。
  • ​*​​​ 操作符如果在赋值操作值的左边,指该指针指向的变量;​​*​​ 操作符如果在赋值操作符的右边,指从一个指针变量中取得变量值,又称指针的解引用。

通过下面的例子,你应该就会比较清楚的理解上面两个指针操作符了。

package main

import "fmt"

func main() {
x := 100
p := &x
fmt.Println("x = ", x) // x = 100
fmt.Println("*p = ", *p) // *p = 100
fmt.Println("&x = ", &x) // &x = 0xc0000ae058
fmt.Println("p = ", p) // p = 0xc0000ae058
}

Go 语言系列13:指针_内存地址_02

指针的类型

Go 语言系列13:指针_指针变量_03


​*(指向变量值的数据类型)​​ 就是对应的指针类型。

package main

import "fmt"

func main() {
mystr := "hello"
myint := 1
mybool := false
myfloat := 1.2

fmt.Printf("type of &mystr is %T\n", &mystr)
fmt.Printf("type of &myint is %T\n", &myint)
fmt.Printf("type of &mybool is %T\n", &mybool)
fmt.Printf("type of &myfloat is %T\n", &myfloat)
}

运行程序输出如下:

type of &mystr is *string
type of &myint is *int
type of &mybool is *bool
type of &myfloat is *float64

Go 语言系列13:指针_内存地址_02

指针的零值

Go 语言系列13:指针_指针变量_03


如果指针声明后没有进行初始化,其默认零值是 ​​nil​​ 。

package main

import "fmt"

func main() {
x := 100
var p *int
fmt.Println("p is ", p)
p = &x
fmt.Println("p is ", p)
}

运行程序输出如下:

p is  <nil>
p is 0xc00000a098

Go 语言系列13:指针_内存地址_02

函数传递指针参数

Go 语言系列13:指针_指针变量_03


在函数中对指针参数所做的修改,在函数返回后会保存相应的修改。

package main

import (
"fmt"
)

func change(value *int) {
*value = 200
}

func main() {
x := 100
p := &x
fmt.Println("before execute func change *p is ", *p)
change(p)
fmt.Println("after execute func change *p is ", *p)
}

运行程序输出如下,函数传入的是指针参数,即内存地址,所以在函数内的修改是在内存地址上的修改,在函数执行后还会保留结果。

before execute func change *p is  100
after execute func change *p is 200

Go 语言系列13:指针_内存地址_02

指针与切片

Go 语言系列13:指针_指针变量_03


切片与指针一样是引用类型,如果我们想通过一个函数改变一个数组的值,可以将该数组的切片当作参数传给函数,也可以将这个数组的指针当作参数传给函数。但 Go 中建议使用第一种方法,即将该数组的切片当作参数传给函数,因为这么写更加简洁易读。

package main

import "fmt"

// 使用切片
func changeSlice(value []int) {
value[0] = 200
}

// 使用数组指针
func changeArray(value *[3]int) {
(*value)[0] = 200
}

func main() {
x := [3]int{10, 20, 30}
changeSlice(x[:])
fmt.Println(x)

y := [3]int{10, 20, 30}
changeArray(&y)
fmt.Println(y)
}

Go 语言系列13:指针_内存地址_02

Go 中不支持指针运算

Go 语言系列13:指针_指针变量_03


学过 C 语言的人肯定知道在 C 中支持指针的运算,例如:​​p++​​ ,但这在 Go 中是不支持的。

package main

func main() {
x := [...]int{20, 30, 40}
p := &x
p++ // error
}

参考文献:

[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.




举报

相关推荐

0 条评论