Why is the position of `[0] byte` in the structure important?

[0]byte

in golang shouldn't take up memory. But these two structures are of different sizes.

type bar2 struct {
    A int
    _ [0]byte
}

type bar3 struct {
    _ [0]byte
    A int   
}

      

So why is position taking place here [0]byte

?

By the way, I am using a method unsafe.Sizeof()

to check the size of the structure. See full example .

+3


source to share


2 answers


This is due to a complex addition.

First, let me rename structs and fields a little to make it easier to talk about them:

type bar1 struct {
    A [0]byte
    I int
}

type bar2 struct {
    I int
    A [0]byte
}

      

This of course doesn't change the size and offsets, as can be checked on the Go Playground :

bar1 size:     4
bar1.A offset: 0
bar1.I offset: 0

bar2 size:     8
bar2.I offset: 0
bar2.A offset: 4

      

The size of the type value [0]byte

is zero, so it is bar1

completely impossible to reserve any space for the first field ( bar1.A

) and lay out the field bar1.I

with 0 offset.

The question is, why can't the compiler do the same in the second case (c bar2

)?

The field must have an address, which must be after the memory area reserved for the previous field. In the first case, the first field bar1.A

has a size of 0, so the second field can have an offset of 0, it will not "overlap" with the first field.

In the case bar2

, the second field cannot have an address (and therefore an offset) that overlaps with the first field, so its offset cannot be less than the size int

, which is 4 bytes in the case of 32-bit architectures (and 8 bytes in the case of 64- bit arch).

It still looks fine. But since it bar2.A

has a size of zero, why bar2

can't the size of the struct be 4 bytes (or 8 in a 64-bit architecture)?



This is because it is perfectly valid for addresses (and variables) that have size 0 . So what?

In case, the bar2

compiler must insert 4 (or 8) bytes' complement, otherwise the received field address bar2.A

will point outside the memory area reserved for the value of the type bar2

.

As an example, without padding, the value bar2

could have an address 0x100

, size 4, so the memory reserved for the struct value has a range of addresses 0x100 .. 0x103

. The address bar2.A

will be 0x104

outside the memory of the structure. In the case of an array of this structure (for example x [5]bar2

), if the array starts with 0x100

, the address x[0]

will be 0x100

, the address x[0].A

will be 0x104

, and the address of the subsequent element x[1]

will also be 0x104

, but the address of a different structure value! Not cool.

To avoid this, the compiler inserts indentation (which will be 4 or 8 bytes depending on the arch) so that accessing the address bar2.A

will not result in the address being outside the memory of the structure, which could otherwise raise questions and cause problems with garbage collection (for example, if only the address is stored bar2.A

, but not the structure or another pointer to it or its other fields, the entire structure should not be garbage collected, but since no pointer points to its memory area, this would seem to be valid for this). The nested padding will be 4 (or 8) bytes, because Spec: size and alignment guarantees:

For a variable of x

type struct: unsafe.Alignof(x)

is the largest of all values unsafe.Alignof(x.f)

for each field f

of x

, but not less 1

.

If so, adding an extra margin int

will make both structures equal in size:

type bar1 struct {
    I int
    A [0]byte
    X int
}

type bar2 struct {
    A [0]byte
    I int
    X int
}

      

Indeed, they both have 8 bytes on a 32-bit architecture (and 16 bytes on a 64-bit architecture) (try it on the Go Playground ):

bar1 size:     8
bar1.I offset: 0
bar1.A offset: 4
bar1.X offset: 4

bar2 size:     8
bar2.A offset: 0
bar2.I offset: 0
bar2.X offset: 4

      

See related question: Structure has a different size if the field order is different from

+5


source


The reason - "holes":

Holes are unused spaces added by the compiler to ensure that the next field or element is correctly aligned relative to the start of a structure or array [1]

For example (numbers are based on whatever hardware the playground uses to play):

struct {bool; float64; int16} // 24 bytes
struct {float64; bool; int16} // 16 bytes

      



You can check the layout of the structure using:

  • unsafe.Alignof returns the required alignment
  • unsafe.Offsetof calculates the offset of the field relative to the start of its inclusion structure, including holes

[1] p354 Donovan, Kernighan, AD, BK, 2016. The GO Programming Language. 1st ed. New York: Addison-Wesley.

+1


source







All Articles