将数据从结构二进制复制到 std::vector
Posted
技术标签:
【中文标题】将数据从结构二进制复制到 std::vector【英文标题】:binary copy data from struct to std::vector 【发布时间】:2018-12-25 21:44:22 【问题描述】:我正在使用一个可以从内存中加载 BMP 图像的库。
我有一个代表 BMP 的类。
要从内存中加载,我必须提供一个指向内存中一些 BMP 格式数据的指针和一个用于该数据大小的变量。 (void* data, size_t length)
我想将我的数据存储在std::vector
中。 (避免手动内存管理)
我试图编写一个函数来返回一个std::vector<unsigned char>
,但我认为我得到的不是很好。
std::vector<unsigned char> BMP::BITMAP::SaveMem() const
// memory storage
std::vector<unsigned char> memory;
BITMAPFILEHEADER f_head;
f_head.bfType = ushort_rev(((WORD)'B' << 0x08) | ((WORD)'M' << 0x00));
f_head.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_width_memory * m_height;
f_head.bfReserved1 = 0;
f_head.bfReserved2 = 0;
f_head.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// build standard bitmap file header
BITMAPINFOHEADER i_head;
i_head.biSize = sizeof(BITMAPINFOHEADER);
i_head.biWidth = m_width;
i_head.biHeight = m_height;
i_head.biPlanes = 1;
i_head.biBitCount = m_bit_count;
i_head.biCompression = 0;
i_head.biSizeImage = m_width_memory * m_height;
i_head.biXPelsPerMeter = 0;
i_head.biYPelsPerMeter = 0;
i_head.biClrUsed = 0;
i_head.biClrImportant = 0;
// alloc
memory.resize(f_head.bfSize);
std::copy(&f_head, &f_head + sizeof(f_head), memory.at(0));
std::copy(&i_head, &i_head + sizeof(i_head), memory.at(0) + sizeof(f_head));
// write data
for(unsigned int y = 0; y < m_height; ++ y)
std::copy(&m_data[y * m_width_memory], m_data[y * m_width_memory + 3 * m_size_x], memory.at(0) + sizeof(f_head) + sizeof(i_head));
显然这不能编译。我想不出std::copy
的任何替代方案。这真的是适合这项工作的工具吗?
为了让它编译,我认为我应该将memory.at(x)
更改为memory.data() + x
...这样做我将使用原始指针——这就是为什么我认为std::copy
不会比memcpy
更好.
我能对此提出一些建议吗?这有点不合逻辑,如果我早先知道这个要求,我会将我的像素数据存储在unsigned char
中,位图文件标题在数据之前。可惜现在改设计需要做很多工作,所以我宁愿不去碰它。
【问题讨论】:
改变设计有那么可怕吗?由于这种错误的设计,修复错误需要多长时间? @JVApen 这并不理想,可能会引入更多错误,因为有大量代码依赖于在二进制级别操作 BMP 数据 【参考方案1】:三个问题:
您想要复制 字节,但 std::copy
函数提供了一个指向 BITMAPFILEHEADER
(或 BITMAPINFOHEADER
)结构的指针。您需要将指针转换为 bytes,例如 reinterpret_cast<uint8_t*>(&f_head)
。
前面导致了数据结尾的其他问题,表达式&f_head + sizeof(f_head)
实际上等于(&f_head)[sizeof(f_head)]
,并且远远超出了结构的结尾。您还需要在此处使用字节,如 reinterpret_cast<uint8_t*>(&f_head) + sizeof f_head
。
最后一个问题是std::copy
调用的目的地,因为它需要是与源类似的类型,即指向uint8_t
的指针(在我的演员表中)。你可以很容易地做到这一点,例如&memory[0]
。第二个电话&memory[sizeof f_head]
。
【讨论】:
他使用的是unsigned char
而不是uint8_t
。
另外,我确信很多代码可能都假设了这一点,但是否可以完全依赖结构的每个字段将直接从前一个字段继承而没有填充?我们能否确定sizeof(i_head)
不会包含填充?
@Galik IIRC 这些结构的设计和布局是为了避免填充,因为它们(并且一直)用于从文件读取和写入原始二进制文件。
@Galik 好问题:结构实际上有:__attribute__((packed))
std::cooy 的源和目标不需要相似,它们都需要是迭代器。以上是关于将数据从结构二进制复制到 std::vector的主要内容,如果未能解决你的问题,请参考以下文章
将元素从 std::vector 复制到 std::stack c++
将 Cv::Mat 的数据存储在 std::vector 中