Go 语言指针详解
我们将学习指针在 Go 中的工作原理,我们还将了解 Go 指针与其他语言(如 C 和 C++)中的指针有何不同。
Go 中的指针学习起来既简单又有趣。一些 Go 编程任务使用指针更容易执行,而其他任务,例如按引用调用,如果不使用指针则无法执行。
什么是指针?
简单来说指针是存储另一个变量的内存地址的变量。在 Go 语言变量 一节,我们说过,每个变量都是一个内存位置,每个内存位置都有定义的地址。
在上图中,变量 b 具有值252并存储在内存地址 0x1040a124
。变量 a 保存 b 的地址。现在 a 指向 b。
声明指针
*T
是指针变量指向数据类型为T的值。
让我们编写一个声明指针的程序。
package main
import (
"fmt"
)
func main() {
b := 255
var a *int = &b
fmt.Printf("a 的类型为:%T\n", a)
fmt.Println("b的地址为:", a)
}
上述程序我们将 b 的地址分配给类型时 *int
的变量a。现在说 a 指向 b。当我们打印 a 中的值时,将会显示 b 的地址。上述代码编译执行结果如下
a 的类型为:*int
b的地址为: 0xc000134008
提醒
- 在实际运行中, b 的地址会显示不同的值,因为 b 的位置可以在内存中的任何位置。
指针的零值
指针的零值是Nil。Go 编译器为指针变量分配一个 Nil 值,以防你没有要分配的确切地址。这是在变量声明时完成的。分配为Nil 的指针称为 Nil指针
。
nil 指针是一个常量,其值为零。
package main
import (
"fmt"
)
func main() {
a := 25
var b *int
if b == nil {
fmt.Println("b 的值:", b)
b = &a
fmt.Println("b 初始化之后:", b)
}
}
b 在上面的程序中最初为 nil,然后将 a 的地址分配给它。 代码执行结果如下
b 的值: <nil>
b 初始化之后: 0xc000016070
在大多数操作系统上,不允许程序访问地址 0 处的内存,因为该内存是由操作系统保留的。但是,内存地址 0 具有特殊的意义;它表示指针不打算指向可访问的内存位置。但是按照惯例,如果指针包含 nil(零)值,则假定它不指向任何内容。 可以使用以下方式检查
nil 指针
if(ptr != nil) if(ptr == nil)
使用 new() 函数创建指针
Go 还提供了一个方便的函数 new()
来创建指针。new()
函数将一个类型作为参数,并返回一个指向作为参数传递的类型的新分配的零值的指针。
package main
import (
"fmt"
)
func main() {
size := new(int)
fmt.Printf("size的值: %d, 类型: %T, 地址: %v\n", *size, size, size)
*size = 85
fmt.Println("修改之后的size的值:", *size)
}
上述代码执行结果如下
size的值: 0, 类型: *int, 地址: 0xc000016070
修改之后的size的值: 85
访问指针变量
如何访问指针指向的变量的值?需要在变量前面加上星号 *
。 例如:*a 是访问a指向的值。
package main
import (
"fmt"
)
func main() {
b := 255
a := &b
fmt.Println("b 的地址是:", a)
fmt.Println("b 的值是:", *a)
}
上述代码编译执行结果如下
b 的地址是: 0xc000016070
b 的值是: 255
让我们再编写一个程序,b使用指针更改值。
package main
import (
"fmt"
)
func main() {
b := 255
a := &b
fmt.Println("b 的地址是:", a)
fmt.Println("b 的值是:", *a)
*a++
fmt.Println("修改后 b 的值是:", b)
}
上述代码编译执行结果如下
b 的地址是: 0xc000016070
b 的值是: 255
修改后 b 的值是: 256
Go 不支持指针运算
Go 不支持其他语言(如 C 和 C++)中存在的指针运算。
package main
func main() {
b := [...]int{109, 110, 111}
p := &b
p++
}
上面的程序会抛出编译错误
study.go:6:3: invalid operation: p++ (non-numeric type *[3]int)