сериализация — структура чтения C ++ по полю и запись структуры непосредственно в поток

Я использую C ++ с Visual Studio 2008. Скажем, у меня есть такая структура:

    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?

2

Решение

Быстрый способ проверить этот случай:

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

Лучший способ сделать это (IMO) — использовать симметричную форму: сериализовать поле за полем, а затем десериализовать поле за полем.

Короче говоря, полагаться на поведение вашей реализации — значит полагаться на ABI целевой архитектуры, а не на стандарт (BAD). Таким образом, это может привести к «коррупции».

Размер структуры может варьироваться в зависимости от ABI, а размер целых и даже порядок их байтов может варьироваться, что приводит к «коррупции». Обивка и выравнивание также определяются ABI.

Таким образом, типы фиксированной ширины, явный порядок байтов и симметричная сериализация по полям часто будут тем, что вам нужно.

1

Другие решения

Да твой код мог приводят к считыванию неверных значений из-за возможного заполнения 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Похоже, это поможет в этом случае. Компилятор не будет вставлять отступы (хотя это является реализация определена …), поэтому значения (скорее всего) будут прочитаны и записаны правильно.

Кроме того, другое дело: если вы выполняете запись в одной системе (компьютер / ОС / компилятор), а затем читаете данные в другой системе, то проблемы с порядком байтов также могут вызвать проблемы.

2