4

For example,the default alignment of a union is following:

union{
   uint32_t v4;
   __uint128_t v6;
}ip;

//in memory
//aaaa
//bbbbbbbbbbbbbbbb

But I want to have a union right aligned:

//            aaaa
//bbbbbbbbbbbbbbbb

Is it possible to achieve this in C?

timrau
  • 22,578
  • 4
  • 51
  • 64
Sam Chiu
  • 113
  • 1
  • 7
  • I'm not sure if the first drawing is correct. Maybe if you had a 128-bit architecture. – mkrieger1 Apr 11 '21 at 15:27
  • How would you use such a union? – dbush Apr 11 '21 at 15:39
  • @dbush When you save a ipv4 address in it, you can use `ip.v6` to get a v6 address directly(with top 96 bit 0). But there is a defect that you need to fill the first 96 bits with 0 in advance, otherwise you don't know it's whether a ipv6 address or a ipv4 address. – Sam Chiu Apr 12 '21 at 03:10

2 Answers2

7

You can use a C11 anonymous struct for this.

union {
#pragma pack(1)
    struct {
        char padding_[sizeof(__uint128_t) - sizeof(uint32_t)];
        uint32_t v4;
    };
#pragma pack(0)
    __uint128_t v6;
} ip;

// usage
ip.v4 = 0x7F000000;
Ray Hamel
  • 1,289
  • 6
  • 16
  • 5
    I suggest to add attribute 'packed' to ensure that no implicit padding bytes were added – tstanisl Apr 11 '21 at 15:45
  • Good idea. I added `#pragma pack(1)` to the anonymous struct, which should work with MSVC as well. – Ray Hamel Apr 11 '21 at 15:54
  • 2
    Careful with 'packed' structs. Packing does not only remove padding. It will also tells the compiler that the struct can and will have an arbitrary alignment, which may greatly affect performance depending on the platform and the code. See https://godbolt.org/z/1q8ascav9. – ElderBug Apr 12 '21 at 03:29
  • @ElderBug I think the union should have the alignment of `__uint128_t`, since the `#pragma pack(1)` applies to the anonymous struct, not the union as a whole. – Ray Hamel Apr 12 '21 at 16:55
  • 1
    If the packing is only for the inner struct then yes. To do that you need to add `#pragma pack(0)` after the struct. – ElderBug Apr 12 '21 at 18:47
  • On a "big endian" machine I think this code does the opposite of what the OP wants. ;-) – Brendan Apr 12 '21 at 21:25
  • The `pragma pack(1)` is unnecessary och confusing. In addition, it is not standard C, as far from all compilers support it. In fact, in some environments, the compiler could generate code that would access the element byte by byte, which isn't what you would like. – Lindydancer Sep 12 '22 at 18:55
  • @Lindydancer `__uint128_t` (as in OP's question) isn't standard C either (also, treating a `uint32_t` as a `char` would, regardless of alignment, itself violate the standard). Do you know of any such platforms where `__uint128_t` is available, and it makes sense to have a data type for IP addresses? If there are any, one can use conditional compilation to address those cases. – Ray Hamel Feb 24 '23 at 17:36
  • @Brendan I don't see why it would be incorrect (potentially slower than a union without the padding, yes) on a big-endian machine. In any case it's what OP asked for. – Ray Hamel Feb 24 '23 at 17:52
1

You might define

union{
   struct {
     char uselessgap[12];
     uint32_t v4;
   } s;
   __uint128_t v6;
}ip;

Then in your C code use s.v4 instead of v4; and you could even

#define v4 s.v4

I am not sure it is a good idea (that macro).

Another approach would be with your GCC plugin (for a particular version of the GCC compiler) and some __attribute__ handled by it.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 4
    In C11 or higher you can use an [anonymous struct member](https://stackoverflow.com/questions/8932707/when-are-anonymous-structs-and-unions-useful-in-c11) and write `ip.v4` without the need for a macro. – Nate Eldredge Apr 11 '21 at 15:32