【问题标题】:What's the proper way to read/populate data buffers using message structs in c++?在 C++ 中使用消息结构读取/填充数据缓冲区的正确方法是什么?
【发布时间】:2014-11-12 01:15:12
【问题描述】:

我有一个处理 char[] 缓冲区以发送/接收消息的程序。到目前为止,它是这样处理的:

#pramga pack(1)
struct messageType
{
    uint8_t data0:4;
    uint8_t data1:4;
    uint8_t data2;
    //etc...
};
#pragma pack()

void MyClass::processMessage(char* buf)
{
    // I already know buf is big enough to hold a messageType
    messageType* msg = reinterpret_cast<messageType*>(buf);

    //populate class member variables
    m_data0 = msg->data0;
    m_data1 = msg->data1;
    m_data2 = msg->data2;
    //etc  
}

现在从我从阅读中收集到的信息是,由于严格的别名,这在技术上是未定义的行为,应该使用 memcpy 代替吗?我不太明白的是,将buf逐字节复制到messageType msgNotPtr,然后从msgNotPtr读取,实际上会避免什么潜在问题?

关于发送,而不是这样做:

void MyClass::sendMessage()
{
    char buf[max_tx_size];
    messageType* msg = reinterpret_cast<messageType*>(buf);

    msg->data0 = m_data0;
    //etc...
    send(buf);
}

我读到我应该使用placement new istead,ala:

messageType* msg = new(buf) messageType;

如果我这样做,我是否需要添加额外的清理,因为结构 messageType 仅包含 POD 类型(例如手动触发析构函数)?

编辑:现在我想一想,sendMessage 仍然未定义吗?我是否还需要将最后一个命令替换为 send(reinterpret_cast&lt;char*&gt;(msg)) 之类的内容,以确保编译器不会优化调用?

【问题讨论】:

  • 鉴于您提供的代码,我想说您不需要手动调用析构函数,因为buf 是在堆栈上分配的。您正在处理 POD,因此调用析构函数的唯一原因是释放分配的内存,而这里不需要。
  • @bialpio 与缓冲区是否在堆栈上无关。不知道在POD中使用placement new时是否需要调用析构函数。
  • 在 POD 的情况下,没有理由调用析构函数 - 没有什么要清理的(例如:没有要关闭的句柄,没有指向空闲的指针等),内存将是函数返回时释放,因为它是在堆栈上分配的。另一方面:char * buf = new char[sizeof(messageType)]; 需要释放内存,我希望调用析构函数会释放它。
  • @bialpio 在这种情况下,你会使用delete[] buf,不是吗?我认为这不会改变手动调用析构函数的任何内容。

标签: c++


【解决方案1】:

在我看来,从 uint8_t 缓冲区加载类实例的正确方法是分别加载每个成员。

该类应该知道其成员在缓冲区中的位置以及任何填充或保留区域的位置和大小。

将结构映射到缓冲区的问题之一是编译器可以在成员之间添加空间。如果您打包结构以消除填充,则每次访问成员时都会减慢程序的速度。

因此,通过将成员放置在缓冲区中所需的位置(或根据规范提取成员)来降低输入和输出的性能。程序的其余部分可以按照编译器对齐成员的方式访问成员。

这包括位域。

此外,通过单独加载成员,可以更轻松地解决缓冲区中字段的字节顺序。

【讨论】:

  • 我的问题或多或少是关于如何根据规范从缓冲区中提取成员。我觉得强制转换为一个打包的结构来做到这一点比我必须做的等效按位操作来获得偏移 2、4 等的成员要简单得多。
  • 我想我应该注意我的类成员没有被打包,只有中间的messageType 是,它用于填充我的类变量。
  • 那么,您要实现一个与规范匹配的打包结构和另一个填充并供程序其余部分使用的结构?
  • 是的,差不多。这已经实现了,但是我今天读到的关于严格别名的内容让我相信我当前的实现可能是未定义的。我正在尝试弄清楚这是否属实,以及解决问题的最佳方法。
猜你喜欢
  • 2019-03-26
  • 2012-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多