Когда программирование в CI показало, что упаковывать структуры, используя __attribute__((__packed__))
атрибут GCCs, неоценимо, я могу легко преобразовать структурированный кусок энергозависимой памяти в массив байтов, который будет передаваться по шине, сохраняться в хранилище или применяться к блоку регистров. Упакованные структуры гарантируют, что при обработке в виде массива байтов он не будет содержать никаких дополнений, что является расточительным, возможным риском для безопасности и, возможно, несовместимым при взаимодействии с аппаратным обеспечением.
Нет ли стандарта для структур упаковки, который работает во всех компиляторах Си? Если нет, то я выдаюсь, думая, что это критическая особенность для системного программирования? Разве ранние пользователи языка C не находили необходимости в упаковочных структурах или есть какая-то альтернатива?
источник
Ответы:
В структуре важно смещение каждого члена от адреса каждого экземпляра структуры. Вопрос не в том, насколько плотно упакованы вещи.
Массив, однако, имеет значение в том, как он «упакован». Правило в C состоит в том, что каждый элемент массива составляет ровно N байтов от предыдущего, где N - это количество байтов, используемых для хранения этого типа.
Но в структуре нет необходимости в единообразии.
Вот один из примеров странной схемы упаковки:
Freescale (производящий автомобильные микроконтроллеры) создает микропроцессор с сопроцессором Time Processing Unit (Google для eTPU или TPU). Он имеет два собственных размера данных, 8 бит и 24 бита, и имеет дело только с целыми числами.
Эта структура:
будет видеть, что каждый U24 хранит свой собственный 32-битный блок, но только в области самого высокого адреса.
Эта:
будет иметь два U24s хранится в смежных блоках 32 - битных и U8 будет сохранен в «дыре» в передней части первого U24,
elementA
.Но вы можете сказать компилятору упаковать все в свой 32-битный блок, если хотите; это дороже в оперативной памяти, но использует меньше инструкций для доступа.
«упаковка» не означает «плотно упаковывать» - это просто означает некоторую схему размещения элементов структуры по смещению.
Универсальной схемы не существует, она зависит от компилятора + архитектуры.
источник
struct b
перемещениеelementC
перед любым другим элементом, то это не соответствующий компилятору Си. Перестановка элементов не допускается в CПоскольку вы упоминаете
__attribute__((__packed__))
, я предполагаю, что вы намерены устранить все отступы внутриstruct
(сделать так, чтобы каждый элемент имел выравнивание в 1 байт).... И ответ нет". Заполнение и выравнивание данных относительно структуры (и смежных массивов структур в стеке или куче) существуют по важной причине. На многих компьютерах доступ к памяти без выравнивания может привести к потенциально значительному снижению производительности (хотя на некоторых более новых аппаратных средствах его станет меньше). В некоторых редких случаях неправильное выравнивание доступа к памяти приводит к ошибке шины, которая не может быть исправлена (может даже привести к сбою всей операционной системы).
Поскольку стандарт C ориентирован на переносимость, не имеет особого смысла иметь стандартный способ устранить все дополнения в структуре и просто разрешить произвольное выравнивание полей, поскольку это может привести к тому, что код C станет непереносимым.
Самый безопасный и самый переносимый способ вывода таких данных во внешний источник таким образом, чтобы исключить все заполнение, - это сериализовать в / из байтовых потоков, а не просто пытаться пересылать необработанное содержимое вашей памяти
structs
. Это также предотвратит снижение производительности вашей программы за пределами этого контекста сериализации, а также позволит вам свободно добавлять новые поля вstruct
файл, не выбрасывая и не сбивая все программное обеспечение. Это также даст вам возможность заняться порядком байтов и тому подобными вещами, если это когда-нибудь станет проблемой.Существует один способ устранить все отступы, не обращаясь к директивам, специфичным для компилятора, хотя он применим только в том случае, если относительный порядок между полями не имеет значения. Учитывая что-то вроде этого:
... нам нужен отступ для выравниваемого доступа к памяти относительно адреса структуры, содержащей эти поля, например:
... где
.
указывает на отступы. Каждыйx
должен соответствовать 8-байтовой границе для производительности (и иногда даже правильного поведения).Вы можете устранить заполнение переносимым способом, используя представление SoA (структура массива) следующим образом (предположим, нам нужно 8
Foo
экземпляров):Мы фактически разрушили структуру. В этом случае представление памяти становится таким:
... и это:
... не нужно больше накладных расходов и без вовлечения несогласованного доступа к памяти, поскольку мы больше не обращаемся к этим полям данных как к смещению структурного адреса, а как к смещению базового адреса для того, что фактически является массивом.
Это также дает преимущество более быстрого последовательного доступа благодаря меньшему потреблению данных (больше не нужно неуместного заполнения в миксе для замедления релевантной скорости использования данных машиной), а также способности компилятора очень просто векторизовать обработку ,
Недостатком является то, что это код PITA. Это также потенциально менее эффективно для произвольного доступа с большим шагом между полями, где часто повторения AoS или AoSoA будут лучше. Но это один из стандартных способов устранить набивку и упаковать вещи настолько плотно, насколько это возможно, не прибегая к выравниванию всего.
источник
Не все архитектуры одинаковы, просто включите 32-битную опцию на одном модуле и посмотрите, что происходит при использовании одного и того же исходного кода и одного и того же компилятора. Порядок байтов является еще одним хорошо известным ограничением. Бросьте представление с плавающей запятой, и проблемы усугубятся. Использование Packing для отправки двоичных данных непереносимо. Чтобы стандартизировать его так, чтобы он был практически применим, вам нужно будет переопределить спецификацию языка Си.
Хотя использование Pack для отправки двоичных данных является обычным явлением, это плохая идея, если вы хотите обеспечить безопасность, переносимость или долговечность данных. Как часто вы читаете двоичный двоичный объект из источника в вашу программу? Как часто вы проверяете, что все значения вменяемы, что хакер или изменение программы не «получили» данные? К тому времени, когда вы закодировали подпрограмму проверки, вы могли бы также использовать подпрограммы импорта и экспорта.
источник
Очень распространенной альтернативой является «именованное заполнение»:
Это делает предполагать , что структура не будет дополнена до 8 байт.
источник