3

I was trying to give a "logical" counter-example to this answer indicating that sorting the members of a struct based on their size would minimize padding, when I encountered what seems to me as illogical.

Imagine the following struct:

struct A { int32_t a; int16_t b; }; 

sizeof this struct would normally be padded to 8 bytes to make sure a is aligned for example in an array of struct A.

Now image these other structs:

struct B { struct A a, b; int16_t c, d; }; struct C { struct A a; int16_t c; struct A b; int16_t d; }; 

As expected struct B has size 20 due to padding. However, I would have expected struct C to have size 16 since padding could be avoided, but ideone and gcc (with or without optimization) give a size of 24 bytes, clearly padding 2 bytes after each of the members.

My reasoning is that struct A in reality has only 6 bytes and should be padded when necessary, for example in an array of struct A or its usage in struct B. However, in struct C padding of struct A is unnecessary and c could have been placed where the padding of a could have been and the same with d and b.

Why doesn't the compiler minimize the padding by putting c where the padding of a would be?


P.S. I understand that sizeof(struct A) must return 8. Otherwise something like memset(array_of_A, 0, N * sizeof *array_of_A) won't work properly since array_of_A would contain padding while N * sizeof *array_of_A would ignore that padding.

The only thing I can think of that could be a problem is that with the optimization above then sizeof(struct C) would be smaller than the sizeof of all its members. However, I can't think of a case where such a thing could become a problem (i.e. a usage that is not based on undefined behavior).

6
  • Not sure I understand the question, but I think what you are asking would require sizeof() to return different values on the same type. For example, sizeof(foo.A) to be different then sizeof(struct A). So copying these things would be really difficult. Commented Nov 16, 2013 at 16:00
  • Why doesn't it rearrange the members? Because the C compiler was not designed to do so in the first place. It's arranged in the order you declare them. C is a fairly low-level language giving us more control over many things, it would be bad if the compiler changed things around. If I declared members A, B and C, I would expect them to be laid out in memory in that order. Commented Nov 16, 2013 at 16:40
  • @CharlieBurns, My last paragraph was trying to address that. sizeof(foo.a) wouldn't need to be different from sizeof(struct A). Sure if sizeof(foo.a) returns 8, then it contains foo.c in its padding, but you can't access that part since its outside the struct and accessing it would be undefined behavior. So, what would be the problem if sizeof(foo.a) returns 8? Commented Nov 16, 2013 at 21:35
  • @JeffMercado, I'm not asking the compiler to reorder the members. The standard specifically forbids that (C11, 6.7.2.1.6) Commented Nov 16, 2013 at 21:39
  • Wouldn't memcpy(&someC.a, &someA, sizeof(someC.a)) write over someC.c? I guess that's the same as the answer, and what I was getting at with my comment. Commented Nov 16, 2013 at 22:27

2 Answers 2

3
struct C someC; struct A someA; *(struct A*)&(someC.a) = someA; 

The assignment above may fail (mistakenly write to someC.c) if the padding you describe is supported by compilers.

EDITED: The example above relies on the compiler behavior when assigning structs. As I have known (an just checked) gcc copies as the struct is a flat region of memory, which is not member-wise.

EDITED: Changed from "would fail" to "may fail" since it's not defined if the padding bits shall be copied, see item 6 of section 6.2.6.1 of ISO_IEC_9899_2011:

When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.51)

and footnote 51):

51) Thus, for example, structure assignment need not copy any padding bits.

Sign up to request clarification or add additional context in comments.

3 Comments

sizeof(someC.a) == sizeof(someA)
That's a pitfall, correct. It seams that the standard could have excluded the padding in the end and define that that padding shouldn't be copied. But I guess that would have probably introduced an unnecessary restriction.
I'm accepting the other answer since it presents an example that unconditionally breaks.
2

memcpy(&someC.a, &someA, sizeof(someC.a)) would write over someC.c.

That's what I was trying to get at with my comment about sizeof()'s having to be different. For that memcpy() to work, sizeof(someC.a) would have to be different from sizeof(someA) which just seems to be asking for a lot of trouble and hard to find bugs.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.