Go 数据类型(九)指针

指针简介

变量
本质:对一块内存空间的命名
可以通过引用变量名使用这块内存空间存储的值
指针
指向这些变量值所在的内存地址的值

变量值所在内存地址的值 不等于 该内存地址存储的变量值

简单示例
a := 100
var ptr *int  // 声明指针类型
ptr = &a      // 初始化指针类型值为变量 a  &为取地址符
fmt.Println(ptr)
fmt.Println(*ptr)

通过*ptr获取指针指向内存地址存储的变量,称为间接引用

为什么要引入指针?

  • 为程序员提供操作变量对应内存数据结构的能力
  • 提高程序性能(指针可以直接指向内存地址,可以节省内存空间)
使用场景
  • 类型指针:传递数据时无须拷贝数据,go中指针不能进行偏移和运算
  • 切片:指向数组起始元素的指针,数量,容量

基本使用

指针的声明与初始化

之所以可以节省内存,是因为指针指向的内存地址大小固定,与存储值的类型无关

  • var ptr *int
    ptr = &a
  • ptr := &a 自动根据a的类型判断指针类型
  • new()函数:ptr := new(int)
    格式化输出时,%p标识指针类型
通过指针传值
func swap(a, b int)  {
    a, b = b, a
    fmt.Println(a, b)
}

func main() {
    a := 1
    b := 2
    swap(a, b)
    fmt.Println(a, b)
}
2 1
1 2
func swap(a, b *int)  {
    *a, *b = *b, *a
    fmt.Println(*a, *b)
}

func main() {
    a := 1
    b := 2
    swap(&a, &b)
    fmt.Println(a, b)
}
2 1
2 1
unsafe.Pointer
  • 任何类型的指针都可以被转化为 unsafe.Pointer
  • unsafe.Pointer 可以被转化为任何类型的指针
  • uintptr 可以被转化为 unsafe.Pointer
  • unsafe.Pointer 可以被转化为 uintptr
指针类型转换

unsafe.Pointer万能指针,可表示任意可寻址的指针类型

i := 10
var p *int = &i

var fp *float32 = (*float32)(unsafe.Pointer(p))
*fp = *fp * 10
fmt.Println(i)  // 100

注意unsafe.Pointer可以在任何指针类型之间转化,但这绕过了Go的类型安全机制,所以不是一个安全的操作

指针运算实现

uintptr是可用于存储指针的整型,而整型是可以进行代数运算
因此,将unsafe.Pointer转化为uintptr后,对应指针就具备了运算能力

arr := [3]int{1, 2, 3}
ap := &arr

sp := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(ap)) + unsafe.Sizeof(arr[0])))
*sp += 3
fmt.Println(arr)
[1 5 3]

这样就可以绕过Go指针的安全限制,实现指针的动态偏移代数运算
这会导致即使数组越界,也不会报错,而是返回下一个内存地址存储的值
这就破坏了内存安全限制,所以要尽量避免使用,必须使用时也要非常谨慎

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容