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:
#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?
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 auint16_t
and change memcpy to 2 bytes. Another option is that you could just add padding bytes to your struct. For example: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.