digitalmars.D.learn - Packing of Struct Fields
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (19/19) Oct 16 2020 Why is `T.sizeof` 12 instead of 8 when `U.sizeof` is 8 in the
- ag0aep6g (7/30) Oct 16 2020 S.sizeof: 4 bytes for the int + 1 byte for the bool + 3 bytes padding so...
- Steven Schveighoffer (11/44) Oct 16 2020 To further explain this -- the padding is added so things like pointer
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (16/18) Oct 17 2020 In my code sample above one can only access the first element
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (5/24) Oct 17 2020 So AFAICT the key question becomes:
- Adam D. Ruppe (4/7) Oct 17 2020 Yes. Put an align on the OUTSIDE of the struct you are nesting,
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (18/21) Oct 17 2020 I though this would do the trick but not...
- ag0aep6g (4/18) Oct 17 2020 c does come directly after s. The padding between b and c is part of s.
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (11/15) Oct 17 2020 I understand that. I don't want the alignment of `S` to change. I
- Adam D. Ruppe (26/29) Oct 17 2020 That padding is part of S. It is at the end, after its fields,
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (2/3) Oct 17 2020 Nice! Thanks!
- Steven Schveighoffer (8/25) Oct 17 2020 There might be some good reasons for this.
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (4/6) Oct 17 2020 Agree, a pragma, say `pragma(pack)`, to control this would be
- =?UTF-8?Q?Ali_=c3=87ehreli?= (83/106) Oct 16 2020 I have a function that dumps member layout of structs, which someone may...
Why is `T.sizeof` 12 instead of 8 when `U.sizeof` is 8 in the following example? struct S { int i; bool b; } struct T { S s; char c; } struct U { int i; bool b; char c; } ?
Oct 16 2020
On 16.10.20 22:32, Per Nordlöw wrote:Why is `T.sizeof` 12 instead of 8 when `U.sizeof` is 8 in the following example? struct S { int i; bool b; } struct T { S s; char c; } struct U { int i; bool b; char c; } ?S.sizeof: 4 bytes for the int + 1 byte for the bool + 3 bytes padding so that the int is aligned = 8 bytes. T.sizeof: 8 bytes for the S + 1 byte for the char + 3 bytes padding so that the S is aligned = 12 bytes. U.sizeof: 4 bytes for the int + 1 byte for the bool + 1 byte for the char + 2 bytes padding so that the int is aligned = 8 bytes.
Oct 16 2020
On 10/16/20 4:44 PM, ag0aep6g wrote:On 16.10.20 22:32, Per Nordlöw wrote:To further explain this -- the padding is added so things like pointer arithmetic on an array work. For example, if you have a T* t, and you say t += 1, you want it to go to the next T, not to a misaligned spot. You can also override this with align keyword. But I don't recommended this unless you know what you are doing. Misaligned reads/writes are different on different architectures, but even if they work and don't crash your program, they are going to be slower. https://dlang.org/spec/attribute.html#align -SteveWhy is `T.sizeof` 12 instead of 8 when `U.sizeof` is 8 in the following example? struct S { int i; bool b; } struct T { S s; char c; } struct U { int i; bool b; char c; } ?S.sizeof: 4 bytes for the int + 1 byte for the bool + 3 bytes padding so that the int is aligned = 8 bytes. T.sizeof: 8 bytes for the S + 1 byte for the char + 3 bytes padding so that the S is aligned = 12 bytes. U.sizeof: 4 bytes for the int + 1 byte for the bool + 1 byte for the char + 2 bytes padding so that the int is aligned = 8 bytes.
Oct 16 2020
On Friday, 16 October 2020 at 21:26:12 UTC, Steven Schveighoffer wrote:To further explain this -- the padding is added so things like pointer arithmetic on an array work.In my code sample above one can only access the first element anyhow so I don't understand why this restriction is imposed here. struct S { int i; bool b; } struct T { S s; // reinterpreting this as an array can only access this first element anyway char c; // so why can't this be aligned directly after `s` without any padding? }
Oct 17 2020
On Saturday, 17 October 2020 at 12:35:37 UTC, Per Nordlöw wrote:On Friday, 16 October 2020 at 21:26:12 UTC, Steven Schveighoffer wrote:So AFAICT the key question becomes: Can `align`s be inserted in S or/and T so that T is packed to 8 bytes but still aligned to 8 bytes? I don't see why this shouldn't be the default behaviour...To further explain this -- the padding is added so things like pointer arithmetic on an array work.In my code sample above one can only access the first element anyhow so I don't understand why this restriction is imposed here. struct S { int i; bool b; } struct T { S s; // reinterpreting this as an array can only access this first element anyway char c; // so why can't this be aligned directly after `s` without any padding? }
Oct 17 2020
On Saturday, 17 October 2020 at 12:44:44 UTC, Per Nordlöw wrote:Can `align`s be inserted in S or/and T so that T is packed to 8 bytes but still aligned to 8 bytes?Yes. Put an align on the OUTSIDE of the struct you are nesting, then put one INSIDE the struct you want the contents packed.I don't see why this shouldn't be the default behaviour...It is generally slower.
Oct 17 2020
On Saturday, 17 October 2020 at 12:44:44 UTC, Per Nordlöw wrote:Can `align`s be inserted in S or/and T so that T is packed to 8 bytes but still aligned to 8 bytes? I don't see why this shouldn't be the default behaviour...I though this would do the trick but not... struct S { int i; // 4 bytes short s; // 2 byte bool b; // 1 byte } static assert(S.sizeof == 8); static assert(S.alignof == 4); align(4) struct T { align(4) S s; align(1) char c; } static assert(T.alignof == 4); // TODO: static assert(T.sizeof == 8); T.sizeof is still 12 bytes, I want it to be 8.
Oct 17 2020
On 17.10.20 14:35, Per Nordlöw wrote:struct S { int i; bool b; } struct T { S s; // reinterpreting this as an array can only access this first element anyway char c; // so why can't this be aligned directly after `s` without any padding? }c does come directly after s. The padding between b and c is part of s. If you don't want that padding, you can use `align(1)` to define S without padding. But then 75% of the ints in an S[] will be misaligned.
Oct 17 2020
On Saturday, 17 October 2020 at 12:51:21 UTC, ag0aep6g wrote:c does come directly after s. The padding between b and c is part of s. If you don't want that padding, you can use `align(1)` to define S without padding. But then 75% of the ints in an S[] will be misaligned.I understand that. I don't want the alignment of `S` to change. I want the padding after `s` in `T` to be avoided and have `c` start at byte-offset 7. I don't see why this padding is needed in the case where only a single (1-element array of) `S` is stored as a field inside another aggregate. Ali's code prints: === Memory layout of 'T' (.sizeof: 12, .alignof: 4) === 0: S s 8: char c 9: ... 3-byte PADDING
Oct 17 2020
On Saturday, 17 October 2020 at 13:00:59 UTC, Per Nordlöw wrote:I understand that. I don't want the alignment of `S` to change. I want the padding after `s`That padding is part of S. It is at the end, after its fields, but still part of it. S's layout doesn't depend on what else is around it.in `T` to be avoided and have `c` start at byte-offset 7.Use a union. struct S { int i; // 4 bytes short s; // 2 byte bool b; // 1 byte } static assert(S.sizeof == 8); static assert(S.alignof == 4); struct T { union { S s; struct { align(1): ubyte[7] _ignore_me; char c; } } } static assert(T.alignof == 4); static assert(T.sizeof == 8); static assert(T.c.offsetof == 7);
Oct 17 2020
On Saturday, 17 October 2020 at 13:23:38 UTC, Adam D. Ruppe wrote:Use a union.Nice! Thanks!
Oct 17 2020
On 10/17/20 9:00 AM, Per Nordlöw wrote:On Saturday, 17 October 2020 at 12:51:21 UTC, ag0aep6g wrote:There might be some good reasons for this. For one, what happens if you copy the s? Does it copy the c too? For another, this is how C does it, so I would expect it to at least match C for compatibility. I think it *should* be possible to do this, if it's not already, just with pragmas. (i.e. pack T but not S). -Stevec does come directly after s. The padding between b and c is part of s. If you don't want that padding, you can use `align(1)` to define S without padding. But then 75% of the ints in an S[] will be misaligned.I understand that. I don't want the alignment of `S` to change. I want the padding after `s` in `T` to be avoided and have `c` start at byte-offset 7. I don't see why this padding is needed in the case where only a single (1-element array of) `S` is stored as a field inside another aggregate. Ali's code prints: === Memory layout of 'T' (.sizeof: 12, .alignof: 4) === 0: S s 8: char c 9: ... 3-byte PADDING
Oct 17 2020
On Saturday, 17 October 2020 at 13:42:46 UTC, Steven Schveighoffer wrote:I think it *should* be possible to do this, if it's not already, just with pragmas. (i.e. pack T but not S).Agree, a pragma, say `pragma(pack)`, to control this would be great to avoid the unsafe union hack.
Oct 17 2020
On 10/16/20 1:32 PM, Per Nordl=C3=B6w wrote:Why is `T.sizeof` 12 instead of 8 when `U.sizeof` is 8 in the following==20example? =20 struct S { =C2=A0=C2=A0=C2=A0 int i; =C2=A0=C2=A0=C2=A0 bool b; } =20 struct T { =C2=A0=C2=A0=C2=A0 S s; =C2=A0=C2=A0=C2=A0 char c; } =20 struct U { =C2=A0=C2=A0=C2=A0 int i; =C2=A0=C2=A0=C2=A0 bool b; =C2=A0=C2=A0=C2=A0 char c; } =20 ?I have a function that dumps member layout of structs, which someone may = find useful: http://ddili.org/ders/d.en/memory.html#ix_memory..offsetof It prints the following for these types: =3D=3D=3D Memory layout of 'S' (.sizeof: 8, .alignof: 4) =3D=3D=3D 0: int i 4: bool b 5: ... 3-byte PADDING =3D=3D=3D Memory layout of 'T' (.sizeof: 12, .alignof: 4) =3D=3D=3D 0: S s 8: char c 9: ... 3-byte PADDING =3D=3D=3D Memory layout of 'U' (.sizeof: 8, .alignof: 4) =3D=3D=3D 0: int i 4: bool b 5: char c 6: ... 2-byte PADDING Copied here: struct S { int i; bool b; } struct T { S s; char c; } struct U { int i; bool b; char c; } void printObjectLayout(T)() if (is (T =3D=3D struct) || is (T =3D=3D union)) { import std.stdio; import std.string; writefln("=3D=3D=3D Memory layout of '%s'" ~ " (.sizeof: %s, .alignof: %s) =3D=3D=3D", T.stringof, T.sizeof, T.alignof); /* Prints a single line of layout information. */ void printLine(size_t offset, string info) { writefln("%4s: %s", offset, info); } /* Prints padding information if padding is actually * observed. */ void maybePrintPaddingInfo(size_t expectedOffset, size_t actualOffset) { if (expectedOffset < actualOffset) { /* There is some padding because the actual offset * is beyond the expected one. */ const paddingSize =3D actualOffset - expectedOffset; printLine(expectedOffset, format("... %s-byte PADDING", paddingSize)); } } /* This is the expected offset of the next member if there * were no padding bytes before that member. */ size_t noPaddingOffset =3D 0; /* Note: __traits(allMembers) is a 'string' collection of * names of the members of a type. */ foreach (memberName; __traits(allMembers, T)) { mixin (format("alias member =3D %s.%s;", T.stringof, memberName)); const offset =3D member.offsetof; maybePrintPaddingInfo(noPaddingOffset, offset); const typeName =3D typeof(member).stringof; printLine(offset, format("%s %s", typeName, memberName)); noPaddingOffset =3D offset + member.sizeof; } maybePrintPaddingInfo(noPaddingOffset, T.sizeof); } void main() { printObjectLayout!S(); printObjectLayout!T(); printObjectLayout!U(); } Ali
Oct 16 2020