【问题标题】:C++ Struct to Byte* throwing errorC++ Struct to Byte* 抛出错误
【发布时间】:2018-04-21 11:02:36
【问题描述】:

我在下面附上了我的代码。我没有看到我做错了什么。我有一个试图序列化为字节数组的结构。我写了一些简单的代码来测试它。当我打印出对象的值时,这一切似乎都在运行时工作,但是一旦我点击 return 0 它就会抛出错误:

运行时检查失败 #2 - 变量“command”周围的堆栈已损坏。

我没有看到问题。感谢所有帮助。

namespace CommIO
{
    enum Direction {READ, WRITE};

    struct CommCommand
    {
        int command;
        Direction dir;
        int rwSize;
        BYTE* wData;

         CommCommand(BYTE* bytes)
        {
            int offset = 0;
            int intsize = sizeof(int);

            command = 0;
            dir = READ;
            rwSize = 0;

            memcpy(&command, bytes + offset, intsize);
            offset += intsize;
            memcpy(&dir, bytes + offset, intsize);
            offset += intsize;
            memcpy(&rwSize, bytes + offset, intsize);
            offset += intsize;

            wData = new BYTE[rwSize];
            if (dir == WRITE)
            {   
                memcpy(&wData, bytes + offset, rwSize);
            }
        }

        CommCommand() {}
    }

int main()
{
    CommIO::CommCommand command;
    command.command = 0x6AEA6BEB;
    command.dir = CommIO::WRITE;
    command.rwSize = 128;
    command.wData = new BYTE[command.rwSize];
    for (int i = 0; i < command.rwSize; i++)
    {
        command.wData[i] = i;
    }

    command.print();

    CommIO::CommCommand command2(reinterpret_cast<BYTE*>(&command));
    command2.print();
    cin.get();
    return 0;
}

【问题讨论】:

  • 您似乎假设Direction 的大小与int 的大小相同。可能确实如此,但 C++ 并不能保证。
  • 您似乎还假设CommIO::CommCommand 的成员将被布置在内存中而没有任何填充,这可能又会发生这种情况,但不能保证。
  • 想想wData&amp;wData的区别。你刚才分配的缓冲区地址是哪一个?
  • 在互联网上搜索“C++ 序列化”。您的过程称为“序列化”。
  • @JohnBollinger #pragma pack() 指令会对此有所帮助吗?

标签: c++ serialization struct


【解决方案1】:

memcpy(&wData, bytes + offset, rwSize);

您从 wData 指针的位置复制到新 CommCommand 的 wData 指针的位置。但是您想从指针指向的位置复制到指针指向的位置。你需要取消引用。您破坏了堆,因为您只有 sizeof(BYTE*) 空间(加上一些额外的空间,因为堆块不能任意小),但是您复制了 rwSize 字节,即 128 字节。您可能打算写的是:

memcpy(wData, *(BYTE*)(bytes + offset), rwSize);

这将使用存储在字节+偏移量的指针,而不是字节+偏移量本身的值。

您还假设您的结构紧凑。但是,C++ 不保证这一点。有没有理由不重写默认的复制构造函数而不是编写这个函数?

【讨论】:

  • 在您开始取消引用之前,您一直走在正确的轨道上。
  • @molbdnilo 我看不出我的错误。你能解释一下吗?
【解决方案2】:

结构的内存布局没有填充,这可以通过在结构的开头添加宏 #pragma pack(1) 和在结构的末尾添加 #pragma pop() 来纠正 - 不过请检查它的语法。

对于你的结构到字节的转换,我会使用一些简单的东西:

template<typename T, typename IteratorForBytes>
void ConvertToBytes(const T& t, IteratorForBytes bytes, std::size_t pos = 0)
{
    std::advance(bytes, pos);
    const std::size_t length = sizeof(t);
    const uint8_t* temp = reinterpret_cast<const uint8_t*>(&t);
    for (std::size_t i = 0; i < length; ++i)
    {
        (*bytes) = (*temp);
        ++temp; 
        ++bytes;
    }
}

T 是结构,在您的情况下,Command 结构和bytes 将是数组。

CommIO::CommCommand command;
command.wData = new BYTE[command.rwSize];
ConvertToBytes(command, command.wData); 

结果数组将包含预期的bytes 如果您想从特定位置开始填充字节数组,您可以指定偏移量以及额外参数

【讨论】:

    【解决方案3】:

    主要问题在这里:

    memcpy(&wData, bytes + offset, rwSize);
    

    成员wDataBYTE *,您的意思似乎是将字节复制到它指向的空间中。相反,您将数据复制到存储指针值本身的内存中。因此,如果您复制的字节数超过了指针的大小,那么您将超出其界限并产生未定义的行为。在任何情况下,您都在破坏原始指针值。你可能想要这个,而不是:

    memcpy(wData, bytes + offset, rwSize);
    

    此外,尽管其余的反序列化代码可能适合您的实际序列化格式,但假设它适合您在测试程序中提供给它的字节序列是不安全的

    CommIO::CommCommand command2(reinterpret_cast<BYTE*>(&command));
    

    如 cmets 中所述,您正在对 CommIO::CommCommand 的内存布局做出 C++ 不保证会成立的假设。

    【讨论】:

      【解决方案4】:

      cmets中提到的以下几点最有可能是您的问题的原因。

      1. 您似乎假设Direction 的大小与int 的大小相同。可能确实是这样,但 C++ 不保证。

      2. 您似乎还假设CommIO::CommCommand 的成员将被布置在内存中而没有任何填充,这可能再次发生,但不能保证。

        李>

      有几种方法可以解决这个问题。

      1. 确保用匹配的对象填充调用函数中的BYTE 数组,或者

      2. 只需将BYTE* 转换为CommCommand* 并直接访问成员。

      对于(1),您可以使用:

      int command = 0x6AEA6BEB;
      int dir = CommIO::WRITE;
      int rwSize = 128;
      
      totatlSize = rwSize + 3*sizeof(int);
      BYTE* data = new BYTE[totalSize];
      
      int offset = 0;
      memcpy(data + offset, &comand, sizeof(int));
      offset += sizeof(int);
      memcpy(data + offset, &dir, sizeof(int));
      offset += sizeof(int);
      memcpy(data + offset, &rwSize, sizeof(int));
      offset += sizeof(int);
      
      for (int i = 0; i < rwSize; i++)
      {
         data[i + offset] = i;
      }
      
      CommIO::CommCommand command2(data);
      

      对于(2),您可以使用:

      CommCommand(BYTE* bytes)
      {
         CommCommand* in = reinterpret_cast<CommCommand*>(bytes);
      
         command = in->command;
         dir = in->dir;
         rwSize = in->size;
      
         wData = new BYTE[rwSize];
         if (dir == WRITE)
         {   
            memcpy(wData, in->wData, rwSize);
         }
      }
      

      另一个错误是你正在使用

      memcpy(&wData, bytes + offset, rwSize);
      

      这是不正确的,因为您将变量的地址视为可以保存数据。它不能。

      你需要使用:

      memcpy(wData, bytes + offset, rwSize);
      

      【讨论】:

        猜你喜欢
        • 2019-02-17
        • 2012-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-22
        • 1970-01-01
        • 1970-01-01
        • 2021-03-12
        相关资源
        最近更新 更多