Kaushal Agarwal
Kaushal Agarwal

Reputation: 80

How does the two different syntax of union declaration give me different size?

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.

Upvotes: 3

Views: 319

Answers (3)

rustyx
rustyx

Reputation: 85481

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;

Upvotes: 1

john
john

Reputation: 88007

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.

Upvotes: 2

user1316208
user1316208

Reputation: 687

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).

Upvotes: 7

Related Questions