0

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

我希望 blob 中的数据按如下方式组织:

| data1 类型 | 数据1 | data2 类型 | 数据2 | dataN 类型 | 数据N |

dataN typeint我在开关中用来将 转换dataN为适当类型的一个。

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

我试过这个:

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

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

PS:在 x86 或 x64 架构中是否有任何大于 8 字节的数据类型?

4

4 回答 4

1

看起来在这种情况下#pragma pack(8)没有效果。

在 MS 编译器文档中,pack 的参数以下列方式描述: 指定用于打包的值(以字节为单位)。n 的默认值为 8。有效值为 1、2、4、8 和 16。成员的对齐方式将位于n 的倍数或成员大小的倍数的边界上,以两者为准更小

因此,该#pragma pack指令不能增加成员的对齐方式,而是可以减少它(#pragma pack(1)例如使用)。在您的情况下,选择整个结构对齐以使其最大元素自然对齐(int在 32 位和 64 位 CPU 上通常为 4 个字节)。结果,总大小为 4 * 3 = 12 个字节。

于 2012-07-27T21:49:45.713 回答
1

这个答案假设了两件事:

  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与二进制内存流一起使用,如本答案所示。


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

于 2012-07-27T23:16:23.653 回答
0

你试过这个吗?

class A {
public:
    union {
        uint64_t dummy;

        int data;
    };
};

的实例A及其data成员现在将始终对齐到 8 个字节。当然,如果你在前面挤一个 4 字节的数据类型,这是没有意义的,它也必须是 8 字节。

于 2012-07-28T01:09:05.567 回答
0

@Negai 解释了为什么你会得到观察到的大小。

您还应该重新考虑您对“紧密包装”数据的假设。对于上述结构,结构中有。假设 32 位 int 和 16 位 short,在 short 之后有一个两个字节的洞,在 char 之后有一个 3 个字节的洞。但这并不重要,因为这个空间在结构内部。

换句话说,要么你得到一个紧凑的数据结构,要么你得到一个对齐的数据结构,但不是两者兼而有之。

通常,您不需要做任何特别的事情来获得编译器默认执行的“对齐”行为。如果您希望数据“打包”而不是对齐, #pragma pack很有用,即删除编译器引入的一些漏洞以保持数据对齐。

于 2012-07-27T22:08:34.927 回答