Each object type has some alignment requirement. Any object of that type is required to start at an address that is a multiple of the alignment requirement. Each C implementation may determine its own alignment requirements for its object types, but alignment requirements are usually set for hardware efficiency. Most notably, the requirements are set so that any correctly aligned object will not cross a memory boundary that requires an extra load or store operation.
For example, consider hardware that can perform transfers of data between the processor and memory in four-byte chunks starting at addresses that are multiples of four bytes. The hardware can transfer bytes addressed 0-3 in one operation, bytes addressed 4-7 in one operation, bytes address 8-11 in one operation, and so on. However, if we wanted bytes 6-9, that would require two memory transfer operations, one to get byes 4-7 and one to get bytes 8-11. In this hardware, we would set the alignment requirement of a four-byte int
to be four bytes, so that all four bytes of the int
are always inside one of these aligned groups.
For a two-byte short
, we could allow a short
object to be in bytes 5-6 in memory, because we can fetch both bytes in one operation (loading bytes 4-7). However, if we have an array of short
with the first in bytes 5-6, then the next would be in 7-8, and we cannot fetch that short
in one operation; we would have to get bytes 4-7 and 8-11. So, to ensure that short
objects always have good alignment, we require them to start at multiples of two bytes.
Once the alignment requirements are set, the algorithm usually used to lay out a structure is:
- Each member in the structure has some size s and some alignment requirement a.
- The compiler starts with a size S set to zero and an alignment requirement A set to one (byte).
- The compiler processes each member in the structure in order:
- Consider the member’s alignment requirement a. If S is not currently a multiple of a, then add just enough bytes to S so that it is a multiple of a. This determines where the member will go; it will go at offset S from the beginning of the structure (for the current value of S).
- Set A to the least common multiple1 of A and a.
- Add s to S, to set aside space for the member.
- When the above process is done for each member, consider the structure’s alignment requirement A. If S is not currently a multiple of A, then add just enough to S so that it is a multiple of A.
The size of the structure is the value of S when the above is done.
Additionally:
- If any member is an array, its size is the number of elements multiplied by the size of each element, and its alignment requirement is the alignment requirement of an element.
- If any member is a structure, its size and alignment requirement are calculated as above.
- If any member is a union, its size is the size of its largest member plus just enough to make it a multiple of the least common multiple1 of the alignments of all the members.
The rules above follow largely from logic; they put each member where it must be to satisfy alignment requirements without using more space than necessary. The C standard allows implementations to add more padding between elements or at the end of the structure, but this is generally not done. (It could be done for special purposes, such as adding padding during debugging to test whether some bug manifests without padding but not with it, to give clues about the nature of the bug.)
In your structure, char
is one byte with alignment requirement one byte, unsigned short
is probably two bytes with requirement two bytes, and char *
is probably eight bytes with requirement eight bytes. Then the structure layout proceeds:
- S is zero and A is one byte.
- No padding is needed, since S is a multiple of the two-byte requirement for
unsigned short Flag
.
unsigned short Flag
adds two to S and makes A two bytes.
- No padding is needed, since S is a multiple of the one-byte requirement for
char DebounceSamples
.
char DebounceSamples
adds one to S, making it three, and does not change A, leaving it two bytes.
- One byte of padding is needed, because S is not a multiple of the two-byte requirement for
unsigned short Device
, so S is increased to four.
unsigned short Device
adds two to S, making it six, and does not change A, leaving it two bytes.
- Two bytes of padding are needed, because S is not a multiple of the eight-byte requirement for
char *BufferPtr
, so S is increased to eight.
char *BufferPtr
adds eight to S, making it 16, and changes A to eight bytes.
- No padding is needed because S is a multiple of the eight-byte requirement for
char *OverridePtr
.
char *OverridePtr
adds eight to S, making it 24, and does not change A.
At the end, the size of the structure is 24 bytes, and its alignment requirement is eight bytes.
Footnote
1 I have worded this for a general case as using the least common multiple of alignment requirements. However, since alignment requirements are always powers of two, the least common multiple of any set of alignment requirements is the largest of them.