为了账号安全,请及时绑定邮箱和手机立即绑定

如果字段顺序不同,则结构具有不同的大小

如果字段顺序不同,则结构具有不同的大小

Go
动漫人物 2021-11-29 15:50:41
package mainimport (    "fmt"    "unsafe")type A struct {    a bool    b int64    c int}type B struct {    b int64    a bool    c int}type C struct {}func main() {    // output 24    fmt.Println(unsafe.Sizeof(A{}))    // output 16    fmt.Println(unsafe.Sizeof(B{}))    // output 0    fmt.Println(unsafe.Sizeof(C{}))}结构A和B具有相同的字段,但如果以不同的顺序指定,它们会导致不同的大小。为什么?结构体的大小C为零。系统为 分配了多少内存a := C{}?谢谢。
查看完整描述

1 回答

?
PIPIONE

TA贡献1829条经验 获得超9个赞

1. 结构尺寸

TL; 博士; (总结):如果您对字段重新排序,将使用不同的隐式填充,并且隐式填充计入struct.


请注意,结果取决于目标架构;适用于您发布结果时GOARCH=386,但是当GOARCH=amd64,既大小的A{}和B{}将是24个字节。


struct的字段地址必须对齐,type字段的地址int64必须是8字节的倍数。规格: 包装unsafe:


计算机体系结构可能需要对齐内存地址;也就是说,对于一个变量的地址是一个因子的倍数,变量的类型的对齐方式。该函数Alignof采用一个表示任何类型变量的表达式,并以字节为单位返回变量(的类型)的对齐方式。


对齐int64是8个字节:


fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8

因此,如果A因为第一个字段是bool,则后面有一个 7 字节的隐式填充,A.a因此A.b类型int64可以从 8 的倍数的地址开始。这(正是需要 7 字节填充)保证为在struct本身对准这是8的倍数的地址,因为这是所有字段的最大尺寸。请参阅:规格:尺寸对齐保证:


对于xstruct 类型的变量:unsafe.Alignof(x)是 的unsafe.Alignof(x.f)每个字段f的所有值中的最大值x,但至少是1。


在这种情况下B(如果GOARCH=386是您的情况)在B.atype 字段之后只会有一个 3 字节的隐式填充,bool因为该字段后面是一个 type 字段int(大小为 4 个字节)而不是int64.


对齐int是 4 个字节 if GOARCH=386,和 8 个字节 if GOARCH=amd64:


fmt.Println(unsafe.Alignof((int(0))))   // Prints 4 if GOARCH=386, and 8 if GOARCH=amd64

使用unsafe.Offsetof()找出领域的偏移量:


// output 24

a := A{}

fmt.Println(unsafe.Sizeof(a),

    unsafe.Offsetof(a.a), unsafe.Offsetof(a.b), unsafe.Offsetof(a.c))


// output 16

b := B{}

fmt.Println(unsafe.Sizeof(b),

    unsafe.Offsetof(b.b), unsafe.Offsetof(b.a), unsafe.Offsetof(b.c))


// output 0

fmt.Println(unsafe.Sizeof(C{}))


var i int

fmt.Println(unsafe.Sizeof(i))

输出 if GOARCH=386(在Go Playground上尝试):


24 0 8 16

16 0 8 12

0

4

输出如果GOARCH=amd64:


24 0 8 16

24 0 8 16

0

8

2. 零尺寸值

规格:尺寸对齐保证:


如果结构或数组类型不包含大小大于零的字段(或元素),则其大小为零。两个不同的零大小变量可能在内存中具有相同的地址。


所以规范只是提示使用相同的内存地址,但这不是必需的。但是当前的实现遵循它。也就是说,不会为大小为零的类型值分配内存,这包括空结构struct{}和零长度的数组,例如[0]int,或元素大小为零(并且具有任意长度)的数组。


看这个例子:


a := struct{}{}

b := struct{}{}

c := [0]int{}

d := [3]struct{}{}


fmt.Printf("%p %p %p %p %p", &a, &b, &c, &d, &d[2])

输出(在Go Playground上尝试):所有地址都相同。


0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c

有关有趣且相关的主题,请阅读:Dave Cheney:Padding is hard


查看完整回答
反对 回复 2021-11-29
  • 1 回答
  • 0 关注
  • 141 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信