ARMCC. Структуры, обьединение, перечисление, и битовые поля в ARM C и C++

Рассматривается реализация структурных типов данных union, enum, и struct. Так же рассматривается заполнение структур и реализацию битового поля.

Unions (обьединение)

Когда используется доступ к полю union другого типа, результат может быть предсказан на основе исходного типа.

Enumerations (перечисление)

Обьект enum реализуется наименьшим числовым типом который содержит диапазон перечисления enum.
В C режиме, и в C++ режиме без --enum_is_int, если enum  перечисление содержит только положительные значения перечислителя, тип хранения перечисления является первым беззнаковым типом из следующего списка в соответствии с диапазоном перечислений в перечислении enum.В других режимах, и в случаях когда enum содержит отрицательные значения перечислителя,тип хранения enum является первым из следующего, согласно диапазону перечисления в перечислителе enum:
  • unsigned char если не используется --enum_is_int
  • signed char если не используется --enum_is_int
  • unsigned short если не используется --enum_is_int
  • signed short если не используется --enum_is_int
  • signed int
  • unsigned int кроме Си  с --strict
  • signed long long кроме Си  с --strict
  • unsigned long long кроме Си  с --strict.

Примечание

  • В RVCT 4.0, тип хранилища enum являющийся первым беззнаковым типом из списка применяется только в режиме GNU (--gnu).
  • В ARM® Compiler 4.1 и выше, тип хранения перечисления enum тип хранения перечисления, являющийся первым беззнаковым типом из списка, применяется независимо от режима.
Реализация enum таким образом может уменьшать размер данных. Опция командной строки --enum_is_int lделает основной тип enum наименьшой шириной  int.
See the description of C language mappings in the Procedure Call Standard for the ARM® Architecture specification for more information.

Примечание

Care must be taken when mixing translation units that have been compiled with and without the --enum_is_int option, and that share interfaces or data structures.

In strict C, enumerator values must be representable as ints. That is, they must be in the range -2147483648 to +2147483647, inclusive. A warning is issued for out-of-range enumerator values:
#66: enumeration value is out of "int" range
Such values are treated the same way as in C++, that is, they are treated as unsigned
int
, long long, or unsigned long
long
.
To ensure that out-of-range Warnings are reported, use the following command to change them into Errors:
armcc --diag_error=66 ...

Structures (структуры)

Следующие пункты относятся к :
  • все структуры Си
  • все C++ структуры и классы не использующие виртуальные или базовые классы.
Структуры могут содержать заполнение для правильного выравнивания полей и корректного выравнивания структуры.
На графике 1 показан пример классической неупакованной структуры.
Байты 1, 2, и 3 заполняются чтобы обеспечить правильное выравнивание поля.
Байты 11 и 12 заполняются чтобы обеспечить правильное выравнивание структуры.
Функция sizeof() возвращает размер структуры включающая заполнение.
График 1 Пример обычной неупакованной структуры
Conventional nonpacked structure example

 

Компилируются структуры одним из способов в зависимости от ее определения:
  • Структуры которые определены как static или  extern заполняются нулями.
  • Структуры в стеке или куче, например которые определены с помощью malloc() или auto, заполняются тем что хранилось в памяти ранее. Вы не можете использовать функцию memcmp() для сравнения содержимого структур определенных таким образом.
Используйте опцию --remarks для просмотра сообщений генерируемых компилятором когда он вставляет дополнения в структуру struct.
Структуры с пустой инициализацией разрешены в  C++:
struct
{
    int x;
} X = { };
Однако, если вы компилируете C или C++ с параметрами — cpp и — c90, генерируется ошибка.

Bitfields (битовое поле)

В неупакованных структурах, ARM компилятор выделяет битовые поля в контейнеры.
Контейнер как корректно выравненный обьект декларируемого типа.
Битовые поля распределяются таким образом, чтобы указанное первое поле занимало младшие биты слова в зависимости от конфигурации::
Little-endian
Самые низкие адресации являются наименее значимыми.
Big-endian
Самые низкие адресации являются наиболее значимыми.
Контейнер битового поля может быть любым целым типом.

Примечание

In strict 1990 ISO Standard C, the only types permitted for a bit field are int, signed int, and unsigned int. For non-int bitfields, the compiler displays an error.

В строгом стандарте ISO стандартного стандарта 1990 года единственными типами, разрешенными для битового  поля, являются int, signed int, и unsigned int. Для не —int битового поля компилятор отображает ошибку.

Простое битовое поле, обьявленное без signed или unsigned квалификаторов, рассматривается как unsigned. Для примера, int x:10 выделяется как целое число  без знака  размером 10 бит.
Битовое поле присваивается первому контейнеру правильного типа, который имеет достаточное количество нераспределенных битов, например:
struct X
{
    int x:10;
    int y:20;
};
В первом объявлении создается целочисленный контейнер и выделяется 10 бит в x. Во втором объявлении компилятор находит существующий целочисленный контейнер с достаточным количеством нераспределенных битов и выделяет y в том же контейнере, что и x.
Битовое поле полностью содержится в контейнере. Битовое поле, которое не помещается в контейнер, помещается в следующий контейнер того же типа. Например, декларация z переполняет контейнер, если для структуры объявлено дополнительное битовое поле:
struct X
{
    int x:10;
    int y:20;
    int z:5;
};

Компилятор заполняет оставшиеся два бита для первого контейнера и назначает новый целочисленный контейнер для z.

Контейнеры битого поля могут перекрывать друг друга, например:

struct X
{
    int x:10;
    char y:2;
};
Первое объявление создает целочисленный контейнер и выделяет 10 бит в x. Эти 10 бит занимают первый байт и два бита второго байта целочисленного контейнера. Во втором объявлении компилятор проверяет контейнер типа char. Не существует подходящего контейнера, поэтому компилятор выделяет новый правильно выровненный контейнерchar.
Поскольку естественное выравнивание символа равно 1, компилятор выполняет поиск первого байта, который содержит достаточное количество нераспределенных битов, чтобы полностью содержать бит. В структуре примера второй байт контейнера int имеет два бита, выделенных для x, и 6 бит нераспределены. Компилятор выделяет контейнер char  начиная со второго байта предыдущего int контейнера, пропускает первые два бита, которые выделены для x, и выделяет два бита в y.
Если y объявлен char y: 8, компилятор заполняет второй байт и выделяет новый контейнер символов в третий байт, потому что битовое поле не может переполнять его контейнер. На следующем рисунке показано распределение битового поля для следующего примера структуры:
struct X
{
    int x:10;
    char y:8;
};
Распределение битового поля
Bitfield allocation 1

 

Примечение

Те же основные правила применяются к объявлениям битового поля с разными типами контейнеров. Например, добавим битовое полеint к структуре и она примет следующий вид:
struct X
{
    int x:10;
    char y:8;
    int z:5;
}
Компилятор выделяет контейнер int, начинающийся в том же месте, что и контейнер int x:10, и выделяет байт-выровненный char и 5-битное битовое поле как показано ниже:
Распределение битового поля
Bitfield allocation 2

 

Вы можете явно помещать контейнер битового поля, объявляя неопределенное битовое поле размером 0. Битовое поле нулевого размера заполняет контейнер до конца, если контейнер не пуст. Последующее объявление битового поля запускает новый пустой контейнер.

Примечание

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

Packing and alignment of bitfields (упаковка и выравнивание битовых полей)

 

Использование __attribute__((aligned(n))) делает битовое поле n-байт выравненным, не только его контейнер, but the bitfield is aligned to the packed alignment at most. It is ignored on bitfields in __packed and __attribute__((packed)) structs.
The alignment of a bitfield member’s container is the same as the alignment of that bitfield member. The size of a bitfield container is the least multiple of the alignment that fully covers the bitfield, but no larger than the size of the container-type. The following code examples show this:
#pragma pack(2)
/* Контейнер b должен начинаться с границы выравнивания 2 байта и должен 
* иметь размер не больше, чем тип контейнера, в этом случае размер 
* короткий. Контейнер b не может начинаться со смещения 0 (перекрывается с a) 
*, так как бит b будет начинаться со смещения 2 и не будет полностью лежать 
* внутри контейнера. Поэтому контейнер для b должен начинаться со смещения 
* 2.
*
* Data layout:      0x11 0x00 0x22 0x22
* Container layout:| a  |    |    b    |   
*/
struct { 
  char a;
  short b : 16;
} var1 = { 0x11, 0x2222 };
  
      
/* контейнер b может быть до 4 байт. Его размер должен быть 2 или 4 байта, так как они являются кратны*  ми выравниванию, которые не больше размера контейнера. При использовании 4 байтового контейнера 
*, начинающегося с 0, битовое поле b может начинаться со смещения 1 и полностью лежать 
*  внутри контейнера.
*
* Data layout:      0x11 0x22 0x22 0x00
* Container layout:| a  |
*                  |         b         |
*/
struct { 
  char a;
  int b : 16;
} var2 = { 0x11, 0x2222 };
Упакованные контейнеры битовых полей, включая все контейнеры битовых полей в упакованных структурах, имеют выравнивание 1. Поэтому максимальное битовое дополнение, вставленное для выравнивания упакованного контейнера битовых полей, составляет 7 бит.
Для неупакованного контейнера битового поля, максимальное заполнение битов 8*sizeof(container-type)-1.
Заполнение хвоста всегда вставляется в структуру по мере необходимости, чтобы гарантировать, что массивы структуры правильно выровнены.
A packed bitfield container is only large enough (in bytes) to hold the bitfield that declared it. Non-packed bitfield containers are the size of their type.
Следующие примеры иллюстрируют эти взаимодействия.
struct A {          int z:17; }; // sizeof(A) = 4, alignment = 4
struct A { __packed int z:17; }; // sizeof(A) = 3, alignment = 1
__packed struct A { int z:17; }; // sizeof(A) = 3, alignment = 1
struct A { char y:1;          int z:31; }; // sizeof(A) = 4, alignment = 4
struct A { char y:1; __packed int z:31; }; // sizeof(A) = 4, alignment = 1
__packed struct A { char y:1; int z:31; }; // sizeof(A) = 4, alignment = 1
struct A { char y:1;          int z:32; }; // sizeof(A) = 8, alignment = 4
struct A { char y:1; __packed int z:32; }; // sizeof(A) = 5, alignment = 1
__packed struct A { char y:1; int z:32; }; // sizeof(A) = 5, alignment = 1
struct A { int x; char y:1;          int z:31; };  // sizeof(A) = 8, alignment = 4
struct A { int x; char y:1; __packed int z:31; };  // sizeof(A) = 8, alignment = 4
__packed struct A { int x; char y:1; int z:31; };  // sizeof(A) = 8, alignment = 1
struct A { int x; char y:1;          int z:32; };  // sizeof(A) = 12, alignment = 4 [1]
struct A { int x; char y:1; __packed int z:32; };  // sizeof(A) = 12, alignment = 4 [2]
__packed struct A { int x; char y:1; int z:32; };  // sizeof(A) = 9, alignment = 1
Note that [1] and [2] are not identical; the location of z within the structure and the tail-padding differ.
struct example1
{
int a : 8;  /* 4-byte container at offset 0 */
__packed int b : 8;  /* 1-byte container at offset 1 */
__packed int c : 24; /* 3-byte container at offset 2 */
}; /* Total size 8 (3 bytes tail padding) */;
struct example2
{
__packed int a : 8; /* 1-byte container at offset 0 */
__packed int b : 8; /* 1-byte container at offset 1 */
int c : 8; /* 4-byte container at offset 0 */
}; /* Total size 4 (No tail padding) */
struct example3
{
int a : 8;  /* 4-byte container at offset 0 */
__packed int b : 32; /* 4-byte container at offset 1 */
__packed int c : 32; /* 4-byte container at offset 5 */
int d : 16; /* 4-byte container at offset 8 */
int e : 16; /* 4-byte container at offset 12 */
int f : 16; /* In previous container */
}; /* Total size 16 (No tail padding) */

Оставить ответ