| |||
| Home > ARM Compiler Reference > C and C++ implementation details > Structures, unions, enumerations, and bitfields | |||
This section describes the implementation of the structured data types union, enum, and struct. It also discusses structure padding and bitfield implementation.
For details of anonymous structures and unions, see Anonymous classes, structures and unions.
When a member of a union is accessed using a member of a different type, the resulting value can be predicted from the representation of the original type. No error is given.
An object of type enum is implemented in the smallest integral type that contains the range of the enum. The type of an enum is one of the following, according to the range of the enum:
unsigned char
signed char
unsigned short
signed short
unsigned int (C++ always, C except
when --strict is specified)
signed int.
Implementing enum in
this way can reduce data size. The command-line option --enum_is_int sets
the underlying type of enum to signed int.
The --enum_is_int option is not recommended
for general use and is not required for ISO-compatible source. Code
compiled with this option is not compliant with the ABI for
the ARM Architecture (base standard) [BSABI], and incorrect
use might result in a failure at runtime. This option is not supported
by the C++ libraries.
Unless you use the --strict option, enum declarations
can have a comma at the end as in:
enum { x = 1, };
In strict C, enumerator values must be representable as ints,
for example, they must be in the range -2147483648 to +2147483647
(inclusive). In previous releases of RVCT out-of-range values were
cast to int without a warning (unless you specified
the --strict option).
In RVCT v2.2, 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 ...
The following points apply to:
all C structures
all C++ structures and classes not using virtual functions or base classes.
The alignment of a non-packed structure is the maximum alignment required by any of its fields.
Structures are arranged with the first-named component at the lowest address. Fields are aligned as follows:
A field with a char type is aligned to the next available byte.
A field with a short type is aligned to the next even-addressed byte.
Bitfield alignment depends on how the bitfield is declared. See Bitfields in packed structures for more information.
All other types are aligned on word boundaries.
Structures can contain padding to ensure that fields are correctly
aligned and that the structure itself is correctly aligned. Figure tgcrf31 shows an
example of a conventional, non-packed structure. Bytes 1, 2, and
3 are padded to ensure correct field alignment. Bytes 11 and 12
are padded to ensure correct structure alignment. The sizeof() function returns
the size of the structure including padding.
The compiler pads structures in one of the following ways, according to how the structure is defined:
Structures that are defined as static or extern are padded with zeros.
Structures on the stack or heap, such as those defined
with malloc() or auto, are padded
with whatever was previously stored in those memory locations. You cannot
use memcmp() to compare padded structures defined
in this way (see Figure tgcrf31).
Use the --remarks option to view the messages
that are generated when the compiler inserts padding in a struct.
Structures with empty initializers are permitted in C++:
struct { int x; } X = { };
However, if you are compiling C, or compiling C++ with the --c90 options,
an error is generated, for example:
Error: #29: expected an expression
A packed structure is one where the alignment of the structure, and of the fields within it, is always 1.
Packed structures are defined with the __packed qualifier,
see Type qualifiers. There
is no command-line option to change the default packing of structures.
In non-packed structures, the ARM compiler allocates bitfields in containers. A container is a correctly aligned object of a declared type.
Bitfields are allocated so that the first field specified occupies the lowest-addressed bits of the word, depending on configuration:
Lowest addressed means least significant.
Lowest addressed means most significant. (On this product, only little-endian is supported.)
A bitfield container can be any of the integral types.
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 the following error:
Error: #230: nonstandard type for a bit field
A plain bitfield, declared without either signed or unsigned qualifiers,
is treated as unsigned. For example, int x:10 allocates
an unsigned integer of 10 bits.
A bitfield is allocated to the first container of the correct type that has a sufficient number of unallocated bits, for example:
struct X {
int x:10;
int y:20;
};
The first declaration creates an integer container and allocates
10 bits to x. At the second declaration, the
compiler finds the existing integer container with a sufficient
number of unallocated bits, and allocates y in
the same container as x.
A bitfield is wholly contained within its container. A bitfield that does not fit in a container is placed in the next container of the same type. For example, the declaration of z overflows the container if an additional bitfield is declared for the structure:
struct X {
int x:10;
int y:20;
int z:5;
};
The compiler pads the remaining two bits for the first container
and assigns a new integer container for z.
Bitfield containers can overlap each other, for example:
struct X {
int x:10;
char y:2;
};
The first declaration creates an integer container and allocates
10 bits to x. These 10 bits occupy the first
byte and two bits of the second byte of the integer container. At
the second declaration, the compiler checks for a container of type char.
There is no suitable container, so the compiler allocates a new
correctly aligned char container.
Because the natural alignment of char is 1, the
compiler searches for the first byte that contains a sufficient
number of unallocated bits to completely contain the bitfield. In
the example structure, the second byte of the int container
has two bits allocated to x, and six bits unallocated.
The compiler allocates a char container starting at
the second byte of the previous int container, skips
the first two bits that are allocated to x, and
allocates two bits to y.
If y is declared char y:8,
the compiler pads the second byte and allocates a new char container
to the third byte, because the bitfield cannot overflow its container.
The bitfield allocation for the following example structure is shown
in Figure 3.2:
struct X {
int x:10;
char y:8;
};
The same basic rules apply to bitfield declarations with different container types. For example, adding an int bitfield to the example structure gives:
struct X {
int x:10;
char y:8;
int z:5;
}
The compiler allocates an int container starting
at the same location as the int x:10 container
and allocates a byte-aligned char and 5-bit bitfield
(see Figure 3.3).
You can explicitly pad a bitfield container by declaring an unnamed bitfield of size zero. A bitfield of zero size fills the container up to the end if the container is not empty. A subsequent bitfield declaration starts a new empty container.