Intereting Posts
Измерение использования ЦП на металлической системе Обнаружение конечности разглаживающий тип-караульный указатель нарушит правила строгого сглаживания Массив указателей и массив элементов Изменение размера терминала и прокрутка с помощью ncurses строковый массив с символом мусора в конце Получить IP-адрес моего компьютера в локальной сети с помощью сокетов BSD? Перемешивание массива при создании каждого индекса имеет ту же вероятность быть в любом индексе Установка неизменяемого флага с использованием ioctl () в C создавая pthreads в цикле и передавая приращенные аргументы, streamи не используют правильные значения аргументов Вставка слова из текстового файла в дерево в C Как создать легковесную песочницу C кода? C: переходные (двойные) задания С помощью C заменить первую половину файла на вторую и усекать? Как у malloc и calloc оказались разные подписи?

Строгое сглаживание и запись int через char *

В старой программе я сериализовал структуру данных в байтах, выделив массив unsigned char, а затем преобразованный ints:

*((*int)p) = value; 

(где punsigned char* , а value – значение, которое нужно сохранить).

Это отлично работало, за исключением случаев, когда скомпилировано на Sparc, где оно вызывало исключения из-за доступа к памяти с неправильным выравниванием. Это имело смысл, потому что элементы данных имели разные размеры, поэтому p быстро стал неравнозначным и вызвал ошибку при использовании для хранения значения int, где основные инструкции Sparc требуют выравнивания.

Это было быстро исправлено (путем выписывания значения в байтовый байт-байт). Но я немного обеспокоен этим, потому что я использовал эту конструкцию во многих программах на протяжении многих лет без проблем. Но ясно, что я нарушаю какое-то правило C (строгое сглаживание?), И в то время как этот случай был легко обнаружен, возможно, нарушения могут привести к тому, что другие типы неопределенного поведения более тонкие из-за оптимизации компиляторов и т. Д. Я также немного озадачен, Я считаю, что за эти годы я видел такие конструкции в большом количестве кода на C. Я думаю об аппаратных драйверах, которые описывают структуру данных, обмениваемую аппаратным обеспечением как структуры (например, с помощью пакета (1)), и записывая их в h / w-регистры и т. Д. Таким образом, это, по-видимому, обычная техника.

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

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

    Да, ваш код нарушает строгое правило псевдонимов . В C только char* а его signed и unsigned аналоги считаются псевдонимами других типов.

    Таким образом, правильный способ выполнить такую ​​сериализацию сериализации – создать массив в ints , а затем обработать его как unsigned char buffer.

     int arr[] = { 1, 2, 3, 4, 5 }; unsigned char* rawData = (unsigned char*)arr; 

    Вы можете memcpy , fwrite или сделать сериализацию rawData , и это абсолютно верно.

    Код десериализации может выглядеть так:

     int* arr = (int*)calloc(5, sizeof(int)); memcpy(arr, rawData, 5 * sizeof(int)); 

    Конечно, вы должны заботиться о endianness , padding и других проблемах для реализации надежной сериализации.

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

    На платформах, где ваши типы элементов требуют определенного выравнивания, в конструкцию добавляются байты заполнения (что равно выражению, которое я сделал выше, sizeof (struct Foo)> = сумма его размеров элементов данных). Прокладка …

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

    То, что вы не можете предположить, состоит в том, что вы можете накладывать меньшие типы (например, unsigned char ) на «более крупные типы» (например, unsigned int ) и memcpy между ними в этом направлении, потому что unsigned int может требовать правильного выравнивания на этой целевой платформе. Обычно, если вы делаете это неправильно, вы видите ошибки шины или аналогичные.

    malloc() в наиболее общем случае – это общий способ получения кучи памяти для любого типа данных. Будь это байтовый массив или какая-либо структура, независимо от требований к выравниванию. Нет существующей системы, где вы не можете struct Foo *ps = malloc(sizeof(struct Foo)) . На платформах, где выравнивание имеет жизненно важное значение, malloc не будет возвращать несогласованные адреса, поскольку он сломает любой код, пытаясь выделить память для структуры. Поскольку malloc() не является экстрасенсом, он также будет возвращать указатели, совместимые с конструкцией, если вы используете его для распределения байтовых массивов.

    Любая форма сериализации «ad hoc», такая как запись всей структуры, является только многообещающим подходом, если вам не нужно обменивать сериализованные данные с другими машинами или другими приложениями (или будущими версиями того же приложения, где кто-то мог бы использовать настройки компилятора , связанные с выравниванием).

    Если вы ищете портативное и надежное и надежное решение, вам следует рассмотреть возможность использования одного из пакетов сериализации основного streamа, одним из которых является вышеупомянутый буферов протокола Google.