【问题标题】:Using a data buffer without violating strict aliasing在不违反严格别名的情况下使用数据缓冲区
【发布时间】:2015-09-25 14:58:16
【问题描述】:

我想重写一段(旧)代码以符合标准。旧代码使用缓冲区来存储 POD 结构和校验和以通过网络发送和接收它。对于发送,代码如下:

struct MessageStruct {int a; float b;};

char buffer[sizeof(MessageStruct) + sizeof(uint32_t)];
((MessageStruct*)buffer)->a = 12;
((MessageStruct*)buffer)->b = 3.14159f;
*((uint32_t*)(buffer + sizeof(MessageStruct))) = 9876;

// Use the data buffer in some way.
SendMessage(buffer, sizeof(buffer));

对于接收,代码如下:

struct MessageStruct {int a; float b;};

// Receive: char *buffer, int size
const MessageStruct *message = (MessageStruct*)buffer;
uint32_t checksum = *((uint32_t*)(buffer + sizeof(MessageStruct)));

如何更新此代码以使其完全符合标准,尤其是不违反严格的别名规则?

我找到了解决类似问题的帖子:strict aliasing and alignmentShared memory buffers in C++ without violating strict aliasing rules。但是,这些都没有真正回答我的问题;或者也许他们有,但我没有看到。

更新:正如一些答案已经说明的那样,最简单的方法是使用memcpy。我想知道,有没有办法使用placement new 或其他不需要复制并就地构造它的构造来做到这一点?

【问题讨论】:

  • IIANM,语言律师标签主要用于正式的标准问题,而不是实际问题。
  • @jrok 嗯,好点子。我会删除它。
  • 顺便说一句,你可能有字节序问题
  • @Jarod42 是的,确实如此,但幸运的是,现在这不是问题。

标签: c++


【解决方案1】:

在您的情况下,避免违反严格别名规则的最简单方法是将 memcpy() 内容放入缓冲区。

【讨论】:

  • 感谢您的快速答复。我也在考虑这个解决方案,但我也/希望有一个避免复制的解决方案。
  • 没有标准的祝福方式。有些人会建议基于联合的解决方案,但这在技术上仍然是 UB - 虽然不是可能会打击你的那个。
【解决方案2】:

使用memcpy:

MessageStruct msg;
msg.a = 12;
msg.b = 3.14;
uint32_t n = 9876;

memcpy(buffer, &msg, sizeof(msg));
memcpy(buffer + sizeof(msg), &n, sizeof(n));

【讨论】:

    【解决方案3】:

    首先,您将结构的内存表示发送到另一台机器的方法是有缺陷的,因为另一台机器的布局甚至大小可能与您的不同。使用正确定义的元格式,如 JSON、XML 或类似的东西。

    现在,如果您不关心这些缺陷,您可以采取自己的方法更进一步:

    struct MessageStructWithChecksum:
        MessageStruct
    {
        uint32_t checksum;
    };
    

    除了struct 和原始字节之间的转换,没有别名问题。另请注意,您可以定义函数局部结构,并且可以编写用于不同消息类型的模板。

    【讨论】:

      【解决方案4】:

      发送部分最简单:

      struct MessageStruct {int a; float b;};
      struct MessageStructWithCheckSum { MessageStruct s; uint32_t check_sum;};
      
      MessageStructWithCheckSum m;
      m.s.a = 12;
      m.s.b = 3.14159f;
      m.check_sum = 9876;
      
      // Use the data buffer in some way.
      SendMessage(reinterpret_cast<const unsigned char*>(m), sizeof(m));
      

      您可以通过 [..] char 或 unsigned char 类型访问对象的存储值。

      对于读取部分,我认为你必须复制以避免严格的别名规则。

      但实际上,您必须进行特殊处理来管理字节顺序(以及具有不同表示形式的类型),这无论如何都是一种复制。

      【讨论】:

        猜你喜欢
        • 2015-11-06
        • 1970-01-01
        • 2018-04-08
        • 1970-01-01
        • 2021-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-16
        相关资源
        最近更新 更多