3

I am trying to verify the size of a struct. For some reasons, it's giving me a size of 18 rather than the expected 14 bytes (the union should have a max of 8 + 2 + 2 = 12 bytes). Can someone help me?

typedef struct __attribute__((__packed__)) def
{
        union {
                double x;   /* 8 bytes */
                char *y;    /* 8 bytes as for 64-bit system */
                struct {
                        struct def *array; /* 8 bytes */
                        unsigned short a;  /* 2 bytes */
                        unsigned short b;  /* 2 bytes */
                } z;
        } w;
        unsigned short v;  /* 2 bytes */
} DEF, *DEFP;

int main(int argc, char **argv) {
        printf("size of DEF = %lu\n", sizeof(DEF));
        printf("size of unsigned short = %lu\n", sizeof(unsigned short));
        printf("size of struct def * = %lu\n", sizeof(struct def *));
        printf("size of char * = %lu\n", sizeof(char *));
        printf("size of double = %lu\n", sizeof(double));
}

Here's what it displays when I run it:

$ gcc test.c && ./a.out
size of DEF = 18
size of unsigned short = 2
size of struct def * = 8
size of char * = 8
size of double = 8
alk
  • 69,737
  • 10
  • 105
  • 255
fatdragon
  • 2,211
  • 4
  • 26
  • 43
  • 1
    Try to prove your assumptions. Show the size of only the union and of only the inner struct. – Yunnosch Feb 14 '18 at 07:41
  • 2
    You might be experiencing data structure packing/padding. https://en.wikipedia.org/wiki/Data_structure_alignment – Taha Paksu Feb 14 '18 at 07:43
  • 1
    Or https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member – Taha Paksu Feb 14 '18 at 07:43
  • What is the layout, i.e. the relative offsets of the parts? Also, you know that things like padding is introduced to provide alignment guarantees for the CPU. Not having these has multiple bad effects, from slowdowns to crashes. – Ulrich Eckhardt Feb 14 '18 at 07:48
  • thanks all! makes perfect sense. – fatdragon Feb 14 '18 at 08:04

2 Answers2

3

__attribute__((__packed__)) refers to struct def only. It does not pack the anonymous struct inside the anonymous union inside struct def.

So most likely those two members

                   unsigned short a;  /* 2 bytes */
                   unsigned short b;  /* 2 bytes */

"use" 4 but 2 bytes.


Unrelated to your question: C mandates to print size_ts using the z length modifier:

 printf("size of DEF = %zu\n", sizeof (DEF));
alk
  • 69,737
  • 10
  • 105
  • 255
  • 2
    I would've expected 4 bytes of padding at the end of the inner structure. – melpomene Feb 14 '18 at 07:48
  • 2 bites padding after `unsigned short a;` lacks consistency as then there would be 2 bites padding after `unsigned short v;` making size 20. Go with [@melpomene](https://stackoverflow.com/questions/48781938/unexpected-size-when-a-struct-has-a-union#comment84565403_48782010) – chux - Reinstate Monica Feb 14 '18 at 13:59
  • @chux: The 18 you get because the out `struct` *is* packed. I tested this (on cygwin, using gcc 4.9.2 :}) and it's in fact the inner `struct` which, when packed, makes `struct def` shrink from 18 to 14 bytes. Packing the `union`, BTW, does not change anything. – alk Feb 14 '18 at 14:17
  • Curious, with `offsetof(DEF,w.z.b) - offsetof(DEF,w.z.a)` do you get 2 or 4 or ? – chux - Reinstate Monica Feb 14 '18 at 15:18
  • @chux: 1st of all this expression "*Packing the union, BTW, does not change anything*" of mine is wrong, sry for this. 14 bytes I *only* get when packing both, the `union` *and* the inner `struct`. To answer your question: The result of `offsetof(DEF,w.z.b) - offsetof(DEF,w.z.a)` is `2` in of the four possible cases. – alk Feb 14 '18 at 16:06
2

Writing:

struct foo {
    struct bar {
        ...
    } x;
};

Is not different than writing:

struct bar { ... };
struct foo {
    struct bar x;
};

Why does it matter? It matters because in the second example it should be obvious why the attributes that you apply to foo don't automatically apply to bar. This means that your outer struct will be packed, but that doesn't change what the components mean.

The inner struct you have will not be packed and it's pretty obvious that if it follows the usual rules of alignment that most architectures follow its size will be a multiple of its most strictly aligned member which will be the pointer. So the size of the struct inside the union will be 16, not 12 as you assumed. Which means that the size of the union will be 16 as well.

Art
  • 19,807
  • 1
  • 34
  • 60