Go的unsafe.Pointer

147 阅读2分钟

unsafe.Pointer 是 Go 语言标准库 unsafe 包中的一个特殊类型,用于在不同类型的指针之间进行 无类型转换,允许你绕过 Go 的类型系统进行底层操作,属于“不安全”的操作。

🎯 使用场景

  • 类型不兼容的指针转换(例如从 *int 转为 *float64
  • uintptr 配合操作底层内存地址
  • 修改只读字段(例如字符串的只读字节)
  • 实现类似 C 的结构体偏移访问

和普通指针的区别

unsafe.Pointer 和普通的 &T 指针(即 *T)在 Go 中有本质上的区别

类型说明
*T是类型安全的指针,指向类型 T 的数据。编译器知道它的类型信息,能自动进行内存对齐、类型检查等。
unsafe.Pointer是一种通用的无类型指针,可以转换为任意类型的指针或从任意类型的指针转换过来。编译器不检查其指向的类型和用法。

✅ 示例 1:不同类型指针转换


package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var x int = 100
	// 将 *int 转换为 *float64(不安全!仅用于演示)
	p := unsafe.Pointer(&x)
	f := (*float64)(p) // 假装 x 是 float64
	fmt.Println(*f)    // 输出不确定,因为解释方式不对
}

❗这是错误用法,仅作为类型转换的演示。实际运行会出现乱码或未定义行为。


✅ 示例 2:uintptrunsafe.Pointer 配合做地址偏移(字段偏移)


package main

import (
	"fmt"
	"unsafe"
)

type Person struct {
	age   int
	score float64
}

func main() {
	p := Person{age: 25, score: 98.5}
	// 获取结构体首地址
	ptr := unsafe.Pointer(&p)

	// 偏移到第二个字段(score)
	scorePtr := (*float64)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(p.score)))
	fmt.Println(*scorePtr) // 输出:98.5
}

✅ 示例 3:修改字符串的只读内容(危险行为)


package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	s := "hello"
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	// 强转为字节数组指针修改内容
	b := (*[5]byte)(unsafe.Pointer(sh.Data))
	b[0] = 'H'
	fmt.Println(s) // 输出 "Hello"
}

❗这是标准库明确禁止的行为,仅作教学展示。在某些平台上可能 crash!


✅ 小结

场景是否安全推荐使用
类型间转换(*T1*T2
uintptr 搭配偏移操作
直接操作底层数据(如字符串修改)