Подтвердить что ты не робот

C/С++ получение размера структуры

Сегодня, с большим удивлением, я обнаружил, что

Когда оператор sizeof применяется к классу, структуре или типу объединения, результатом является количество байтов в объекте этого типа плюс добавление дополнений для выравнивания элементов на границах слов. Результат не обязательно соответствует размеру, рассчитанному путем добавления требований к хранению отдельных членов.

Я не знал об этом, и я уверен, что эта вещь нарушает мой старый код: для чтения двоичных файлов у меня были такие структуры, как этот:

struct Header
{
    union {
        char identc[4];
        uint32 ident;
    };
    uint16 version;
};

и прочитать эти 6 байтов напрямую с помощью fread, управляемых sizeof:

fread( &header, sizeof(header), 1, f );

Но теперь sizeof(header) возвращает 8!


Возможно ли, что с более старыми версиями GCC sizeof(header) вернулся 6, или я полностью потерял сознание?

В любом случае есть ли какой-либо другой оператор (или директива препроцессора или что-то еще), позволяющий компилятору узнать, насколько велики структуры - исключая заполнение?

В противном случае, что было бы чистым способом чтения структуры raw-data из файла, для которого не требуется писать слишком много кода?


ИЗМЕНИТЬ: Я знаю, что это не правильный способ чтения/записи двоичных данных: у меня будет другой результат, зависящий от конечной машины и т.д. В любом случае, этот метод является самым быстрым, я пытаюсь прочитать некоторые двоичные данные, чтобы быстро получить его контент, а не писать хорошее приложение, которое я буду использовать в будущем или выпустить.

4b9b3361

Ответ 1

То, что вы хотите, это команда #pragma pack. Это позволяет вам устанавливать упаковку на любую сумму, которую вы хотите. Обычно вы устанавливаете значение упаковки равным 1 (или 0?) Перед определением структуры, а затем возвращаете значение по умолчанию после определения.

Обратите внимание, что это не делает ничего, чтобы гарантировать переносимость между системами.

Смотрите также: use-of-pragma-in-c и различные другие вопросы по SO

Ответ 2

Да, представленный вами код не переносится. Не только размеры структуры, но и порядки байтов могут отличаться.

Ответ 3

Это неправильный способ обработки двоичных файлов. Помимо проблем с выравниванием, у него также есть проблемы со стороны. Правильный способ чтения двоичных файлов - это массив uint8_t (или unsigned char, это действительно не имеет значения) и ваши собственные функции для построения представления в памяти из данных.

Ответ 4

В большинстве компиляторов предусмотрено определенное расширение, которое позволяет вам управлять упаковкой структур. Это должно позволить вам контролировать это. Однако, когда вы пишете структуру в двоичном формате, вы должны просто написать ее и прочитать ее независимо от упаковки, так как при написании структуры она также должна писать байты sizeof (struct). Единственный случай, когда это было бы проблемой, - это если вы хотите прочитать файлы, созданные с предыдущими версиями. Кроме того, вам необходимо рассмотреть вопросы о байтовых заказах и т.д.

Ответ 5

Ваш вопрос специфичен для компилятора, но обычно, если вы строите свою структуру таким образом, чтобы каждый член находился на границе того же размера, что и сам (четыре элемента байта на границах, делящихся на четыре и т.д.), вы получите поведение вы хотите. Смотрите также такие случаи, как тот, который вы представили, где дополнение находится в конце структуры, чтобы выровнять начало первого элемента следующей структуры - если они были выложены в массиве.

Ответ 6

Кажется, что вы не задали вопрос, поэтому я не уверен, почему я даже пытаюсь ответить! Но да, упаковка важна и будет меняться в зависимости от версий компилятора, флагов, целевых архитектурных прагм, направления ветра, фаз луны и, возможно, многих других вещей. Сброс двоичного файла в файл (или сокет) не очень хороший способ сериализации.

Ответ 7

Это дополнительное дополнение необходимо для правильного выравнивания элементов при создании массива этих структур. Без него 2-й элемент массива будет иметь член-идентификатор, выровненный по адресу, который не будет кратным 4.

Возможно, слишком поздно что-то сделать, вы, вероятно, раньше писали файлы с этой структурой. Изменение упаковки сделает эти файлы нечитаемыми. Но, да, наличие файлов, зависящих от настроек компилятора, не является самой большой идеей. В наши дни есть данные, хранящиеся в человеко-читаемом формате. Ни байты дисков, ни циклы CPU не стоят того.

Ответ 8

Да, проблема выравнивания. Вот почему сообщения интернет-протокола выравнивают структуры, поэтому эту проблему можно избежать при отправке данных по сети.

Что вы можете сделать - либо исправить свои структуры так, чтобы они были правильно выровнены, либо иметь функции маршаллинга, которые вы используете при сохранении и извлечении данных.