I don’t think I’ve ever used union
for anything, but today I came across a very interesting use case to avoid bit-shifting tricks when dealing with data embedded in numbers.
What’s a union?
A
union
is a user-defined type in which all members share the same memory location. This definition means that at any given time, a union can contain no more than one object from its list of members. It also means that no matter how many members a union has, it always uses only enough memory to store the largest member.
Example:
[pastacode lang=”cpp” manual=”union%20IntChar%20%7B%0A%20%20%20%20unsigned%20int%20i%3B%0A%20%20%20%20char%20c%3B%0A%7D%3B%0A%0AIntChar%20foo%3B%0Afoo.i%20%3D%2065%3B%20%2F%2F%20’A’%20in%20ASCII%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0Aprintf(%22i%3A%20%25d%2C%20c%3A%20%25c%5Cn%22%2C%20foo.i%2C%20foo.c)%3B%0A%0A%2F%2F%20i%3A%2065.%20c%3A%20A” message=”Represent an integer as a char, or char as int” highlight=”” provider=”manual”/]
We can also do the same with an anonymous union, and directly use the variables, which will change each other’s values
[pastacode lang=”cpp” manual=”union%20%7B%0A%20%20unsigned%20int%20i%3B%0A%20%20char%20c%3A%0A%7D%0Ac%3D’A’%3B%0Aprintf(%22i%3A%20%25d%2C%20f%3A%20%25c%5Cn%22%2C%20i%2C%20c)%3B%0A%2F%2F%20i%3A%2065%2C%20c%3A%20A” message=”” highlight=”” provider=”manual”/]
Let’s apply this feature to a 32 bit integer (4 bytes) and a 4 byte array
This I believe might come in handy if you need to use integers as arrays of 8 bit numbers because you can use the array ‘[]’ operator to access the individual bytes in the number without having to do bit shifting tricks (>>,<<,&,|) to extract them or manipulate them
[pastacode lang=”cpp” path_id=”51909d0741b8901a8e59d704104c2ef7″ file=”int_as_array.cpp” highlight=”” lines=”” provider=”gist”/]
Build and output:
$ g++ int_as_array.cpp && ./a.out
a: 0xaabbccdd
a[0]: 0xdd
a[1]: 0xcc
a[2]: 0xbb
a[3]: 0xaa
a: 0xaabbccff
DO NOT USE THIS TECHNIQUE IN PRODUCTION CODE:
Here’s a word about this trick from my very esteemed friend (and elite coder) Dave Nicponski
Doing this correctly is highly dependent upon following and understanding the language spec, fyi
— 👉 @virus_dave@mastodon.social (@virus_dave) November 12, 2021
I should clarify. You basically CANNOT do this safely in general. Specifically, "It's undefined behavior to read from the member of the union that wasn't most recently written." (with very restrictive exceptions). You may see "reasonable" behavior depending on the compiler…
— 👉 @virus_dave@mastodon.social (@virus_dave) November 12, 2021
…used, which may provide additional guarantees not present in the C++ spec, but it's not portable, and a compliant compiler would be allowed to do just about anything it wants to break your program (like return a zero value on the read, or skip the preceding write, etc).
— 👉 @virus_dave@mastodon.social (@virus_dave) November 12, 2021