3

I am declaring a union data type inside a struct (but two different syntax). I get different output for the size of the structs. What's the catch here?

union u {
    double x;
    int y;
};

union {
    double x;
    int y;
} u_1;

struct s1 {
    int a;
    union {
        int b;
        double c;
    };
};  

struct s2 {
    int a;
    union u{
        int b;
        double c;
    };
};

int main()
{
    u u1;
    s1 s_1;
    s2 s_2;

    cout<< sizeof(u1)<< " "<<sizeof(u_1)<<" " <<sizeof(s_1)<<" " <<sizeof(s_2);
}

I expected the output: 8 8 16 16 but the actual output is 8 8 16 4.

timrau
  • 22,578
  • 4
  • 51
  • 64

3 Answers3

7

s2 declares a union but makes no member of it.
s1 is using "anonymous union" semantics: (from https://en.cppreference.com/w/cpp/language/union)

Members of an anonymous union are injected in the enclosing scope (and must not conflict with other names declared there).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
user1316208
  • 667
  • 1
  • 5
  • 12
  • Thanks a lot for your answer. I have one more doubt. If an anonymous union is injected, shouldn't the struct use its maximum size? Shouldn't be it 12. – Kaushal Agarwal Aug 15 '19 at 09:29
  • @KaushalAgarwal I see what you mean, from your output its obvious your int is size 4, it seems the compiler adds a bit of padding. ( see https://stackoverflow.com/questions/3486854/size-of-structure-with-a-char-a-double-an-int-and-a-t ) However i dont see why this is neccessary in this case, perhaps someone more experienced can answer that. – user1316208 Aug 15 '19 at 09:38
  • as in the linked question the bottom line seems to be that the "data padding is implementation dependent" loosely translated as "compiler can do whatever the hell it wants". – user1316208 Aug 15 '19 at 09:43
2

Change

struct s2 {
int a;
union u {
    int b;
    double c;
    };
};

to

struct s2 {
int a;
union u {
    int b;
    double c;
    } x;
};

and you'll get the same size.

Your version declares an inner type but does not create an instance of union u.

Your s1 uses an anonymous union which effectively does inject an instance of your union into s1 but without naming it.

john
  • 85,011
  • 4
  • 57
  • 81
1

Basically it comes down to alignment requirements. For x86, the alignment requirements for primitive types are to align the type on its size, i.e. a 4-byte type will be aligned on 4 bytes, an 8-byte type on 8 bytes, etc.

On x86, a class/union is aligned on the largest of its primitive members, i.e. if there is an 8-byte member, the class/union will be aligned on 8 bytes.

In case of s1 we have an anonymous union, which is injected into the outer scope (i.e. it becomes part of s1)

struct s1 {
    int a;
    union {
        int b;
        double c;
    };
};

The union's largest type is double, 8 bytes so the union is aligned on 8 bytes. The compiler adds 4 bytes of padding after a to get the union to align on 8 bytes.

We can confirm it with this:

cout << offsetof(s1, a) << " " << offsetof(s1, b) << " " << offsetof(s1, c) << endl;

Prints

0 8 8

In case of s2

union u { ... }; is a declaration. It defines no element of type u. So s2 has only one member, int a; and is 4 bytes long.

To change it into a definition, give it a name: union u { ... } u;

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • @KaushalAgarwal I dont understand why this is accepted answer, I answered first and more comprehensively, only thing added here is **incorrect statement** `A class/union is aligned on the largest of its primitive members` as this behaviour is implementation-defined – user1316208 Aug 19 '19 at 08:27
  • @user1316208 - the context of that statement is x86. Yes, it's implementation-defined. – rustyx Aug 19 '19 at 09:19
  • its incorrect even on x86, its implementation defined on the compiler level – user1316208 Aug 19 '19 at 11:16
  • What ABI? c++ has no ABI. – user1316208 Aug 19 '19 at 12:34
  • The C++ [standard-layout](https://timsong-cpp.github.io/cppwp/n3337/class#9) types are [binary-compatible with C](https://timsong-cpp.github.io/cppwp/n3337/basic.types#footnote-42). – rustyx Aug 19 '19 at 12:53
  • and C has no ABI either, https://stackoverflow.com/questions/4489012/does-c-have-a-standard-abi So your argument is irrelevant. – user1316208 Aug 19 '19 at 13:29
  • It is perfectly legal c++ for compiler to setup the structure padding as it wishes, and in fact many compilers allow that to be set by user. Just google #pragma pack(). – user1316208 Aug 19 '19 at 13:39