package main import ( "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具有相同的字段,但是如果以不同的顺序指定,它们将导致大小不同。为什么?
A
B
结构的大小C为零。系统为多少内存分配a := C{}?
C
a := C{}
谢谢。
TL; DR; (摘要):如果对字段进行重新排序,将使用不同的隐式填充,并且隐式填充计入的大小struct。
struct
注意结果取决于目标体系结构。 适用于您发布结果时GOARCH=386,但是当GOARCH=amd64,既大小的A{}和B{}将是24个字节。
GOARCH=386
GOARCH=amd64
A{}
B{}
结构的字段地址必须对齐,并且类型的字段地址int64必须为8字节的倍数。规格:包装unsafe:
int64
unsafe
计算机体系结构可能需要 对齐 内存地址; 也就是说,如果变量的地址是一个因子的倍数,则该变量的类型为 alignment 。该函数Alignof采用表示任何类型变量的表达式,并以字节为单位返回(变量的类型)对齐方式。
Alignof
对齐int64为8个字节:
fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8
因此,在A第一个字段为的情况下,bool后面有7个字节的隐式填充,A.a因此该A.b类型的填充int64可以在8的倍数的地址上开始。这保证了(确切地需要7个字节的填充)为在struct本身对准这是8的倍数的地址,因为这是所有字段的最大尺寸。请参阅:规格:尺寸对齐保证:
bool
A.a
A.b
对于x结构类型的变量:unsafe.Alignof(x)是的unsafe.Alignof(x.f)每个字段f的所有值中的最大值x,但至少是1。
x
unsafe.Alignof(x)
unsafe.Alignof(x.f)
f
1
在B(如果GOARCH=386是您的情况)情况下,在B.atype字段之后将只有3个字节的隐式填充,bool因为此字段后面是type字段int(其大小为4个字节),而不是int64。
B.a
int
int如果对齐则为4个字节,如果对齐为GOARCH=3868个字节GOARCH=amd64:
fmt.Println(unsafe.Alignof((int(0)))) // Prints 4 if GOARCH=386, and 8 if GOARCH=amd64
使用unsafe.Offsetof()找出领域的偏移量:
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
规格:尺寸对齐保证:
如果结构或数组类型不包含大小大于零的字段(或元素),则其大小为零。 两个不同的零大小变量在内存中可能具有相同的地址。
因此,该规范仅提示使用相同的内存地址,但这不是必需的。但是,当前的实现遵循它。也就是说,不会为大小为零的类型的值分配任何内存,这包括空结构struct{}和长度为零的数组,例如[0]int,或元素大小为零(且具有任意长度)的数组。
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:填充很难