How is this strict undefined pseudo-reduction?
I know I've seen this already, but I couldn't find a good explanation as to why this behavior is undefined:
#include <stdio.h>
#include <stdint.h>
//Common union for both types
union float_int {
float f;
uint32_t i;
};
int main(void) {
union float_int fi;
//This should be problematic
uint32_t* i_ptr = (uint32_t *)&fi.f;
fi.f = 10.0f;
printf("%f : %u\r\n", fi.f, fi.i); //Prints: 10.000000 : 1092616192 which is OK
printf("%u\r\n", *i_ptr); //Prints: 1092616192 which is also OK
return 0;
}
If we check the memory representation, then they 4-bytes
are long, so there is no memory overflow in a pointer or similar.
How is this undefined behavior?
int main() {
union float_int fi;
void* v_ptr = &fi.f;
uint32_t* i_ptr = (uint32_t *)v_ptr;
}
Is this code still undefined behavior? I want to read the number float
like unsigned integer 32-bits
.
Why is the memcpy
only available way to do this used?
source to share
This is not a strict alias, it is a violation of strict anti-aliasing.
First you do
uint32_t* i_ptr = (uint32_t *)&fi.f; //converting to a non-character type pointer
and then you try to access that with
printf("%u\r\n", *i_ptr); //access value via incompatible lvalue expr.
which is causing the problem. float
and uint32_t
are not compatible types.
Quote C11
, chapter §6.5 / P7
The object must have a stored value that can only be accessed by an lvalue expression that is one of the following types: 88)
- a type compatible with the effective type of the object,
- a qualified version of the type, compatible with the effective type of the object,
- a type that is a signed or unsigned type corresponding to the effective type Object,
- a type that is a signed or unsigned type corresponding to the qualified version of the effective object type,
- a collection or type of union that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregation or union), or
- the type of symbol.
In response to the comment, see C11
chapter §6.2.6.1
The values stored in objects without a bit field of any other type of object consist of
n × CHAR_BIT
bits, wheren
is the size of an object of that type, in bytes. The value can be copied to an object of typeunsigned char [n]
(for examplememcpy
); the resulting set of bytes called the value object representation.
and
Certain object representations do not have to represent the value of the object type. If the stored object value has such a representation and is read by an lvalue expression that does not have a character, the behavior is undefined. [...] This view is called a trap view.
source to share
Fields and integer representations are different from each other. This is why defining such a union is not a good use case.
In your example, you are casting from void * to uint32_t *. Casting is done at the pointer level. This means that i_ptr points to a memory location considered as an interconnection without changing the bits themselves.
To summarize, if you want this casting to work, you need to change the internal representation of the variable. For example:
printf("%u\r\n", (uint32_t)*i_ptr);
source to share