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

huangapple go评论99阅读模式
英文:

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

问题

我正在尝试将信息组装到一个结构体中,然后将其复制到一个spi缓冲区中。这是我正在使用的代码:

[演示][1]

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

但是gcc 12 x86会输出以下警告

    <source>:38:16: 警告:‘void* memcpy(void*, const void*, size_t)’形成的偏移量[2, 3]超出了对象‘m’的边界[0, 2],该对象的类型为‘message [-Warray-bounds=]
       38 |     std::memcpy(&spi_buf, &m, 4);
          |     ~~~~~~~~~~~^~~~~~~~~~~~~~~~~
    <source>:35:13: 注意:此处声明‘m
       35 |     message m = { 255, channel_settings::DA5 };
          |             ^


问题出在哪里?因为我的结构体只有2个字节,是否存在对齐问题?

输出结果如下:

    sizeof m = 2, spi_buf = 0x000005ff

看起来数据设置将占据较低的字节,这实际上是我想要的。但是,如果系统是大端字节序会发生什么?我如何编写我的结构体,使得复制的数据始终保持相同的位顺序,与字节序无关?

[1]: https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:18,endLineNumber:21,positionColumn:18,positionLineNumber:21,selectionStartColumn:18,selectionStartLineNumber:21,startColumn:18,startLineNumber:21),source:'%23include+%3Ccstdio%3E%0A%23include+%3Ccstdint%3E%0A%23include+%3Ccstring%3E+/*+memcpy+*/%0A%0Ausing+data_settings+%3D+uint8_t%3B%0A%0Aenum+class+channel_settings+:+uint8_t%0A%7B%0A++++power_down_setting+%3D+0,%0A++++DA1,%0A++++DA2,%0A++++DA3,%0A++++DA4,%0A++++DA5,%0A++++DA6,%0A++++DA7,%0A++++DA8,%0A++++power_down_release,%0A++++NA1,%0A++++NA2,%0A++++io_da_select,%0A++++io_serial_parallel,%0A++++io_parallel_serial,%0A++++io_status_setting,%0A%7D%3B%0A%0Astruct+message%0A%7B%0A++++data_settings+data%3B%0A++++channel_settings+channel%3B%0A%7D%3B%0A%0Aint+main()%0A%7B%0A++++message+m+%3D+%7B+255,+channel_settings::DA5+%7D%3B%0A%0A++++uint32_t+spi_buf%3B%0A++++std::memcpy(%26spi_buf,+%26m,+4)%3B%0A%0A++++printf(%22sizeof+m+%3D+%25zu,+spi_buf+%3D+%25%23010x%5Cn%22,+sizeof(message),+spi_buf)%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0'})),k:50,l:'4',n:'0',o:'',s:0,t:'0'}),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,deviceViewOpen:'1',filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-std%3Dc%2B%2B20+-Wall+-Os+-pthread',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+gcc+(trunk)+(Editor+%231)',t:'0'})),k:50,l:'4',m:50,n:'0',o:'',s:0,t:'0'}),(g:!((h:output,i:(compilerName:'x86-64+gcc+12.2',editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+(trunk)+(Compiler+%231)',t:'0'})),header:(),l:'4',m:50,n:'0',o:'',s:0,t:'0'})),k:50,l:'3',n:'0',o:'',t:'0'})),l:'2',n:'0',o:'',t:'0'})
英文:

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 &lt;cstdio&gt;
#include &lt;cstdint&gt;
#include &lt;cstring&gt; /* 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(&amp;spi_buf, &amp;m, 4);
printf(&quot;sizeof m = %zu, spi_buf = %#010x\n&quot;, sizeof(message), spi_buf);
}

But gcc 12 x86 spits out the following warning:

&lt;source&gt;:38:16: warning: &#39;void* memcpy(void*, const void*, size_t)&#39; forming offset [2, 3] is out of the bounds [0, 2] of object &#39;m&#39; with type &#39;message&#39; [-Warray-bounds=]
38 |     std::memcpy(&amp;spi_buf, &amp;m, 4);
|     ~~~~~~~~~~~^~~~~~~~~~~~~~~~~
&lt;source&gt;:35:13: note: &#39;m&#39; 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

得分: 1

关于对齐问题,虽然它们是不对齐的,但问题不一定出在这里,而是您试图复制的字节数超过了您的结构体的大小。个人建议将 spi_buf 更改为 uint16_t 并将 memcpy 更改为 2 字节。另一个选项是您可以向您的结构体添加填充字节。例如:

struct message
{
    data_settings data;
    channel_settings channel;

    uint8_t padding[2];
};

从技术上讲,一个单独的 uint16_t 也可以胜任,如果您的编译器支持对齐标志,也可以工作,但仅因为强制对齐会导致在这种情况下填充2个额外字节。

至于字节顺序,这应该在编译时已知。C++20 提供 std::endian(如果可行的话)。否则,如果没有通过宏或其他方式已经提供,您可能需要在每个平台上硬编码一些常量。

如果出于某种原因这不可行,您可以尝试以编程方式检测字节顺序,但如果可能的话最好选择前者。

英文:

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.

huangapple
  • 本文由 发表于 2023年3月7日 01:26:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75653971.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定