cpointersmisrapointer-conversion

MISRA C:2012 11.3 violation casting (float *) to (uint32_t *)


I have the following code from a serial message encoder:

uint32_t bits;
*((float *) &bits) = some_external_variable;
pFrame->data[0x2C] = ((uint8_t)(bits >> 0x18) & 0xFFu);
pFrame->data[0x2D] = ((uint8_t)(bits >> 0x10) & 0xFFu);
pFrame->data[0x2E] = ((uint8_t)(bits >> 0x08) & 0xFFu);
pFrame->data[0x2F] = ((uint8_t)(bits >> 0x00) & 0xFFu);

I am trying to put the float value onto the bytestream in an endianness independent way. To do that, I (think I) need to access the bytes and put them onto the stream individually.

The byte mapping itself appears MISRA-C compliant, but I cannot figure out any way to serialize the float in a compliant way without triggering rule 11.3 using the cppcheck tool.

How could I encode the float in a compliant way?

Both data types should have the same size, so alignment should not be an issue here?

UPDATE:

Thanks for the answers and especially the clarification for the hidden pitfalls. I went with this solution in the end:

uint32_t bits;
(void)memcpy(&bits, some_external_variable, sizeof(bits));
pFrame->data[0x2C] |= ((uint8_t)(bits >> 0x00) & 0xFFu);
pFrame->data[0x2D] |= ((uint8_t)(bits >> 0x08) & 0xFFu);
pFrame->data[0x2E] |= ((uint8_t)(bits >> 0x10) & 0xFFu);
pFrame->data[0x2F] |= ((uint8_t)(bits >> 0x18) & 0xFFu);

Disclaimer: this code is the output of my code generator, and from that context I know in advance that the sizes of the variables match. The extensive shifts and masks look like no-op here, but there are cases with misaligned containers where the masks will play a role.


Solution

  • Casting a pointer to one type to a pointer to different type and dereferencing (except in a few special cases) is undefined behavior.

    One case where it is well defined is if you cast to a character type which lets you access the bytes of the variable's representation:

    unsigned char *bits = (unsigned char *)some_external_variable;
    pFrame->data[0x2C] = bits[3];
    pFrame->data[0x2D] = bits[2];
    pFrame->data[0x2E] = bits[1];
    pFrame->data[0x2F] = bits[0];
    

    Or you can just use memcpy:

    memcpy(&pFrame->data[0x2C], some_external_variable, 
           sizeof some_external_variable);
    

    As you mentioned endian-independent, you can first memcpy to uint32_t, then do the shifts as you were before:

    uint32_t bits;
    memcpy(&bits, some_external_variable, sizeof bits);
    pFrame->data[0x2C] = ((uint8_t)(bits >> 0x18) & 0xFFu);
    pFrame->data[0x2D] = ((uint8_t)(bits >> 0x10) & 0xFFu);
    pFrame->data[0x2E] = ((uint8_t)(bits >> 0x08) & 0xFFu);
    pFrame->data[0x2F] = ((uint8_t)(bits >> 0x00) & 0xFFu);
    

    This assumes that a float and a uint32_t use the same endianness, although I'm not aware of any implementation where they might be different.