Go Unsafe Offsetof explained

Last Modified: 2023/06/05

概述

Golang unsafe.Offsetof 函数返回结构体中某个字段在结构体内的偏移量。函数签名如下:

func Offsetof(x ArbitraryType) uintptr

其中 x 的形式必须是 structValue.field。

Go unsafe.Offsetof 实战

type M struct {
    x int8
    z int8
    y int64
}

m := M{}
fieldY := m.y
// invalid expression unsafe.Offsetof(fieldY)
println(unsafe.Offsetof(fieldY))
// 8
println(unsafe.Offsetof(m.y))
// 8
println(unsafe.Offsetof(M{}.y))

概述部分指出 Offsetof 函数接收的表达式的形式必须是 structValue.field,因此 unsafe.Offsetof(fieldY) 会报错。后面的两个都是符合要求的写法,但是他们为什么输出 8,而不是 2 呢?

如果按照常规想法,x 是结构体中第一个字段,因此偏移为 0,x 本身占用 1 个字节, 因此 z 的偏移为 1,z 也是占用一个字节,因此 y 的偏移为 2。

虽然这是符合大家的第一反应,然而事实却并非如此,由于内存对齐,导致 y 的实际偏移量为 8。如果不清楚内存对齐,建议去复习一下 unsafe.Alignof

如果大家认真看了 unsafe.Alignof,那么就很清楚 x 和 z 都是 1 字节对齐的,因此他们的偏移分别为 0 和 1。但是 y 是 8 字节对齐(假设为 64 位操作系统),因此 z 和 y 之间会填充 6 个空字节(padding),从而让 y 满足 8 字节对齐。

一般有:unsafe.Offsetof(structValue.field) = N * unsafe.Alignof(structValue.field),其中 N 为整数。

type M struct {
    x int16
    z int16
    y int16
}

m := M{}
// 0
println(unsafe.Offsetof(m.x))
// 2
println(unsafe.Offsetof(m.z))
// 4
println(unsafe.Offsetof(m.y))

x,z 和 y 都是 2 字节的,他们都是 2 字节对齐的,因此他们在结构体内的偏移分别为 0,2 和 4。

有问题吗?点此反馈!

温馨提示:反馈需要登录