Демпинг структуры в C - PullRequest
2 голосов
/ 13 мая 2011

Является ли хорошей идеей просто сбросить структуру в двоичный файл с помощью fwrite? * 1001 например *

  struct Foo {
     char name[100]; 
     double f;
     int bar; 
  } data;

   fwrite(&data,sizeof(data),1,fout);

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

У меня есть друг, утверждающий, что это очень распространено .... на практике. Это правда ???

Редактировать: Каков рекомендуемый способ записи переносимого двоичного файла? Используете какую-то библиотеку? Мне интересно, как это достигается тоже. (Указывая порядок байтов, размеры, ..?)

Ответы [ 4 ]

3 голосов
/ 13 мая 2011

Критически важно, хотите ли вы, чтобы файл был переносимым, или просто код.

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

Если файл нужно передавать между разными платформами, то это довольно плохая идея, потому что вы в конечном итоге случайно определяете формат файла как что-то вроде «что бы MSVC на Win32 не записывал». Это может оказаться довольно неудобным для чтения и записи на какой-либо другой платформе, и, конечно, код, который вы написали в первую очередь, не будет делать это при работе на другой платформе с несовместимым представлением структуры хранения.

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

  1. Не. Используйте текстовый формат. Будьте готовы потерять некоторую точность в значениях с плавающей точкой.
  2. Используйте библиотеку, хотя здесь есть какое-то проклятье. Вы можете подумать, что ASN.1 выглядит хорошо, и это до тех пор, пока вам никогда не придется манипулировать этим материалом самостоятельно. Я предполагаю, что буфер протокола Google довольно хорош, но я никогда не использовал его сам.
  3. Определите довольно простой двоичный формат с точки зрения того, что означает каждый unsigned char в свою очередь. Это хорошо для символов [*] и других целых чисел, но становится немного хитрым для типов с плавающей точкой. «Это представление с прямым порядком байтов в формате IEEE-754», если все целевые платформы используют плавающие объекты IEEE, все будет в порядке. Что я ожидаю, что они делают, но вы должны сделать ставку на это. Затем соберите эту последовательность символов для записи и интерпретируйте ее для чтения: если вам «повезло», то на данной платформе вы можете написать определение структуры, которое точно соответствует ей, и использовать этот прием. В противном случае делайте любые манипуляции с байтами, которые вам нужны. Если вы хотите быть действительно переносимым, будьте осторожны, чтобы не использовать int во всем коде для представления значения, взятого из bar, потому что если вы сделаете это на какой-то платформе, где int равен 16 битам, это не будет поместиться. Вместо этого используйте long или int_least32_t или что-то еще, и проверьте границы при записи. Или используйте uint32_t и дайте ему завернуться.

[*] До тех пор, пока вы не нажмете на машину EBCDIC. Не то чтобы кто-то серьезно ожидал, что ваши файлы будут переносимы на компьютер, на котором простые текстовые файлы тоже не переносимы.

3 голосов
/ 13 мая 2011

Это, безусловно, очень плохая идея по двум причинам:

  • один и тот же struct может иметь разные размеры на разных платформах из-за проблем с выравниванием и настроением компилятора
  • элементы struct могут иметь разные представления на разных машинах (например, big-endian / little-endian, IEE754 против некоторых других вещей, sizeof(int) на разных платформах)
1 голос
/ 13 мая 2011

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

1 голос
/ 13 мая 2011

Как вы любите звонить посреди ночи?Либо используйте #pragma, чтобы упаковать их, либо запишите их переменные за переменной.

...