C / C ++ получает размер структуры - PullRequest
11 голосов
/ 22 ноября 2010

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

Когда оператор 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, или я полностью сошел с ума?

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

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


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

Ответы [ 8 ]

9 голосов
/ 22 ноября 2010

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

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

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

3 голосов
/ 22 ноября 2010

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

2 голосов
/ 22 ноября 2010

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

1 голос
/ 22 ноября 2010

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

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

1 голос
/ 22 ноября 2010

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

1 голос
/ 22 ноября 2010

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

1 голос
/ 22 ноября 2010

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

0 голосов
/ 22 ноября 2010

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

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

...