【问题标题】:Struct Padding结构填充
【发布时间】:2011-08-13 20:32:47
【问题描述】:

我正在尝试将文件中的数据块直接读取到结构中,但填充导致读取的数据过多并且数据未对齐。

我必须手动将每个部分读入结构中还是有更简单的方法来做到这一点?

我的代码:

结构

typedef unsigned char byte;

struct Header
{
    char ID[10];
    int  version;
};

struct Vertex //cannot rearrange the order of the members
{
    byte    flags;
    float   vertex[3];
    char    bone;
    byte    referenceCount;
};

我是如何读取数据的:

std::ifstream in(path.c_str(), std::ifstream::in | std::ifstream::binary);

Header header;
in.read((char*)&header.ID, sizeof(header.ID));
header.ID[9] = '\0';
in.read((char*)&header.version, sizeof(header.version));
std::cout << header.ID << " " << header.version << "\n";
in.read((char*)&NumVertices, sizeof(NumVertices));
std::cout << NumVertices << "\n";

std::vector<Vertex> Vertices(NumVertices);

for(std::vector<Vertex>::iterator it = Vertices.begin(); it != Vertices.end(); ++it)
{
    Vertex& v = (*it);
    in.read((char*)&v.flags, sizeof(v.flags));
    in.read((char*)&v.vertex, sizeof(v.vertex));
    in.read((char*)&v.bone, sizeof(v.bone));
    in.read((char*)&v.referenceCount, sizeof(v.referenceCount));
}

我尝试这样做:in.read((char*)&amp;Vertices[0], sizeof(Vertices[0]) * NumVertices);,但这会产生不正确的结果,因为我认为是填充。

另外:目前我正在使用 C 样式转换,在这种情况下使用正确的 C++ 转换是什么,或者 C 样式转换可以吗?

【问题讨论】:

  • 关于你问题的最后一部分,你可以使用 reinterpret_cast,这使得它非常明确。
  • 这比我最初想象的要多:P

标签: c++ struct padding


【解决方案1】:

不,您不必单独阅读每个字段。这称为对齐/打包。见http://en.wikipedia.org/wiki/Data_structure_alignment

C 风格的转换等价于reinterpret_cast。在这种情况下,您可以正确使用它。您可以使用特定于 C++ 的语法,但它需要更多的输入。

【讨论】:

  • "但打字要多得多。"对于今天的工具,这是一个非常可悲的点。
  • @jv42:有两个语法不同的选项来描述同一个表达式,即使编辑器自动完成,我更喜欢最短的一个。
  • 这是你的选择,没关系,我自己在 C++ 中编码时确实使用 C 风格的转换。但是 C++ 风格转换的表现力要好得多,并且在不添加 cmets 的情况下传达意图(即,我是在破坏 const 说明符,是在进行类型转换还是在搞乱指针)。
【解决方案2】:

您可以通过明确要求编译器将结构对齐到 1 个字节而不是 4 个或任何默认值来更改填充。根据环境的不同,这可以通过多种不同的方式完成,有时是逐个文件(“编译单元”),甚至是逐个结构(使用编译指示等),或者仅在整个项目中完成。

【讨论】:

    【解决方案3】:

    如果您以二进制形式写出整个结构,则无需像单独存储每个变量一样读取它。您只需将结构的大小从文件读入您​​定义的结构。

    Header header;
    in.read((char*)&header, sizeof(Header));
    

    如果您始终在同一架构或同一台机器上运行,则无需担心字节序问题,因为您将按照应用程序需要读取它们的方式将它们写出。如果您是在一种架构上创建文件并期望它在另一种架构上可移植/可用,那么您将需要相应地交换字节。我过去这样做的方法是创建自己的交换方法。 (例如 Swap.h)

    Swap.h - This is the header you use within you're code
    
    void swap(unsigned char *x, int size);
    
    ------------------
    
    SwapIntel.cpp - This is what you would compile and link against when building for Intel
    
    void swap(unsigned char *x, int size)
    {
        return;   // Do nothing assuming this is the format the file was written for Intel (little-endian)
    }
    
    ------------------
    
    SwapSolaris.cpp -  This is what you would compile and link against when building for Solaris
    
    void swap(unsigned char *x, int size)
    {
        // Byte swapping code here to switch from little-endian to big-endian as the file was written on Intel
        // and this file will be the implementation used within the Solaris build of your product
        return;   
    }
    

    【讨论】:

    • 当您将结构写入文件时,它将与所有数据一起写入,包括您的空行结尾和填充。因此,当您重新读回它时,一切都会按预期的方式放回原处。
    • 哦,我明白了(我想)。这些文件不一定写在读取它们的同一台机器上,因此文件也可能具有不同的字节顺序,这就是为什么还会出现对齐问题?
    • 是的,您需要确保您写出数据的方式与当前读取数据的方式不同。 (即你不应该有像 write((char*)&header.ID, 10) 这样的代码)你应该像 write((char*)&header, sizeof(header)) 一样将结构作为一个整体来编写;然后按上述方式阅读。
    • 太棒了,我不知道这些也与文件的结构方式有关。谢谢你。我有一个关于字节序的问题;整个结构会在文件中“向后”,还是成员仍会保持相同的顺序但他们的数据会向后?
    【解决方案4】:

    header.ID[10] = '\0';

    header.ID[9] 是数组的最后一个元素。

    【讨论】:

    • 糟糕!最近使用 Lua 太多了:P
    【解决方案5】:

    如果您使用的是 Microsoft 编译器,请探索 align pragma。还有对齐包含文件:

    #include <pshpack1.h>
    // your code here
    #include <poppack.h>
    

    GNU gcc 有一个不同的系统,允许您在结构定义中添加对齐/填充。

    【讨论】:

    • 微软编译器也支持包编译指示。我更喜欢使用它们,因为代码变得更加便携/独立于编译器。见msdn.microsoft.com/en-us/library/ms253935.aspx
    • 如果程序在不同的机器上运行编译时,结构是否仍然具有此处任一方法给出的填充? (我觉得这可能是一个愚蠢的问题)
    • @Rarge - 是的,填充会影响二进制图像,因此在编译/链接时固定。
    【解决方案6】:

    如果您自己读写此文件,请尝试使用 Google Protobuf 库。它将处理所有字节顺序、对齐、填充和语言互操作问题。

    http://code.google.com/p/protobuf/

    【讨论】:

      猜你喜欢
      • 2015-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-12
      • 1970-01-01
      • 2012-08-14
      • 2013-01-20
      相关资源
      最近更新 更多