Что такое "упакованная" структура в C?
Я собираюсь, хотя некоторый код C написан для компилятора Microchip C30, и я часто вижу структуры, которые определены следующим образом:
typedef struct __attribute__((__packed__))
{
IP_ADDR MyIPAddr; // IP address
IP_ADDR MyMask; // Subnet mask
IP_ADDR MyGateway; // Default Gateway
// etc...
} APP_CONFIG;
Что означает упакованный?
Ответы
Ответ 1
Когда структуры определены, компилятору разрешено добавлять paddings (пробелы без фактических данных), чтобы члены попадали в границы адресов, которые легче получить для CPU.
Например, на 32-битном процессоре 32-разрядные члены должны запускаться с адресами, кратными 4 байтам, для обеспечения эффективного доступа (чтение и запись). Следующее определение структуры добавляет 16-разрядное дополнение между обоими членами, так что второй член попадает на соответствующую границу адреса:
struct S {
int16_t member1;
int32_t member2;
};
Структура в памяти вышеуказанной структуры в 32-битной архитектуре ( ~= заполнение):
+---------+---------+
| m1 |~~~~| m2 |
+---------+---------+
Когда структура упакована, эти прокладки не вставлены. Компилятор должен генерировать больше кода (который работает медленнее) для извлечения неприсоединившихся элементов данных, а также для их записи.
Такая же структура, когда она упакована, появится в памяти как-то вроде:
+---------+---------+
| m1 | m2 |~~~~
+---------+---------+
Ответ 2
Он инструктирует компилятор не добавлять никаких дополнений между членами struct
.
См., например, эту страницу.
Ответ 3
_attribute__((__packed__))
означает (скорее всего) "не вставлять никаких дополнений, чтобы ускорить работу", а также может означать "не вставлять выравнивания для сохранения выравнивания".
Ответ 4
Позвольте мне объяснить концепцию заполнения в структурах, а затем упакованных структур на примере.
А потом давайте посмотрим, почему требуется упаковка.
Обивка:
struct eg_struct
{
unsigned char abc;
unsigned int xyz;
}
Когда структура объявлена как выше для 16-битной архитектуры, переменной abc
будет назначен некоторый адрес. Следующий адрес не назначен переменной xyz
, вместо этого добавляется один дополнительный байт, а затем следующий адрес будет назначен переменной xyz
.
В итоге структура выглядит примерно так:
struct eg_struct
{
unsigned char abc;
unsigned char paddedbytes[1];
unsigned int xyz;
}
Заполнение делает адреса переменных-членов легко доступными для микроконтроллера. Недостатком являются лишние ненужные байты, которые входят в картинку.
Упаковка:
Если та же структура объявлена с использованием атрибута " packed
", дополнительный байт не будет добавлен после переменной abc
.
Позвольте мне привести один пример, где необходима упаковка:
Рассмотрим микроконтроллер с интерфейсом EEPROM, в котором хранится некоторая структура.
Представьте, что запись функции в EEPROM будет выглядеть следующим образом:
Write_EEPROM(EEPROM address, Ram address, Byte count);
Теперь, если упаковка не завершена, дополнительные заполненные байты будут занимать место в EEPROM, что бесполезно.
Ответ 5
Одна вещь, которая не была явно вызвана, - то, что упаковка обычно делается, чтобы соответствовать предопределенным структурам поля. Например, на низкоуровневом уровне сетевого интерфейса между сетевыми машинами происходит обмен серией байтов. После получения данных их необходимо сопоставить с высокоуровневой структурой, чтобы можно было легко манипулировать данными. Это когда обычно не требуется заполнения, чтобы структура напрямую отображалась в байтах.
Обмен сетевыми данными также связан с проблемой байтовой байтовости (т.е. Почти все сетевые данные используют формат байтов с прямым порядком байтов, независимо от порядкового номера машин источника и назначения).
Кроме того, некоторые машины не могут получить доступ к широким данным по невыровненному адресу, например, ядра Cortex-M0 не могут получить доступ к 32-разрядным данным по не 32-разрядному выровненному адресу, поэтому в таких случаях необходимо соблюдать осторожность при написании сетевого кода.