| |||
| Home > Compiler Coding Practices > Detailed comparison of an unpacked struct, a __packed struct, and a struct with individually __packed fields | |||
The differences between not packing a struct, packing an entire struct, and packing individual fields of a struct are illustrated by the three implementations of a struct shown in Table 17.
In the first implementation, the struct is not
packed. In the second implementation, the entire structure is qualified
as __packed. In the third implementation, the __packed attribute
is removed from the structure and the individual field that is not
naturally aligned is declared as __packed.
Table 17. C code for an unpacked struct, a packed struct, and a struct with individually packed fields
| Unpacked struct | __packed struct | __packed fields |
|---|---|---|
struct foo
{
char one;
short two;
char three;
int four;
} c;
|
__packed struct foo
{
char one;
short two;
char three;
int four;
} c;
|
struct foo
{
char one;
__packed short two;
char three;
int four;
} c;
|
Table 18 shows
the corresponding disassembly of the machine code produced by the
compiler for each of the sample implementations of Table 17, where the C code
for each implementation has been compiled using the option -O2.
The -Ospace and -Otime compiler
options control whether accesses to unaligned elements are made
inline or through a function call. Using -Otime results
in inline unaligned accesses. Using -Ospace results
in unaligned accesses made through function calls.
Table 18. Disassembly for an unpacked struct, a packed struct, and a struct with individually packed fields
| Unpacked struct | __packed struct | __packed fields |
|---|---|---|
; r0 contains address of c ; char one LDRB r1, [r0, #0] ; short two LDRSH r2, [r0, #2] ; char three LDRB r3, [r0, #4] ; int four LDR r12, [r0, #8] | ; r0 contains address of c ; char one LDRB r1, [r0, #0] ; short two LDRB r2, [r0, #1] LDRSB r12, [r0, #2] ORR r2, r12, r2, LSL #8 ; char three LDRB r3, [r0, #3] ; int four ADD r0, r0, #4 BL __aeabi_uread4 | ; r0 contains address of c ; char one LDRB r1, [r0, #0] ; short two LDRB r2, [r0, #1] LDRSB r12, [r0, #2] ORR r2, r12, r2, LSL #8 ; char three LDRB r3, [r0, #3] ; int four LDR r12, [r0, #4] |
In the disassembly of the unpacked struct in Table 18, the compiler always accesses data on aligned word or halfword addresses. The compiler is able to do this because the struct is padded so that every member of the struct lies on its natural size boundary.
In the disassembly of the __packed struct in Table 18, fields one and three are
aligned on their natural size boundaries by default, so the compiler
makes aligned accesses. The compiler always carries out aligned
word or halfword accesses for fields it can identify as being aligned. For
the unaligned field two, the compiler uses multiple
aligned memory accesses (LDR/STR/LDM/STM),
combined with fixed shifting and masking, to access the correct
bytes in memory. The compiler calls the ARM Embedded Application
Binary Interface (AEABI) runtime routine __aeabi_uread4 for
reading an unsigned word at an unknown alignment to access field four because
it is not able to determine that the field lies on its natural size
boundary.
In the disassembly of the struct with individually
packed fields in Table 18, fields one, two,
and three are accessed in the same way as in
the case where the entire struct is qualified as __packed.
In contrast to the situation where the entire struct is
packed, however, the compiler makes a word-aligned access to the
field four. This is because the presence of the __packed
short within the structure helps the compiler to determine
that the field four lies on its natural size
boundary.
Compiler Reference: