c++ - Blob 内数据的对齐和填充

标签 c++ visual-c++

我正在使用一个大的 blob(分配的内存)在内存中连续存储数据。

我希望像这样组织 blob 中的数据:

| data1 type | data1 | data2 type | data2 | dataN type | dataN |

dataN 类型 是一个 int,我在开关中使用它来将 dataN 转换为适当的类型。

问题是我想保持数据正确对齐,所以我想强制 blob 中的所有数据都打包为 8 字节(我选择 8 字节进行打包,因为它可能会保持数据正确对齐?),这个数据将紧密打包的方式(由于对齐,数据->数据类型之间不会有空洞)。

我试过这个:

#pragma pack(8)
class A
{
public:
    short b;
    int x;
    char v;
};

但它不起作用,因为使用 sizeof(A) 我得到了 12 个字节而不是预期的 16 个字节。

P.S:在 x86 或 x64 架构中是否存在大于 8 字节的数据类型?

最佳答案

这个答案假设了两件事:

  1. 您希望二进制 blob 紧密打包(无孔洞)。
  2. 您不希望以未对齐的方式访问数据成员(与访问按编译器默认方式对齐的数据成员相比速度较慢)。

如果是这种情况,那么您应该考虑将大型“blob”视为面向字节的流的设计。在此流中,您编码/解码标记/值对,这些标记/值对填充具有自然对齐的对象。

通过此方案,您可以两全其美。您会得到一个紧凑的 blob,但是一旦您从 blob 中提取对象,由于自然对齐,访问对象成员会很快。它也是可移植的1 并且不依赖于编译器扩展。缺点是您需要为可以放入 blob 中的每种类型编写样板代码。

基本示例:

#include <cassert>
#include <iomanip>
#include <iostream>
#include <stdint.h>
#include <vector>

enum BlobKey
{
    kBlobKey_Widget,
    kBlobKey_Gadget
};

class Blob
{
public:
    Blob() : cursor_(0) {}

    // Extract a value from the blob. The key associated with this value should
    // already have been extracted.
    template <typename T>
    Blob& operator>>(T& value)
    {
        assert(cursor_ < bytes_.size());
        char* dest = reinterpret_cast<char*>(&value);
        for (size_t i=0; i<sizeof(T); ++i)
            dest[i] = bytes_[cursor_++];
        return *this;
    }

    // Insert a value into the blob
    template <typename T>
    Blob& operator<<(const T& value)
    {
        const char* src = reinterpret_cast<const char*>(&value);
        for (size_t i=0; i<sizeof(T); ++i)
            bytes_.push_back(src[i]);
        return *this;
    }

    // Overloads of << and >> for std::string might be useful

    bool atEnd() const {return cursor_ >= bytes_.size();}

    void rewind() {cursor_ = 0;}

    void clear() {bytes_.clear(); rewind();}

    void print() const
    {
        using namespace std;
        for (size_t i=0; i<bytes_.size(); ++i)
            cout << setfill('0') << setw(2) << hex << int(bytes_[i]) << " ";
        std::cout << "\n" << dec << bytes_.size() << " bytes\n";
    }

private:
    std::vector<uint8_t> bytes_;
    size_t cursor_;
};

class Widget
{
public:
    explicit Widget(int a=0, short b=0, char c=0) : a_(a), b_(b), c_(c) {}
    void print() const
    {
        std::cout << "Widget: a_=" << a_ << " b=" << b_
                  << " c_=" << c_ << "\n";
    }
private:
    int a_;
    short b_;
    long c_;
    friend Blob& operator>>(Blob& blob, Widget& widget)
    {
        // Demarshall members from blob
        blob >> widget.a_;
        blob >> widget.b_;
        blob >> widget.c_;
        return blob;
    };
    friend Blob& operator<<(Blob& blob, Widget& widget)
    {
        // Marshall members to blob
        blob << kBlobKey_Widget;
        blob << widget.a_;
        blob << widget.b_;
        blob << widget.c_;
        return blob;
    };
};

class Gadget
{
public:
    explicit Gadget(long a=0, char b=0, short c=0) : a_(a), b_(b), c_(c) {}
    void print() const
    {
        std::cout << "Gadget: a_=" << a_ << " b=" << b_
                  << " c_=" << c_ << "\n";
    }
private:
    long a_;
    int b_;
    short c_;
    friend Blob& operator>>(Blob& blob, Gadget& gadget)
    {
        // Demarshall members from blob
        blob >> gadget.a_;
        blob >> gadget.b_;
        blob >> gadget.c_;
        return blob;
    };
    friend Blob& operator<<(Blob& blob, Gadget& gadget)
    {
        // Marshall members to blob
        blob << kBlobKey_Gadget;
        blob << gadget.a_;
        blob << gadget.b_;
        blob << gadget.c_;
        return blob;
    };
};

int main()
{
    Widget w1(1,2,3), w2(4,5,6);
    Gadget g1(7,8,9), g2(10,11,12);

    // Fill blob with widgets and gadgets
    Blob blob;
    blob << w1 << g1 << w2 << g2;
    blob.print();

    // Retrieve widgets and gadgets from blob
    BlobKey key;
    while (!blob.atEnd())
    {
        blob >> key;
        switch (key)
        {
            case kBlobKey_Widget:
                {
                    Widget w;
                    blob >> w;
                    w.print();
                }
                break;

            case kBlobKey_Gadget:
                {
                    Gadget g;
                    blob >> g;
                    g.print();
                }
                break;

            default:
                std::cout << "Unknown object type in blob\n";
                assert(false);
        }
    }
}

如果你可以使用 Boost,你可能想使用 Boost.Serialization使用二进制内存流,如 answer .


(1) 可移植 意味着源代码可以在任何地方编译。如果传输到具有不同字节顺序和整数大小的其他机器,生成的二进制 blob 将不能可移植。

关于c++ - Blob 内数据的对齐和填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11695889/

相关文章:

c++ - 分段堆栈如何工作

c++ - 调试客户机器上生成的核心文件

C++我在所有标题中都使用了一个函数

C++ 数独谜题,初始化网格 - 新手

c++ - 在C、VS 2010中包含C++头文件

c++ - 使用 typedef 类型实现函数的 VC++ 类模板

c++ - 错误 C2512 : 'std::basic_ostream<_Elem,_Traits>' : no appropriate default constructor available with Visual Studio only

c++ - Visual Studio 中的 std::begin 和 std::end 迭代器

c++ - 我必须使用 ->Release() 吗?

c++ - 如何从库函数访问 C++ 中的 argc 和 argv