go-结构体成员指针到结构体自身指针

指针的使用

【空指针】
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 和 具体类型指针可以互转

// =================================================================

猜你喜欢

转载自blog.csdn.net/wangkai6666/article/details/121191782