Что такое "упакованная" структура в 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;

Что значит упакованный?

6 ответов

Решение

Когда структуры определены, компилятору разрешается добавлять отступы (пробелы без фактических данных), чтобы члены попадали в границы адресов, которые легче доступны для ЦП.

Например, на 32-разрядном процессоре 32-разрядные элементы должны начинаться с адресов, кратных 4 байтам, для эффективного доступа (чтения и записи). Следующее определение структуры добавляет 16-битное заполнение между обоими элементами, так что второй элемент попадает в правильную границу адреса:

struct S {
    int16_t member1;
    int32_t member2;
};

Структура в памяти вышеупомянутой структуры в 32-битной архитектуре (~ = заполнение):

+---------+---------+
| m1 |~~~~|   m2    |
+---------+---------+

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

Та же самая структура, когда она упакована, появится в памяти как-то так:

+---------+---------+
| m1 |   m2    |~~~~
+---------+---------+

Он указывает компилятору не добавлять какие-либо отступы между членами struct,

Смотрите, например, эту страницу.

Позвольте мне объяснить концепцию заполнения в структурах, а затем упакованных структур на примере.

А потом давайте посмотрим, почему требуется упаковка.

Обивка:

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, что бесполезно.

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

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

Кроме того, некоторые машины не могут получить доступ к широким данным по невыровненному адресу, например, ядра Cortex-M0 не могут получить доступ к 32-разрядным данным по не 32-разрядному выровненному адресу, поэтому в таких случаях необходимо соблюдать осторожность при написании сетевого кода.

_attribute__((__packed__)) означает (наиболее вероятно) "не вставлять какие-либо отступы, чтобы сделать вещи быстрее", а также может означать "не вставлять какие-либо выравнивания, чтобы сохранить выравнивание".

При использовании упаковки во время объявления структуры компилятор не будет добавлять какие-либо отступы к членам одной и той же структуры. Ниже приведен пример кода и вывод, который говорит сам за себя.

      $ cat structure_packed.c
#include <stdio.h>

typedef struct __attribute__((__packed__))
{
        char a;
        int ai;
        char ac;
}A;

struct B
{
        char b;
        int bi;
        char bc;
};

int main()
{
         A a;
        struct B b;
        int c;
        printf("size of struct A: %lu, addr a: %p, addr ai: %p, addr ac: %p\n", sizeof(a), &(a.a), &(a.ai), &a.ac);
        printf("size of struct B: %lu, addr b: %p, addr bi: %p, addr bc: %p\n", sizeof(b), &(b.b), &(b.bi), &b.bc);
        printf("addr of c: %p\n", &c);
        return 0;
}

Компиляция

$ gcc структура_упакована.c -o структура_упакована

Выполнить|Вывод

$ ./структура_упакована

размер структуры A: 6, адрес a: 0x7ffc6f177ed6, адрес ai: 0x7ffc6f177ed7, адрес ac: 0x7ffc6f177edb

размер структуры B: 12, адрес b: 0x7ffc6f177edc, адрес bi: 0x7ffc6f177ee0, адрес bc: 0x7ffc6f177ee4

адрес c: 0x7ffc6f177ed0

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