Is there a way to type-pun that is valid in both C and C++? Preferably low overhead, and avoiding trivial preprocessor hacks.
In C89, I know I can do something like this:
unsigned int float_bits(float num) {
return *(unsigned int *)#
}
However this violates C99's strict aliasing rule. So something like this might be more portable across the various C standards:
unsigned int float_bits(float num) {
union { float f; unsigned int i; } u;
u.f = num;
return u.i;
}
But I know that this is not valid C++, because only one member of a union can be “active” at a time. The typical solution given for both C and C++ is something like this:
unsigned int float_bits(float num) {
unsigned int i;
memcpy(&i, &num, sizeof(int));
return i;
}
However, this relies on the compiler being able to optimize away the call to memcpy. Is memcpy the only method that is portable across C and C++ standards?
In C and C++, constructs such as pointer type conversion and union ... are provided in order to permit many kinds of type punning, although some kinds are not actually supported by the standard language. Type aliasing or punning is when an object is referred to using different types, which may or may not be "compatible".
This kind of type punning is more dangerous than most. Whereas the former example relied only on guarantees made by the C programming language about structure layout and pointer convertibility, the latter example relies on assumptions about a particular system's hardware.
In C and C++, constructs such as pointer type conversion and union — C++ adds reference type conversion and reinterpret_cast to this list — are provided in order to permit many kinds of type punning, although some kinds are not actually supported by the standard language.
In other words, the sockets library uses type punning to implement a rudimentary form of polymorphism or inheritance . Often seen in the programming world is the use of "padded" data structures to allow for the storage of different kinds of values in what is effectively the same storage space.
unsigned int float_bits(float num) {
unsigned int i;
memcpy(&i, &num, sizeof(int));
return i;
}
there is no side effect from that call of memcpy
other than changing i
to have the same bytes as num
did.
It is true that compilers are free to insert a call to a library memcpy
function here. They are also free to insert 1 million NOOPs, a pong simulation AI training session, and try to seach the proof space for the goldblach's conjecture proof.
At some point you have to presume your compiler isn't hostile.
Every decent compiler understands what memcpy does.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With