如果字段顺序不同,结构的大小也将不同 [英] 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{}))
}
-
结构
A
和B
具有相同的字段,但是如果以不同的顺序指定,它们将导致大小不同.为什么?
Struct
A
andB
have the same fields, but if specified in different order they result in different size. why?
结构C
的大小为零.系统为a := C{}
分配了多少内存?
Size of struct C
is zero. How much memory is allocated by the system for a := C{}
?
谢谢.
推荐答案
1.结构尺寸
TL; DR; (摘要):如果对字段进行重新排序,则将使用不同的隐式填充,并且隐式填充计入struct
的大小.
1. Struct sizes
TL;DR; (Summary): Different implicit padding will be used if you reorder the fields, and the implicit padding counts towards the size of the struct
.
请注意,结果取决于目标体系结构.您发布的结果在GOARCH=386
时适用,但是在GOARCH=amd64
时,A{}
和B{}
的大小均为24个字节.
Note that the result depends on the target architecture; results you posted applies when GOARCH=386
, but when GOARCH=amd64
, sizes of both A{}
and B{}
will be 24 bytes.
结构的字段的地址必须对齐,并且int64
类型的字段的地址必须为8字节的倍数. 规范:软件包unsafe
:
Address of fields of a struct must be aligned, and the address of fields of type int64
must be a multiple of 8 bytes. Spec: Package unsafe
:
计算机体系结构可能要求对内存地址进行对齐;也就是说,如果变量的地址是一个因子的倍数,则该变量的类型为 alignment .函数
Alignof
接受一个表达式,该表达式表示任何类型的变量,并以字节为单位返回变量(类型)的对齐方式.
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个字节:
Align of int64
is 8 bytes:
fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8
因此,在A
的情况下,因为第一个字段为bool
,所以在A.a
之后有一个7字节的隐式填充,因此类型为int64
的A.b
可以从一个地址开始,该地址为8的倍数得到保证(确切地需要7字节填充),因为struct
本身与8的倍数对齐的地址对齐,因为这是其所有字段的最大大小.请参阅:规范:尺寸对齐保证:
So in case of A
since first field is bool
, there is a 7-byte implicit padding after A.a
so that A.b
which is of type int64
can start on an address that is a multiple of 8. This (that 7-byte padding is needed exactly) is guaranteed as the struct
itself is aligned to an address which is a multiple of 8, because that is the largest size of all of its fields. See: Spec: Size alignment guarantees:
对于结构类型为变量
x
的变量:unsafe.Alignof(x)
是x
的每个字段f
的所有值unsafe.Alignof(x.f)
中最大的一个,但至少为1
.
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
),在类型bool
的B.a
字段之后将只有3个字节的隐式填充,因为此字段后跟一个字段键入int
(大小为4个字节),而不是int64
.
In case of B
(and if GOARCH=386
which is your case) there will only be a 3-byte implicit padding after the B.a
field of type bool
because this field is followed by a field of type int
(which has size of 4 bytes) and not int64
.
如果GOARCH=386
,int
的对齐为4个字节,如果GOARCH=amd64
,则对齐为8个字节:
Align of int
is 4 bytes if GOARCH=386
, and 8 bytes if GOARCH=amd64
:
fmt.Println(unsafe.Alignof((int(0)))) // Prints 4 if GOARCH=386, and 8 if GOARCH=amd64
使用 unsafe.Offsetof()
来查找字段的偏移量:
Use unsafe.Offsetof()
to find out the offsets of fields:
// 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
,则输出(在游乐场上尝试):
Output if GOARCH=386
(try it on the 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 struct{}
和零长度的数组,例如. [0]int
或元素大小为零(且具有任意长度)的数组.
So the spec just gives a hint to use the same memory address but it's not a requirement. But current implementations follow it. That is, no memory will be allocated for values of types having a size of zero, this includes the empty struct struct{}
and arrays of zero length, e.g. [0]int
, or arrays whose elements has a size of zero (and with arbitrary length).
请参见以下示例:
a := struct{}{}
b := struct{}{}
c := [0]int{}
d := [3]struct{}{}
fmt.Printf("%p %p %p %p %p", &a, &b, &c, &d, &d[2])
输出(在游乐场上尝试):所有地址都相同.
Output (try it on the Go Playground): All the addresses are the same.
0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c
有关有趣且相关的主题,请阅读:> 戴夫切尼:填充很难
For an interesting and related topic, read: Dave Cheney: Padding is hard
这篇关于如果字段顺序不同,结构的大小也将不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!