Заполнение структуры

#c #struct #padding

#c #структура #заполнение

Вопрос:

Я пытаюсь считывать фрагменты данных из файла непосредственно в структуру, но заполнение приводит к считыванию слишком большого количества данных и их смещению.

Должен ли я вручную считывать каждую часть в структуру или есть более простой способ сделать это?

Мой код:

Структуры

 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*)amp;header.ID, sizeof(header.ID));
header.ID[9] = '';
in.read((char*)amp;header.version, sizeof(header.version));
std::cout << header.ID << " " << header.version << "n";
in.read((char*)amp;NumVertices, sizeof(NumVertices));
std::cout << NumVertices << "n";

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

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

Я пытался выполнить: in.read((char*)amp;Vertices[0], sizeof(Vertices[0]) * NumVertices); но это приводит к неверным результатам из-за того, что я считаю дополнением.

Также: на данный момент я использую приведения в стиле C, какое приведение C было бы правильным для использования в этом сценарии или приведение в стиле C подойдет?

Комментарии:

1. Что касается последней части вашего вопроса, вы могли бы использовать reinterpret_cast<char *> , что делает его очень явным.

2. В этом есть нечто большее, чем я сначала подумал: P

Ответ №1:

Если вы записываете всю структуру в двоичном формате, вам не нужно читать ее так, как если бы вы сохраняли каждую переменную отдельно. Вы бы просто прочитали размер структуры из файла в структуру, которую вы определили.

 Header header;
in.read((char*)amp;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;   
}
  

Комментарии:

1. Когда вы записываете структуру в файл, она будет записана со всеми данными, включая ваши нулевые окончания строк и отступы. Поэтому, когда вы читаете это снова, все возвращается на свои места так, как и ожидалось.

2. О, я понимаю (я думаю). Файлы не обязательно были написаны на том же компьютере, который их читает, поэтому файлы также могут иметь разный порядковый номер, и поэтому также возникают проблемы с выравниванием?

3. Да, вам нужно убедиться, что вы не записываете данные так же, как вы их читаете в данный момент. (т. Е. у вас не должно быть кода типа write((char *)amp;header. ИДЕНТИФИКАТОР, 10) ) Вы должны записывать структуру целиком, как в write((char*)amp;header, sizeof(заголовок)); затем прочитайте это, как указано выше.

4. Блестяще, я не знал, что это также связано с тем, как структурирован файл. Спасибо. У меня вопрос о порядке следования; Будет ли вся структура в файле «в обратном порядке» или элементы по-прежнему будут располагаться в том же порядке, но их данные будут в обратном порядке?

Ответ №2:

Нет, вам не обязательно читать каждое поле отдельно. Это называется выравниванием / упаковкой. Смотрите http://en.wikipedia.org/wiki/Data_structure_alignment

Приведение в стиле C эквивалентно reinterpret_cast . В этом случае вы используете его правильно. Вы можете использовать синтаксис, специфичный для C , но это требует гораздо больше ввода.

Комментарии:

1. «но это намного больше ввода». это очень печальный момент с сегодняшним инструментарием.

2. @jv42: Имея два синтаксически разных варианта описания одного и того же выражения, я предпочитаю самый короткий, даже если редактор автоматически завершается.

3. Это ваш выбор, и это нормально, я сам часто использую приведения в стиле C при написании кода на C . Но выразительность приведений в стиле C намного лучше и передает намерения без добавления комментариев (т. Е. Нарушаю ли я спецификатор const, выполняю ли я преобразование типов или я путаюсь с указателями).

Ответ №3:

Вы можете изменить заполнение, явно попросив ваш компилятор выровнять структуры по 1 байту вместо 4 или как там по умолчанию. В зависимости от среды, это может быть сделано многими различными способами, иногда файл за файлом (‘единица компиляции’) или даже структура за структурой (с прагмами и тому подобным) Или только для всего проекта.

Ответ №4:

header.ID[10] = '';

header.ID [9] является последним элементом массива.

Комментарии:

1. Упс! В последнее время слишком часто использую Lua: P

Ответ №5:

Если вы используете компилятор Microsoft, тогда изучите прагму align. Существуют также файлы, включающие выравнивание:

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

GNU gcc имеет другую систему, которая позволяет добавлять выравнивание / дополнение к определению структуры.

Комментарии:

1. Компилятор Microsoft также поддерживает пакетные прагмы. Я предпочитаю использовать их, поскольку код становится более переносимым / независимым от компилятора. Смотрите msdn.microsoft.com/en-us/library/ms253935.aspx

2. При компиляции, если программа выполняется на другом компьютере, будут ли структуры по-прежнему иметь заполнение, заданное любым из приведенных здесь методов? (Я чувствую, что это может быть глупый вопрос)

3. @Rarge — да, заполнение влияет на двоичное изображение, поэтому исправляется во время компиляции / компоновки.

Ответ №6:

Если вы читаете и записываете этот файл самостоятельно, попробуйте библиотеку Google Protobuf. Он будет обрабатывать все вопросы с порядком следования, выравниванием, заполнением и языковым взаимодействием.

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