warning: memcpy forming offset [X, Y] is out of the bounds [0, 2] of object Z

2.9k Views Asked by At

I'm trying to assemble information in a struct to later memcopy it to an spi buffer. This is the code I'm working with:

Demo

#include <cstdio>
#include <cstdint>
#include <cstring> /* memcpy */

using data_settings = uint8_t;

enum class channel_settings : uint8_t
{
    power_down_setting = 0,
    DA1,
    DA2,
    DA3,
    DA4,
    DA5,
    DA6,
    DA7,
    DA8,
    power_down_release,
    NA1,
    NA2,
    io_da_select,
    io_serial_parallel,
    io_parallel_serial,
    io_status_setting,
};

struct message
{
    data_settings data;
    channel_settings channel;
};

int main()
{
    message m = { 255, channel_settings::DA5 };

    uint32_t spi_buf;
    std::memcpy(&spi_buf, &m, 4);

    printf("sizeof m = %zu, spi_buf = %#010x\n", sizeof(message), spi_buf);
}

But gcc 12 x86 spits out the following warning:

<source>:38:16: warning: 'void* memcpy(void*, const void*, size_t)' forming offset [2, 3] is out of the bounds [0, 2] of object 'm' with type 'message' [-Warray-bounds=]
   38 |     std::memcpy(&spi_buf, &m, 4);
      |     ~~~~~~~~~~~^~~~~~~~~~~~~~~~~
<source>:35:13: note: 'm' declared here
   35 |     message m = { 255, channel_settings::DA5 };
      |             ^

What is the issue here? Are there alignement concerns as my struct is only 2 bytes?

The output is as follows:

sizeof m = 2, spi_buf = 0x000005ff

And it seems that the data settings will occupy the less significant byte which is actually what I'm looking for. However, what happens if the system is big endian? How can I write my struct endian-independent so that memcopied data will always reflect the same bit ordering?

1

There are 1 best solutions below

0
On

When it comes to your alignment, though they are unaligned it isn't necessarily the issue but that you are trying to copy more bytes than your struct has in size. Personally I would change spi_buf to a uint16_t and change memcpy to 2 bytes. Another option is that you could just add padding bytes to your struct. For example:

struct message
{
    data_settings data;
    channel_settings channel;

    uint8_t padding[2];
};

Technically a single uint16_t would also do just fine, and if your compiler has alignment flags that may also work but only because forcing alignment would cause it to pad the 2 extra bytes in this case.

As for endianness, that should be known at compile time. C++20 provides std::endian if that's an option. Otherwise you may have to hardcode some constants per platform if none are already available via macros or other means.

If for some reason that isn't possible you could try to detect endianness programmatically but prefer the former if possible.