如果字段顺序不同,则结构具有不同的大小
Struct has different size if the field order is different
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{}))
}
Struct A
和 B
具有相同的字段,但如果以不同的顺序指定,它们会导致不同的大小。为什么?
结构 C
的大小为零。系统为a := C{}
分配了多少内存?
谢谢。
1。结构尺寸
TL;DR; (总结): 如果您重新排序字段,将使用不同的隐式填充,并且隐式填充计入 struct
.
的大小
注意结果取决于目标架构;您发布的结果在 GOARCH=386
时适用,但在 GOARCH=amd64
时,A{}
和 B{}
的大小均为 24 字节。
结构体字段地址必须对齐,int64
类型字段地址必须是8字节的倍数。 Spec: Package unsafe
:
Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable's type's alignment. The function Alignof
takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes.
int64
的对齐是 8 个字节:
fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8
所以在 A
的情况下,因为第一个字段是 bool
,在 A.a
之后有一个 7 字节的隐式填充,所以 A.b
是 int64
可以从 8 的倍数的地址开始。这(恰好需要 7 字节填充)得到保证,因为 struct
本身与 8 的倍数的地址对齐,因为这是其所有字段中最大的字段。参见:Spec: Size alignment guarantees:
For a variable x
of struct type: unsafe.Alignof(x)
is the largest of all the values unsafe.Alignof(x.f)
for each field f
of x
, but at least 1
.
在 B
的情况下(如果 GOARCH=386
是你的情况)在类型为 [=27= 的 B.a
字段之后将只有一个 3 字节的隐式填充] 因为此字段后跟 int
类型的字段(大小为 4 个字节)而不是 int64
.
如果GOARCH=386
,int
的对齐是4个字节,如果GOARCH=amd64
:
,则为8个字节
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))
如果 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。零尺寸值
Spec: Size alignment guarantees:
A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.
所以规范只是提示使用相同的内存地址,但这不是必需的。但是当前的实现遵循它。也就是说,不会为大小为零的类型的值分配内存,这包括空结构 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
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{}))
}
Struct
A
和B
具有相同的字段,但如果以不同的顺序指定,它们会导致不同的大小。为什么?结构
C
的大小为零。系统为a := C{}
分配了多少内存?
谢谢。
1。结构尺寸
TL;DR; (总结): 如果您重新排序字段,将使用不同的隐式填充,并且隐式填充计入 struct
.
注意结果取决于目标架构;您发布的结果在 GOARCH=386
时适用,但在 GOARCH=amd64
时,A{}
和 B{}
的大小均为 24 字节。
结构体字段地址必须对齐,int64
类型字段地址必须是8字节的倍数。 Spec: Package unsafe
:
Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable's type's alignment. The function
Alignof
takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes.
int64
的对齐是 8 个字节:
fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8
所以在 A
的情况下,因为第一个字段是 bool
,在 A.a
之后有一个 7 字节的隐式填充,所以 A.b
是 int64
可以从 8 的倍数的地址开始。这(恰好需要 7 字节填充)得到保证,因为 struct
本身与 8 的倍数的地址对齐,因为这是其所有字段中最大的字段。参见:Spec: Size alignment guarantees:
For a variable
x
of struct type:unsafe.Alignof(x)
is the largest of all the valuesunsafe.Alignof(x.f)
for each fieldf
ofx
, but at least1
.
在 B
的情况下(如果 GOARCH=386
是你的情况)在类型为 [=27= 的 B.a
字段之后将只有一个 3 字节的隐式填充] 因为此字段后跟 int
类型的字段(大小为 4 个字节)而不是 int64
.
如果GOARCH=386
,int
的对齐是4个字节,如果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))
如果 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。零尺寸值
Spec: Size alignment guarantees:
A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.
所以规范只是提示使用相同的内存地址,但这不是必需的。但是当前的实现遵循它。也就是说,不会为大小为零的类型的值分配内存,这包括空结构 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