指针的使用
【空指针】
var t *T // 空指针
tmp := unsafe.Pointer(t)
log.Println(tmp) // 打印 <nil>
【访问 nil 指针的成员,异常】
// 通过指针操作成员 t.f 和(*t).f完全等价,只能使用.运算符
// invalid memory address or nil pointer dereference
log.Println(t.f) // 错误写法
// =================================================================
【空指针转换为能参与计算的通用指针】
tmp1 := uintptr(unsafe.Pointer(t))
log.Println(tmp1) // 打印 0
// =================================================================
【unsafe.Pointer 可以强转其他类型指针,空指针也可以,但非指针不可以】
如下异常
var t T
// cannot convert t (type T) to type unsafe.Pointer
tmp := unsafe.Pointer(t) // 错误写法
// =================================================================
访问 nil 指针的成员,更是异常
var t *T
log.Println(t.f)
// =================================================================
// 借鉴博客:https://blog.csdn.net/chuodun4566/article/details/100765727
结构体成员指针转换为结构体指针
错误写法
仿C的写法
func (f *F) T1() *T {
var dummy *T
fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))
return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(f)) - fieldOffset))
}
第一种写法
package main
import (
"log"
"unsafe"
)
type T struct {
a int
b int
f F
}
type F struct {
c int
d int
}
func (f *F) T1() *T {
// 先创建一个dummy
var dummy T
// 通过这个实实在在存在的dummy计算 T中成员f 相对 T头部 的偏移量
fieldOffset := uintptr(unsafe.Pointer(&(dummy.f))) - uintptr(unsafe.Pointer(&dummy))
// 通过传入的实参 *F 和 上文计算得到的 偏移量 计算 实实在在的 *T
return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(f)) - fieldOffset))
}
func main() {
var t T
tmp := &(t.f)
// &(t.f).T1() 这种写法错误
// 单目运算符 !、*(指针)、& 、++、--、+(正号)、-(负号) :执行顺序从右到左
// 要么换成 (&(t.f)).T1() 这种写法
// 要么换成 t.f.T1()
if &t != tmp.T1() {
log.Println("convert error");
} else {
log.Println("convert success");
}
}
为什么 t.f.T1() 等价于 (&(t.f)).T1()
先判断 t.f 是否等价于 (&(t.f))
log.Printf("%T %v", t.f, t.f)
log.Printf("%T %v", &(t.f), &(t.f))
打印结果:
2021/11/07 14:22:24 main.F {0 0}
2021/11/07 14:22:24 *main.F &{0 0}
则 t.f 不等价于 (&(t.f))
所以 t.f.T1() 是 main.F 调用 *F 的方法的时候发生隐式转换 // 有待学习函数形参实参转换
第二种写法
伪造一个*T的非nil指针
func (f *F) T2() *T {
// 真能伪造
var x struct{}
dummy := (*T)(unsafe.Pointer(&x))
fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))
return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(f)) - fieldOffset))
}
第三种写法
羊毛出在羊身上
func (f *F) T3() *T {
// 乱转
dummy := (*T)(unsafe.Pointer(f))
fieldOffset := uintptr(unsafe.Pointer(&(dummy.f))) - uintptr(unsafe.Pointer(dummy))
return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(f)) - fieldOffset))
}
unsafe.Pointer(&(dummy.f)) 和 unsafe.Pointer(&dummy.f) 效果一样
单目运算符,从右到左
第四种写法
提前存好偏移量
var fieldOffset uintptr
func init() {
dummy := (*T)(unsafe.Pointer(&fieldOffset))
fieldOffset = uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))
}
func (f *F) T4() *T {
return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(f)) - fieldOffset))
}
用 uintptr 参与计算
uintptr 和 unsafe.Pointer 可以互转
unsafe.Pointer 和 具体类型指针可以互转
// =================================================================