Я был на этом весь день, и я не могу найти решение 🙁
У меня есть заголовок для файла, который я хочу создать (я анализирую файл obj в Python для вывода в двоичные данные, которые будут загружены в моем игровом движке C ++).
Вот определение C ++ заголовка сетки
struct MeshHeader
{
unsigned short _vertex_size;
uint32 _vertex_count;
unsigned short _index_buffer_count;
short _position_offset;
unsigned short _position_component_count;
short _uv_offset;
unsigned short _uv_component_count;
short _normal_offset;
unsigned short _normal_component_count;
short _tangent_offset;
unsigned short _tangent_component_count;
short _binormal_offset;
unsigned short _binormal_component_count;
short _colour_offset;
unsigned short _colour_component_count;
};
Где uint32 — это в основном typedef из uint32_t из stdint.h ….
Таким образом, судя по этому, первые три члена имеют размер 2 байта, 4 байта, 2 байта соответственно, да?
Вот как я прочитал это в структуру
fread(&_header, sizeof(MeshHeader), 1, f);
_vertex_size получает правильно установленное значение 56, но _vertex_count устанавливается равным 65536. Если я изменяю его тип данных на uint16 (unsigned short), он корректно устанавливается на 36. Но почему?
Я использую pack("<I")
функция (зная, что моя машина немного порядковый номер).
Это мой код упаковки в Python
f.write(pack('<H', self._vertex_size))
f.write(pack('<I', self._vertex_count))
f.write(pack('<H', self._index_buffer_count))
f.write(pack('<h', self._position_offset))
f.write(pack('<H', self._position_component_count))
f.write(pack('<h', self._uv_offset))
f.write(pack('<H', self._uv_component_count))
f.write(pack('<h', self._normal_offset))
f.write(pack('<H', self._normal_component_count))
f.write(pack('<h', self._tangent_offset))
f.write(pack('<H', self._tangent_component_count))
f.write(pack('<h', self._binormal_offset))
f.write(pack('<H', self._binormal_component_count))
f.write(pack('<h', self._colour_offset))
f.write(pack('<H', self._colour_component_count))
Следуя спецификациям функции struct.pack ( https://docs.python.org/2/library/struct.html ) … H — это unsigned short (2 байта), I — это целое число без знака (4 байта), а h — short (2 байта), которые точно соответствуют тому, что я указал в моем классе C MeshHeader, не так ли?
Последние несколько часов я выдергивал свои волосы (и у меня их осталось немного!). Любые предложения о том, что может произойти?
Вот снимок заголовочного файла в Sublime Text 3, кстати
http://gyazo.com/e15942753819e695617390129e6aa879
Как уже упоминалось @martineau, вы видите C структура упаковки. Компилятор добавляет заполнение между элементами, не имеющими размера слова, для оптимизации доступа к памяти. Вы можете отключить это с определенным #pragma
директивы. Для Visual C синтаксис #pragma pack(1)
как объяснено в MSDN. В следующем примере я также использую push
а также pop
восстановить упаковку до прежнего значения. Правильная упаковка важна для эффективного доступа к памяти, поэтому ее изменение должно быть зарезервировано только для структур, которые вы записываете на диск.
// align on 1-byte
#pragma pack(push, 1)
struct MeshHeader
{
unsigned short _vertex_size;
uint32 _vertex_count;
unsigned short _index_buffer_count;
short _position_offset;
// ...
};
// restore alignment
#pragma pack(pop)
Обратите внимание, что даже если избежать упаковки структуры, у вас могут возникнуть проблемы с endianity. Запись структур на диск предполагает, что вы обладаете полным контролем и знаниями заранее, как для писателя, так и для читателя. Вы можете сделать вашу жизнь проще, используя правильную библиотеку сериализации. Есть много, но несколько примеров, которые поддерживают как C, так и Python: Protobuf а также Avro.