如果字段顺序不同,结构的大小也将不同 [英] Struct has different size if the field order is different

查看:100
本文介绍了如果字段顺序不同,结构的大小也将不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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{}))
}

  1. 结构AB具有相同的字段,但是如果以不同的顺序指定,它们将导致大小不同.为什么?

  1. Struct A and B 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字节的隐式填充,因此类型为int64A.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 values unsafe.Alignof(x.f) for each field f of x, but at least 1.

B的情况下(如果您使用的是GOARCH=386),在类型boolB.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=386int的对齐为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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆