13

Consider the following union:

typedef union {
    struct { // Anonymous struct
        int red;
        int green;
        int blue;
    };
    int colorChannels[3];
} Color;

Regardless of the system this code is compiled or executed on, will the fields in the anonymous struct always line up with the indexes in colorChannels?

In other words, does the specification require the memory address of myColor.colorChannels[0] to be the same as the address of myColor.red?

Additionally, would the answer be different if the code was compiled as C++ rather than C?

Please note that I'm asking about the specific case where every element in the union has the same type/size (i.e. int in this case)

EKW
  • 2,059
  • 14
  • 24
  • The answer is "usually, but not necessary". You might want to add a static assert. Note that in C++ type punning through unions like this is undefined behaviour. – HolyBlackCat Feb 22 '18 at 23:05
  • No. And it's hard to see why you would care if they did, unless you were to use such alignment for some very non-portable conversion antics. –  Feb 22 '18 at 23:07
  • Note: The compiler is allowed to add padding between members, for example to support alignment favorable to the processor's addressing. – Thomas Matthews Feb 22 '18 at 23:08
  • In C++ (and maybe in C, not sure) struct placed in union along with int array results in undefined behaviour (in specified cases, but very common ones). Look at a question I've asked recently and I was using the same thing: [C++ make member object unnamed](https://stackoverflow.com/q/48916460/4386320) – Poeta Kodu Feb 22 '18 at 23:12
  • @NeilButterworth I was hoping they would because it would be super convenient for my code if I could reference the color channels both by name and by number. – EKW Feb 22 '18 at 23:18
  • 1
    @EKW That's very much not the way to achieve this in C++. – Baum mit Augen Feb 22 '18 at 23:18
  • @BaummitAugen I know. I'm trying to do this in C. It's not a big deal if I can't. It would just be inconvenient. – EKW Feb 22 '18 at 23:19
  • @EKW And that's why double-tagging is a bad idea. If you tag C++, you better be happy with C++ answers. – Baum mit Augen Feb 22 '18 at 23:20
  • 2
    Then why did you tag this as C++? I've just removed it. –  Feb 22 '18 at 23:20
  • Ah. My mistake. I tagged it as C++ because I thought the answer might be different for C vs C++. I didn't realize it might be confusing. Sorry. – EKW Feb 22 '18 at 23:22
  • The answer *is* different for C vs C++. That's why it's not a good idea to ask both questions at once. If there is good answer for C and another equally good one for C++, which would you accept? ;-) – alain Feb 23 '18 at 00:04

2 Answers2

9

Regardless of the system this code is compiled or executed on, will the fields in the anonymous struct always line up with the indexes in colorChannels?

Not necessarily. The implementation can add byte "padding" between individual variables of the struct. "Padding" is just additional bytes of memory inserted into the memory definition between variables of or at the end of the struct in an implementation-defined way. If this happened, then your integer array would not be aligned with the memory layout of the struct (unless the padding was only at the end of the struct, maybe). The order of the variables in the struct in memory should be consistent across implementations (in that variable a will be followed by b followed by c in memory), but, again, the byte padding between can still be there (e.g., a is followed by b in memory but there is padding between a and b such that they are not right after each other in memory).

For some compilers like gcc, there are ways to modify how it handles padding. This could help guarantee the struct would be in memory alignment with your integer array, but this could cause other memory problems downstream.

In other words, does the specification require the memory address of myColor.colorChannels[0] to be the same as the address of myColor.red?

If there is no padding between red, green, and blue of the struct, then the indexes of colorChannels would match up with each variable in memory.

Daniel
  • 1,291
  • 6
  • 15
  • While formally correct from a language-lawyer point of view; considering that the array cannot have padding but must still have the ints properly aligned, it would be *extremely surprising* for the struct to select a different packing. On the other hand, this is a trick that might work in C but is outright forbidden in C++, so not the best idea to use. – Bo Persson Feb 23 '18 at 00:43
7

The C Standard doesn't make any guarantees about this, but it is likely to be correct on any actual system that exists.

I would suggest adding a compile-time check to the code so that in case there is some system that adds padding, you get a compilation error which you can deal with at the time:

_Static_assert( sizeof(Color) == 3 * sizeof(int),  "We're on a weird system boys." );

NB. _Static_assert was added in C11, prior to that you can use some hack as described here

M.M
  • 138,810
  • 21
  • 208
  • 365