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 12.
Table 12. 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;
|
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 13 shows
the corresponding disassembly of the machine code produced by the
compiler for each of the sample implementations of Table 12, where the C code
for each implementation has been compiled using the option -O2.
Table 13. 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]
|
Note
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.
In the disassembly of the unpacked struct in Table 13, 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 13, 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 13, 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.