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?

+3


source to share


2 answers


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, where n

is the size of an object of that type, in bytes. The value can be copied to an object of type unsigned char [n]

(for example memcpy

); 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.

+1


source


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);

      

0


source







All Articles