go不允许*int直接转*float64,因二者二进制解释不同,须经unsafe.pointer中转;uintptr仅用于临时指针运算,不可长期持有。

为什么 *int 不能直接转 *float64
Go 编译器会直接报错 cannot convert p (type *int) to type *float64,这不是语法糖缺失,而是类型系统主动拦截——因为 int 和 float64 的二进制解释完全不同,强行 reinterpret 内存会导致未定义行为。即使两者都是 8 字节,*int 指向的 0x3FF0000000000000 解释为整数是 4607182418800017408,解释为 float64 才是 1.0。编译器不让你跳步,就是为了防止你把“地址”当“值”用。
- 错误写法:
q := (*float64)(p)(编译失败) - 正确路径必须是:
*int → unsafe.Pointer → *float64 - 若源目标类型大小不等(如
*int32转*float64),解引用时必然越界或截断
unsafe.Pointer 是唯一合法中转站
unsafe.Pointer 是 Go 中唯一能桥接任意指针类型的类型,它不携带类型信息,也不参与 GC 跟踪,纯粹是内存地址的容器。所有跨类型指针操作都必须经过它,且只能走这两条路:
-
*T → unsafe.Pointer(任意指针可转) -
unsafe.Pointer → *U(必须显式转换,不能隐式)
例如把 int64 变量按 IEEE 754 double 解释:
i := int64(0x3FF0000000000000) fptr := (*float64)(unsafe.Pointer(&i)) fmt.Println(*fptr) // 输出 1.0
注意:这没做任何值转换,只是换了一种读法;如果 i 值不是合法的 float64 位模式,结果不可预测。
从指针构造 []byte 的常见写法及陷阱
这是网络/序列化场景高频操作,比如把结构体首地址转成字节切片以便 syscall 或写入 buffer:
- 安全前提:确保原变量生命周期足够长(不能是局部栈变量被提前回收)
- 必须检查长度:
len(data) >= unsafe.Sizeof(x),否则[:]切片会 panic - 推荐写法(Go 1.17+):
unsafe.Slice((*byte)(unsafe.Pointer(&x)), unsafe.Sizeof(x)) - 旧写法(兼容老版本):
(*[size]byte)(unsafe.Pointer(&x))[:size:size],注意末尾双冒号避免底层数组逃逸
错误示例:[]byte(unsafe.Pointer(&x)) —— 这语法根本不存在,Go 不允许直接把 unsafe.Pointer 转 []byte。
uintptr 用于指针运算,但不能存作长期引用
uintptr 是整数,能做加减、偏移计算,常配合 unsafe.Offsetof 定位结构体字段,但它不是指针:
-
uintptr值不会被 GC 认为是“存活引用”,一旦对应的原始对象被回收,该地址就成悬空地址 - 不能把
uintptr保存到全局变量或返回给调用方,除非你手动保证对象生命周期 - 要重新取值,必须先转回
unsafe.Pointer,再转具体指针类型,例如:(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(s.a)))
真正需要 unsafe 的地方其实非常窄:syscall 传参、零拷贝解析固定格式二进制包、内存池重用。业务逻辑里出现 unsafe.Pointer,大概率说明设计可以重构——比如该用 encoding/binary 就别手撕字节。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











