Я использую 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?
Быстрый способ проверить этот случай:
static_assert(sizeof(StructOfInts) == (3 * sizeof(int)), "size mismatch");
Лучший способ сделать это (IMO) — использовать симметричную форму: сериализовать поле за полем, а затем десериализовать поле за полем.
Короче говоря, полагаться на поведение вашей реализации — значит полагаться на ABI целевой архитектуры, а не на стандарт (BAD). Таким образом, это может привести к «коррупции».
Размер структуры может варьироваться в зависимости от ABI, а размер целых и даже порядок их байтов может варьироваться, что приводит к «коррупции». Обивка и выравнивание также определяются ABI.
Таким образом, типы фиксированной ширины, явный порядок байтов и симметричная сериализация по полям часто будут тем, что вам нужно.
Да твой код мог приводят к считыванию неверных значений из-за возможного заполнения 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
Похоже, это поможет в этом случае. Компилятор не будет вставлять отступы (хотя это является реализация определена …), поэтому значения (скорее всего) будут прочитаны и записаны правильно.
Кроме того, другое дело: если вы выполняете запись в одной системе (компьютер / ОС / компилятор), а затем читаете данные в другой системе, то проблемы с порядком байтов также могут вызвать проблемы.