blob内数据的对齐和填充

时间:2012-07-27 21:29:12

标签: c++ visual-c++

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

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

  

| data1类型| data1 | data2类型| data2 | dataN类型| dataN |

dataN typeint,我在交换机中使用dataN#pragma pack(8) class A { public: short b; int x; char v; }; 转换为适当的类型。

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

我试过了:

sizeof(A)

但它不起作用,因为使用{{1}}我得到12个字节而不是预期的16个字节。

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

4 个答案:

答案 0 :(得分:1)

在这种情况下,#pragma pack(8)似乎无效。

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

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

答案 1 :(得分:1)

这个答案假设有两件事:

  1. 您希望二进制blob紧密打包(无孔)。
  2. 您不希望以未对齐的方式访问数据成员(与访问默认情况下编译器所需方式对齐的数据成员相比,这是慢的。)
  3. 如果是这种情况,那么您应该考虑将大型“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) Portable 意味着源代码应该在任何地方编译。如果传输到具有不同字节顺序和整数大小的其他计算机,则生成的二进制blob将可移植。

答案 2 :(得分:0)

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

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

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

通常,您不会有任何特殊操作来获得编译器默认执行的“对齐”行为。 #pragma pack 非常有用,如果您希望数据“打包”而不是对齐,那就是删除编译器引入的一些漏洞,以保持数据一致。

答案 3 :(得分:0)

你试过这个吗?

class A {
public:
    union {
        uint64_t dummy;

        int data;
    };
};

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