Go 的指针

123 阅读3分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。

概念

  • 指针是存储另一个变量的内存地址的变量
  • 变量是一种使用方便的占位符,用于引用计算机内存地址
  • 指针保存了值的内存地址,指针可以指向任何一个值的内存地址

  • 变量 b 的值为156,存储在内存地址 0x1040a124
  • 变量 a 保存 b 的地址,现在 a 指向 b

为什么说 Go 指针简单?

因为指针不能运算,而 C 语言的指针可以运算

语法格式

var v *T

类型*T是指向 T 类型值的指针,其零值为 nil

声明指针的🌰

var v *int
var s *string

& 操作符

&操作符会生成一个指向其操作数的指针,其实就是返回变量的内存地址

package main

import "fmt"

func main() {
   var a int = 10   

   fmt.Printf("变量的地址: %x\n", &a  )
}

运行结果

变量的地址: c000018030

* 操作符

*操作符表示指针指向的底层的值,就是获取指针指向的变量的值

package main

import "fmt"

func main() {
	var a int = 10
	b := &a

	fmt.Printf("变量的地址: %x \nb 指针的值:%v\n", &a, *b)
}

运行结果

变量的地址: c00009c000 
b 指针的值:10

指针的综合🌰

package main

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i                                                                                // 指向 i
	fmt.Printf("type %T, value %v ,address %v, i-address %v \n", p, *p, p, &i) // 通过指针读取 i 的值,并且查看他们的内存地址
	*p = 222                                                                               // 通过指针设置 i 的值
	fmt.Println(i)                                                                         // 查看 i 的值

	p = &j // 重新指向 j

	fmt.Printf("type %T, value %v ,address %v, j-address %v \n", p, *p, p, &j) // 通过指针读取 j 的值,并且查看他们的内存地址

	*p = *p / 37   // 通过指针对 j 进行除法运算
	fmt.Println(j) // 查看 j 的值
}

运行结果

type *int, value 42 ,address 0xc000018030, i-address 0xc000018030 
222
type *int, value 2701 ,address 0xc000018038, j-address 0xc000018038 
73

同一个指针变量可以指向不同的值的内存地址


空指针

  • 当一个指针被定义后没有分配到任何变量时,它的值为 nil
  • nil 指针也称为空指针
  • nil 在概念上和其它语言的 null、None、nil、NULL 一样,都指代零值或空值
  • 一个指针变量通常缩写为 ptr
package main

import "fmt"

func main() {
	var a *int

	fmt.Printf("type %T, value %v", a, a)
}

运行结果

type *int, value <nil>

Go 语言使用值传递?引用传递?

Go 语言只有值传递一种方式

值传递

package main

import "fmt"

func f(a int){
    a = 3
}

func main() {
	var a int = 0
    f(a)
    fmt.Println(a)
}

运行结果

0

值传递的原理

拷贝变量 a 到参数 a,它们的内存地址是不一致的

指针传递

package main

import "fmt"

func f(pa *int){
    *pa = 3 
}

func main() {
	var a int = 0
    // 传递的是指针
    f(&a)
    fmt.Println(a)
}

运行结果

3

指针传递的原理

  • 实际上也是值传递
  • 只不过是拷贝了 &a 给 pa,就是将 a 的内存地址拷贝给了 pa
  • 所以 pa 指针保存的也是变量 a 的内存地址
  • 当修改 pa 的时候,也会同步修改 a 的值
  • 总结: Go 没有引用传递,只是通过指针传递实现了引用传递

两个变量交换值

这应该是经典案例了

通过值传递完成

通过 Go 函数可以返回多个值的特性来完成

package main

import "fmt"

func swapByValue(a, b int) (int, int) {
	a, b = b, a
	return a, b
}

func main() {
	a, b := 11, 22
	fmt.Println(a, b)
	
	a, b = swapByValue(a, b)
	fmt.Println(a, b)
}

运行结果

11 22
22 11

通过指针传递来完成

package main

import "fmt"

func swapByPointer(a, b *int) {
	*a, *b = *b, *a
}

func main() {
	a, b := 11, 22
	fmt.Println(a, b)

	swapByPointer(&a, &b)
	fmt.Println(a, b)
}

运行结果

11 22
22 11