在 Go 语言中,指针类型存的是变量的内存地址。传大结构体、或者要在函数里改到调用方的变量时,用的就是它。
1. 指针类型的声明
指针类型的声明使用 * 符号表示。例如,*int 表示一个指向 int 类型的指针。
go
var p *int2. 获取变量的地址
使用 & 符号可以获取变量的地址。
go
package main
import "fmt"
func main() {
var x int = 10
p := &x
fmt.Println(p) // 输出: 变量 x 的内存地址,例如 0xc0000140a0
}3. 通过指针访问和修改变量的值
使用 * 符号可以访问指针指向的变量的值,也可以通过指针修改变量的值。
go
package main
import "fmt"
func main() {
var x int = 10
p := &x
// 访问指针指向的值
fmt.Println(*p) // 输出: 10
// 通过指针修改值
*p = 20
fmt.Println(x) // 输出: 20
}4. 指针的使用场景
函数参数传递
在 Go 中,函数参数是按值传递的,这意味着函数接收的是参数的副本。通过传递指针,可以使函数直接操作原始变量。
go
func increment(p *int) {
*p++
}
func main() {
x := 10
increment(&x)
fmt.Println(x) // 输出: 11
}结构体字段
指针常用于结构体字段,以避免在函数传参时复制整个结构体,提高效率。
go
type Person struct {
Name string
Age int
}
func updatePerson(p *Person) {
p.Name = "Alice"
p.Age = 30
}
func main() {
person := Person{Name: "Bob", Age: 25}
updatePerson(&person)
fmt.Println(person) // 输出: {Alice 30}
}动态内存分配
使用 new 函数可以动态分配内存,并返回指向该内存的指针。
go
p := new(int)
*p = 100
fmt.Println(*p) // 输出: 1005. 指针的安全性和限制
不支持指针算术运算
与 C 和 C++ 不同,Go 不支持指针算术运算(如指针加减),这提高了程序的安全性,避免了很多常见的错误。
自动垃圾回收
Go 语言有自动垃圾回收机制,程序员无需手动管理内存。这大大简化了指针的使用,减少了内存泄漏和悬挂指针的可能性。
示例代码
把声明、取址、解引用、new 和改结构体串在一个例子里:
go
package main
import "fmt"
func main() {
var x int = 10
var p *int = &x
fmt.Println("Address of x:", p) // 输出: x 的内存地址,例如 0xc0000140a0
fmt.Println("Value of x:", *p) // 输出: 10
*p = 20
fmt.Println("New value of x:", x) // 输出: 20
// 使用函数修改变量值
increment := func(p *int) {
*p++
}
increment(&x)
fmt.Println("Incremented value of x:", x) // 输出: 21
// 动态内存分配
ptr := new(int)
*ptr = 100
fmt.Println("Value of ptr:", *ptr) // 输出: 100
// 指针作为结构体字段
type Person struct {
Name string
Age int
}
modifyPerson := func(p *Person) {
p.Name = "Alice"
p.Age = 30
}
person := Person{Name: "Bob", Age: 25}
modifyPerson(&person)
fmt.Println("Modified person:", person) // 输出: {Alice 30}
}总结
& 取地址,* 解引用,new 分配内存并返回指针。三个符号之外,Go 的指针还留着一条和 C 的关键差别:不支持指针算术运算,加上自动垃圾回收,悬挂指针这类问题少了一大截。