6

When I type in the following code, I get a result of 32 bytes. It supposed to be 28 bytes, though. Why do I get that result?

//size of union variable

union U {
    int x[7];
    double y[3];
}z;

int main(){
    printf("%d",sizeof(z));
    return 0;
}
hat
  • 781
  • 2
  • 14
  • 25
  • Related and IMO not strictly a dupe (this question is about unions, the following question is about structs): [**Why isn't sizeof for a struct equal to the sum of sizeof of each member?**](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) – Andrew Henle Aug 28 '19 at 13:07
  • 1
    Also, `printf("%d",sizeof(z));` is incorrect and undefined behavior. The [proper format specifier for `size_t` is `%zu`](https://port70.net/~nsz/c/c11/n1570.html#7.21.6.1p7) - `z` for `size_t`, `u` for `unsigned`. – Andrew Henle Aug 28 '19 at 13:17

2 Answers2

10

Unions, like structs, are padded to agree with the required alignment of their members.

double is aligned on a multiple of eight on your platform, and 32 is the smallest multiple of eight that is at least 28.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • um shouldnt it be 3 x8 =24 plus 7x4 =28, so totaling 52? – akshay kishore Aug 28 '19 at 12:15
  • 3
    This answer should add, why it matters (ie. use in arrays). – hyde Aug 28 '19 at 12:16
  • I dont get your point, It should make available the space required for both the integer array and the double array right? – akshay kishore Aug 28 '19 at 12:17
  • 1
    @akshaykishore A union only stores one of its members at any given time. Its size is that of its largest member, plus padding and alignment adjustments. – molbdnilo Aug 28 '19 at 12:20
  • @akshaykishore You're mixing it up with a struct. – klutt Aug 28 '19 at 12:22
  • "A union only stores one of its members at any given time." - not necessarily true. The union in this example can store x[0] and y[2] without any conflict. But yes, writing to y[0] will overwrite x[0] and x[1]. I think I know what you meant (treating the entire arrays of x and y as members), but I'm just adding this in case it is helpful to others in understanding unions. – h0r53 Aug 28 '19 at 12:33
  • @CaitLANJenner Isn't that undefined behavior, even if it seems like it works? – Joseph Sible-Reinstate Monica Aug 28 '19 at 12:44
  • 1
    @JosephSible it isn't undefined, and there are actually good reasons for it. The bytes of a union are stored contiguously. Using array indices allows you to write to specific groups of bytes. If a union of char[4] and int is size 4 bytes, storing a value into the int portion has a defined impact on the char portion. Each element of the char array will represent whatever the corresponding bytes of the int are. This is more commonly used in union data structures performing tasks such as disassembly, where the union fields are often defined with a specific bit-width. – h0r53 Aug 28 '19 at 12:58
  • 2
    @CaitLANJenner But be careful. [The padding bytes for the `struct` being written to can be updated](https://port70.net/~nsz/c/c11/n1570.html#6.2.6.1p6): "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**." In this case, assigning a value to `y[2]` could change the value of `x[6]` in the example union. Admittedly that's only *likely* to happen when directly assigning `struct` values themselves. – Andrew Henle Aug 28 '19 at 13:20
5

As explained in another answer, padding is used to facilitate struct (and union) member alignment and is the reason for the unexpected size that you see.

To see the size you expected (i.e. without padding), you can force the code to to ignore the alignment issue by using a pragma statement. #pragma pack specifically is used to indicate to the compiler that the struct/union be packed in a specific way, by changing the addressing alignment from the default value to something else.

The following results in a size of 28:

#pragma pack(1) //Sets compiler to align on 1 byte address boundaries
                //(Effectively removing all padding)

union U {
    int x[7];
    double y[3];
}z;

#pragma pack() //Set packing back to what it was before

int main(){
    printf("%d",sizeof(z));
    return 0;
}
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • 1
    Does this `#pragma` work with other compilers than Micro$oft's? For GCC it's common to use `__attribute__((pack))`. – the busybee Aug 28 '19 at 20:10
  • @thebusybee - The `pragma` directive is defined by the C standard, not Microsoft, so it is portable. Any decent C compiler (including Microsoft's, GCC, CLANG, etc.) since C99 implements it. _[There is lots written about it, eg this GCC page.](https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html)_ I use 3 different C compilers regularly, and have seen no issues using it in them. Have you had a problem with the compiler you use? – ryyker Oct 01 '19 at 12:19
  • 1
    Thank you very much for providing some source! Most of the projects I worked on had to use a really old version of GCC. Sorry for the noise. – the busybee Oct 01 '19 at 12:38
  • Well, I was not questioning `#pragma` but `pack`. Of course `#pragma` is known since the old days, but `pack` is **not** mentioned in the standard. Each compiler is free to ignore unknown pragmas. – the busybee Oct 01 '19 at 12:53
  • @thebusybee - Well, you of coarse are correct, and I am surprised it is not included. Nevertheless, `pack`, although not specifically mentioned in the standard, _is_ often implemented argument for the `pragma` directive. As I have mentioned, I have used it regularly on several compilers. But nice to know that its availability is not guaranteed. – ryyker Oct 01 '19 at 13:11