Структуры странные - C ++ - PullRequest
       5

Структуры странные - C ++

1 голос
/ 17 февраля 2012

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

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

Так вот моя структура:

struct Entry
    {
        unsigned short type;
        unsigned long long identifier;
        unsigned int offset_specifier, length;
    };

А вот мой код для чтения всего дерьма в указатель / массив структуры:

Entry *entries = new Entry[SOME_DYNAMIC_AMOUNT];
fread(entries, sizeof(Entry), SOME_DYNAMIC_AMOUNT, openedFile);

Как видите, я записываю все это в свой массив struct.Теперь я покажу вам данные, которые я читаю (для первой структуры в этом примере).

The bytes I am reading.

Итак, это данные, которые входят в первый элемент в 'записей.Первый пункт (короткий, type), кажется, читается нормально.После этого, когда «идентификатор» читается, создается впечатление, что вся структура смещена на X байтов.Вот изображение первого элемента (после обращения к обратному порядку байтов):

enter image description here

А вот данные в памяти (красный квадрат - то место, где они начинаются):

enter image description here

Я знаю, это немного сбивало с толку, но я пытался объяснить это как можно лучше.Спасибо за любую помощь, Хетелек.:)

Ответы [ 2 ]

6 голосов
/ 17 февраля 2012

Структуры дополняются дополнительными байтами, чтобы поля были быстрее доступны. Вы можете предотвратить это с помощью #pragma pack:

#pragma pack(push, 1)

struct Entry
{
    /* ... */
};

#pragma pack(pop)

Обратите внимание, что он может быть не на 100% переносимым (я знаю, что по крайней мере GCC и MSVC поддерживают его для x86).

3 голосов
/ 17 февраля 2012

Чтение и запись структур в двоичный файл представляет опасность.

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

Это можетпроизойдет, если две системы (одна записывает данные, а другая - читает) имеют разные требования к выравниванию и, следовательно, разные макеты для типа Entry.

Выравнивание не единственная потенциальная проблема;Различия в endianness также могут быть серьезной проблемой.Различные системы могут иметь разные размеры для предопределенных целочисленных типов.Вы не можете предполагать, что struct Entry будет иметь согласованную разметку, если весь код, который с ней работает, не будет работать в одной системе - и в идеале с той же версией того же компилятора.

You мог бы использовать #pragma pack, чтобы обойти это, но я не рекомендую это.Это не портативно, и может быть небезопасно .В лучшем случае это обойдет проблему заполнения между членами;есть еще много способов, которыми макет может варьироваться от одной системы к другой.

Невозможно дать вам окончательное решение, не зная, где и как определено расположение данных файла, который вы читаете.

Если мы предположим, что формат файла для каждой записи, например:

  • 2-байтовое целое число без знака в сетевом порядке (type)
  • An8-байтовое целое в сетевом порядке байтов (identifier)
  • 4-байтовое целое в сетевом порядке байтов (offset_specifier, length)
  • с добавлением нет между ними

тогда вы должны либо прочитать данные в буфер unsigned char[], либо в объекты типа uint16_t, uint32_t и uint64_t (определенные в <cstdint> или <stdint.h>), а затем преобразовать его из сетевого порядка байтов в локальный порядок байтов.

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

Если , вы можете предположить, что программа будет работать толькоВ ограниченном наборе систем вы можете обойти это.Например, вы можете настроить декларацию struct Entry, чтобы она соответствовала формату файла, и прочитать и записать ее напрямую.Это будет означать, что ваш код не переносим на некоторые системы.Вам придется решить, какую цену вы готовы заплатить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...