C ++通过字段读取结构并将结构直接写入流

时间:2013-07-03 17:10:45

标签: c++ serialization deserialization abi

我在Visual Studio 2008中使用C ++。假设我有这样的结构:

    struct StructOfInts
    { 
        int a;
        int b;
        int c; 
    };

这意味着要像这样读写:

    void Read( std::istream& is, StructOfInts& myStruct  )
    {
        is.read( (char*)&myStruct.a, sizeof myStruct.a );
        is.read( (char*)&myStruct.b, sizeof myStruct.b );
        is.read( (char*)&myStruct.c, sizeof myStruct.c );
    }
    void Write( std::ostream& os, StructOfInts& myStuct )
    {
        os.write( (char*)&myStruct, sizeof myStruct );
    }

上述代码在读取或写入文件时是否会导致某种内存损坏?由于内存损坏,我的意思是读入不正确的值。我正在尝试确定正在读入的-1。#QNB值的来源,并且想知道这是否可能是原因。 另外,如果我使用pragma pack打包结构会有区别吗?

2 个答案:

答案 0 :(得分:2)

是的,由于struct字段之间可能存在填充,您的代码可能导致读入无效值。让我们使用你的struct StructOfInts的例子,并想象编译器在字段之间插入一些填充,如下所示:

byte  | 0 1 2 3 | 4 5     | 6 7 8 9 | 10 11 12 13
value | field a | padding | field b | field c

然后,当您将结构体写入流时,最终可能会出现类似

的内容
byte | 0  1  2  3   | 4   5   | 6  7  8  9   | 10 11 12 13
char | \0 \0 \0 'a' | '?' '?' | \0 \0 \0 'b' | \0 \0 \0 'c'

如果字段包含(分别)值(int)'a', (int)'b', (int)'c'

然后,当您重新读取值时,它看起来像

myStruct->a = int version of \0 \0 \0 'a'
myStruct->b = int version of '?' '?' \0 \0
myStruct->c = int version of \0 'b' \0 \0

这显然不是你想要的。

在搜索#pragma pack后,看起来对这种情况有帮助。编译器不会插入填充(尽管实现定义的......),因此值(很可能)可以正确读取和写入。

另外,另一件事是:如果你在一个系统(计算机/操作系统/编译器)上写,然后在另一个系统中读取数据,那么字节序问题也可能导致问题。

答案 1 :(得分:1)

测试此案例的快速方法是:

static_assert(sizeof(StructOfInts) == (3 * sizeof(int)), "size mismatch");

实现这一目标(IMO)的最佳方法是使用对称形式:逐个字段序列化,然后逐个字段反序列化。

简而言之,依赖于您的实现所使用的行为是依赖于目标体系结构的ABI,而不是标准(BAD)。因此,它可能导致“腐败”。

结构的大小可能因ABI而异,并且整数的大小甚至它们的字节顺序可能会有所不同 - 导致“损坏”。填充和对齐也由ABI指定。

因此,固定宽度类型,显式字节序以及字段对称序列化通常都是您需要的。