В структуре C почему packaged,aligned выполняет заполнение?

Почему packed,aligned в структуре C выполняет заполнение?

Мне нужно прочитать кучу байтов MSB/LSB с удаленного устройства с помощью i2c. Поскольку все данные устройства представляют собой байты, я использую uint8_t, который представляет ровно 8 бит, то есть 1 байт.

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

Затем я добавил aligned для производительности (т. е. разрешил процессору читать структуру с минимальным доступом к памяти).

Как объясняется здесь Заполнение и упаковка структуры, заполнение выравнивает элементы структуры по «естественным» границам адреса, а упаковка предотвращает заполнение.

Этот простой тест меня удивил:

$ gcc --version
gcc (Debian 10.2.1-6) 10.2.1 20210110

$ cat dumb.c 
#include <stdio.h>
#include <stdint.h> // uint8_t

struct struct1 {
  uint8_t msb;
} __attribute__((packed));

struct struct2 {
  uint8_t msb;
} __attribute__((packed,aligned(4)));

struct struct3 {
  uint8_t msb;
  uint8_t lsb;
} __attribute__((packed));

struct struct4 {
  uint8_t msb;
  uint8_t lsb;
} __attribute__((packed,aligned(4)));

int main(void) {
  struct struct1 s1;
  printf("sizeof(s1) %zu\n", sizeof(s1));
  struct struct2 s2;
  printf("sizeof(s2) %zu\n", sizeof(s2));
  struct struct3 s3;
  printf("sizeof(s3) %zu\n", sizeof(s3));
  struct struct4 s4;
  printf("sizeof(s4) %zu\n", sizeof(s4));
  return 0;
}

$ gcc -o dumb dumb.c

$ ./dumb 
sizeof(s1) 1
sizeof(s2) 4
sizeof(s3) 2
sizeof(s4) 4

aligned кажется, «дополняет конец структуры»: ожидается ли это?

  • Я ожидал, что s2 начнется с адреса памяти, кратного 4 байтам, но будет иметь размер 1.
  • Я ожидал, что s4 начнется с адреса памяти, кратного 4 байтам, но будет иметь размер 2.

Я читаю эти байты с удаленного устройства, поэтому каждый байт передается с помощью i2c с устройства на ПК: теперь для производительности лучше пойти packed только во избежание «дополнительного заполнения в конце», или лучше пойти packed,aligned перенос и чтение дополнительных неиспользованных байтов с «дополненными в конце» каждый раз при передаче?

Я думаю, вы неверно истолковываете, что делает aligned.

Marcus Müller 22.08.2024 18:50

«Я ожидал, что s2 начнется с адреса памяти, кратного 4 байтам, но будет иметь размер 1.»: Как бы вы ожидали, что struct struct2 s2[2]; будет работать тогда? Я думаю, вам нужен атрибут aligned в объявлении переменной, а не в ее типе.

user17732522 22.08.2024 19:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
2
54
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Атрибут aligned в объявлении struct означает, что любой экземпляр структуры должен начинаться с выровненного адреса.

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

Хотя атрибут packed минимизирует любые отступы, используемые в структуре, такие отступы не обязательно могут быть полностью устранены при использовании вместе с aligned.

Например, если у вас было это объявление:

struct struct2 s2[2];

Если бы у вас не было завершающего отступа, то s2[1] не было бы выровнено должным образом.

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

fghoussen 22.08.2024 19:09

@fgoussen, packed и aligned вместе могут дать вам другой макет, чем любой из них по отдельности. В частности, я ожидаю, что он обычно будет давать все отступы в конце, без каких-либо между членами, в тех случаях, когда только aligned имеет внутренние отступы в некоторых местах, а packed не имеет их вообще. Но да, выравнивание ограничивает размер макета структуры, поэтому может потребоваться отступ.

John Bollinger 22.08.2024 20:24

packed говорит, что нужно упаковать элементы внутри структуры.

aligned говорит, что объект нужно выровнять, отступив в конце.

Почему packed,aligned в структуре C выполняет заполнение?

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

Мне нужно прочитать кучу байтов MSB/LSB с удаленного устройства с помощью i2c. Поскольку все данные устройства представляют собой байты, я использую uint8_t, который представляет ровно 8 бит, то есть 1 байт.

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

Возможно, более естественным способом сделать это было бы прочитать каждую из ваших пар MSB/LSB в один uint16_t, который, как вы можете быть уверены, не имеет внутреннего заполнения. И поскольку вы описываете пары таким образом, я полагаю, вы можете интерпретировать пары как 16-битные числа, чего затем можно легко достичь с помощью функции ntohs().

Затем я добавил выравнивание для повышения производительности (т. е. разрешил процессору читать структуру с минимальным доступом к памяти).

Очень вероятно, что это преждевременная оптимизация. Любая задержка, вызванная несовпадением, скорее всего, будет полностью перегружена издержками ввода-вывода.

заполнение выравнивает элементы структуры по «естественным» границам адресов,

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

и упаковка предотвращает набивку.

Не совсем. Точное значение директив управления упаковкой варьируется в зависимости от реализации C, но вы используете GCC, чье определение таково:

packed
Этот атрибут, прикрепленный к определению типа struct, union или C++ class, указывает, что каждый из его членов [...] размещается так, чтобы минимизировать требуемую память.

более того,

Указание этого атрибута для типов struct и union эквивалентно указанию атрибута packed для каждого члена структуры или объединения.

... в каком контексте,

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

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

aligned кажется, «дополняет конец структуры»: ожидается ли это?

Да, как уже обсуждалось. И это не противоречит определению packed.

  • Я ожидал, что s2 начнется с адреса памяти, кратного 4 байтам, но будет иметь размер 1.
  • Я ожидал, что s4 начнется с адреса памяти, кратного 4 байтам, но будет иметь размер 2.

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

  struct struct1 s2 __attribute__((aligned(4)));
  struct struct3 s4 __attribute__((aligned(4)));

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

Я читаю эти байты с удаленного устройства, поэтому каждый байт передается с помощью i2c с устройства на ПК: теперь для производительности лучше пойти packed только во избежание «дополнительного заполнения в конце», или лучше пойти packed,aligned перенос и чтение дополнительных неиспользованных байтов с «дополненными в конце» каждый раз при передаче?

Кто сказал, что вы будете читать что-либо в любом дополнении структуры? Вы не должны этого делать. Если вы читаете байты по одной паре за раз, вы все равно этого не сделаете. Если вы читаете последовательности из нескольких пар байтов за одну операцию, вы не можете использовать заполнение. Но опять же, похоже, вы все усложняете, чем нужно. Я бы пропустил структуры и использовал тип uint16_t для пар байтов. Особенно, если вы читаете многие из них вместе, и в этом случае вам следует использовать uint16_t[]. Предположим, вы хотите интерпретировать каждую пару как 16-битное число, обработайте каждое из них с помощью ntohs() после чтения, прежде чем делать что-либо еще.

Если результат недостаточно быстрый, профилируйте его, чтобы определить, где находится узкое место, и поработайте над этим.

Другие вопросы по теме