Использование fread для чтения содержимого файла в структуру - PullRequest
3 голосов
/ 01 июня 2011

В книге «Расширенное программирование в среде Unix» есть часть (гл. 8.14, стр. 251), в которой автор показывает нам определение структуры «acct» (используется для хранения информации учетных записей).Затем он показывает программу, в которой он считывает учетные данные из файла в структуру (ключевая часть которой):

fread (&acdata, sizeof(acdata), 1, fp)

Проблема, с которой я столкнулся, заключается в том, что я слышал, что Cкомпиляторы иногда переставляют элементы структуры в памяти, чтобы лучше использовать пространство (из-за проблем с выравниванием).Таким образом, если этот код просто берет все содержимое файла и вставляет его в acdata (а содержимое файла расположено так, чтобы соответствовать порядку, указанному в определении структуры), если некоторые элементы struct были перемещены,затем, если я обращаюсь к ним в коде, возможно, я получаю не то, что ожидал (поскольку данные в файле не перестраивались так, как структура в памяти).

Чего мне не хватает (потому что изто, что я получаю, кажется ненадежным)?

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

Ответы [ 4 ]

8 голосов
/ 01 июня 2011

Worry!

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

Иногда это работает (случайно), и вы никогда не замечаете проблему; иногда это не работает, и вы выдергиваете волосы в течение нескольких дней.

Вопрос не имеет ничего общего с перестановкой членов структуры. Компиляторы этого не делают. Это также не имеет ничего общего с оптимизацией.

Проблема в выравнивании байтов , и в статье Википедии, упомянутой ниже, рассказывается, как исправить ваши структуры, чтобы они всегда были правильно выровнены. всегда хорошая идея обратить внимание на выравнивание байтов. В противном случае ваша программа не переносима. И, что еще хуже, программа, которую вы тщательно скомпилировали на своем whiz-bang x86-64 и разослала всем своим клиентам, вдруг не будет работать на их 32-битных компьютерах.

Не менее важно: помните о длине и выравнивании членов структуры.

Есть хорошая статья в Википедии , которая объясняет детали. Это очень стоящее чтение.

Я бы с осторожностью относился к прагме для конкретного компилятора, которая выполняет эту работу, но только для этого компилятора. Если вы добавите прагму в свой код, значит, ваша программа больше не является C.

3 голосов
/ 01 июня 2011

Структура (заполнение и выравнивание, но не порядок) структуры может измениться, если вы компилируете свой код на другом компиляторе или более поздней версии компилятора, или даже с другими параметрами времени компиляции.

Это не будет меняться от запуска к запуску одной и той же скомпилированной программы - это будет кошмарный сценарий: -)

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

Соответствующие разделы стандарта C99:

6.2.6.1 / 1: представления всех типов не определены, кроме случаев, указанных в этом подпункте.

6.2.6.1 / 6 (единственное упоминание структур в этом подпункте): когда значение сохраняется в объекте структуры или типа объединения, в том числе в объекте-члене, байты представления объекта, соответствующие любому заполнению байты принимают неопределенные значения. Значение структуры или объекта объединения никогда не является представлением ловушек, даже если значение члена структуры или объекта объединения может быть представлением ловушек.

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

6.7.2.1 / 13: ... Внутри объекта структуры может быть безымянный отступ, но не в его начале.

6.7.2.1 / 15: Там может быть безымянный отступ в конце структуры или объединения.


Если бы вы создавали версию 1.1 своей программы и в ней использовалась другая структура структуры (новый компилятор, другие параметры компилятора, #pragma pack и т. Д.), Очень быстро стало бы очевидно, что у вас возникла проблема во время модульных тестов. (который должен включать загрузку в файл из предыдущей версии).

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

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

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

2 голосов
/ 01 июня 2011

Да

Ваша программа будет стабильной.

Ваш вопрос вызвал костер рекомендаций по переносимости, которые вы на самом деле не просили.Вопрос, который вы, похоже, задаете: «Этот шаблон кода и моя программа стабильны?». И ответ на этот вопрос да.

Ваша структура не будет переупорядочена.C99 специально запрещает перестановку элементов конструкции. 1

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

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

Правила выравнивания памяти действительно являются своего рода скрытыми ABI ,Они не могут измениться без добавления очень специализированных и по определению редко используемых флагов компилятора.Они, как правило, отлично работают на разных компиляторах.(В противном случае каждый элемент системы, указанный выше, ТАКЖЕ должен был бы быть скомпилирован одним и тем же компилятором или бесполезен. Каждый компилятор, который поддерживает данную систему, использует точно такие же правила выравнивания. Ничего не будет работать, иначе.) Флаги компилятора, которые изменяют политики выравнивания, обычно предназначены для встраивания в конфигурацию компилятора для данной ОС.

Теперь ваша структура двоичного файла, хотя и вполне разумная, немного старая школа.У него есть определенные недостатки.Хотя ни один из них не является шоу-стоппером, и ни один из них, как правило, не стоит переписывать приложение, они включают в себя:

  • , трудно отлаживать бинарные файлы
  • они блокируются в порядке одного байта иединая политика выравнивания.В (к сожалению, все более маловероятном) случае, когда вам нужно портировать на новую архитектуру, вам может понадобиться распаковать запись с помощью memcpy (3).Не конец света.
  • они не структурированы.Такие вещи, как YAML и, хм, даже XML, являются своего рода синтаксическим анализом, поэтому их намного проще читать в файле, а некоторые типы манипуляций с файлами можно выполнять с помощью инструментов.Что еще более важно, сам формат файла становится более гибким.Тем не менее, ваша способность использовать преимущества объекта автоматического анализа ограничена в C и C ++.

Поскольку я понимаю запрос Паксдиабло, он хотел бы, чтобы я согласился с тем, что существуют параметры компилятора и прагмы.это, если используется, изменит правила выравнивания.Это правда.Очевидно, что эти параметры используются только по определенным причинам.


1. C99 6.7.2.1 (13) Внутри объекта структурычлены полей и единицы, в которых находятся битовые поля, имеют адреса, которые увеличиваются в порядке их объявления.

2 голосов
/ 01 июня 2011

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

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