Search code examples
c++c++11g++gcc-warning

Strict aliasing warning, creating uint32_t reference to unsigned char array + offset


With GNU GCC 4.7.0+ I got a few strict aliasing warnings, which I would like to resolve.

I have a payload (from hardware):

unsigned char payload[davidlt::PAYLOAD_SIZE];

I had this line:

*(uint32_t*)(payload + davidlt::DATA_OFFSET) = (pid & davidlt::PID_MASK) << davidlt::PID_SHIFT;

This creates a pointer to a specific location in payload and 4 bytes are interpreted as uint32_t. A new value uint32_t type is calculated and replaced in the payload.

I get:

warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

I was hoping to solve it by using reinterpret_cast, but I get the same warning.

*reinterpret_cast<uint32_t *>(payload + davidlt::DATA_OFFSET) = (pid & davidlt::PID_MASK) << davidlt::PID_SHIFT;

As I understood you can convert whatever data to char or unsigned char, which is allowed, but that works only one way.

One solution would be to make a union. Aren't were any other way to create a reference of different type to unsigned char data?

Thanks! david


Solution

  • Yes, viewing data as char or unsigned char is allowed, but not the reverse.

    Instead you should use memcpy in this case. Your line takes a pid value, masks it, shifts it, and then inserts it into the payload. A direct translation of this would be:

    unsigned char payload[davidlt::PAYLOAD_SIZE];
    
    uint32_t payload_pid = (pid & davidlt::PID_MASK) << davidlt::PID_SHIFT;
    
    std::memcpy(payload + davidlt::DATA_OFFSET, &payload_pid, sizeof payload_pid);
    

    Another alternative would be to create your payload as a standard layout type with the appropriate size and members, and then to view it as an unsigned char array. Assuming you're in control of creating the payload:

    struct Payload {
        ...
        uint32_t pid;
        ...
    } payload;
    
    payload.pid = (pid & davidlt::PID_MASK) << davidlt::PID_SHIFT;
    
    static_assert(davidlt::PAYLOAD_SIZE == sizeof(Payload), "");
    
    unsigned char (&payload_as_char)[davidlt::PAYLOAD_SIZE] = reinterpret_cast<unsigned char (&)[davidlt::PAYLOAD_SIZE]>(&payload);
    

    This isn't violating the strict aliasing rule because it's going the right direction now.