Due to some recent bus errors, I have been thinking a bit about alignment lately.
When the compiler lays out a struct it will generally ensure that the members of the struct are aligned on the required boundaries for the target architecture. For example, given:
struct foo {
int32_t a;
int16_t b;
};
A compiler may lay it out something like this:
| 0x??????04 | a |
| 0x??????05 | |
| 0x??????06 | |
| 0x??????07 | |
| 0x??????08 | b |
| 0x??????09 | |
| 0x??????0A | pad |
| 0x??????0B |
The entire struct will likely be placed at an address aligned on a 4 byte boundary because the largest struct member is 4 bytes long, and the int16 type will be followed by a 2 byte pad. Why? So that an array of structs will have the ‘a’ member for all array elements aligned on a 4 byte boundary. All is good, right?
If we add a char to the struct:
struct foo {
int32_t a;
char c;
int16_t b;
};
| 0x??????04 | a |
| 0x??????05 | |
| 0x??????06 | |
| 0x??????07 | |
| 0x??????08 | c |
| 0x??????09 | pad |
| 0x??????0A | b |
| 0x??????0B |
Changing our char type to a 64 bit type may result in something like this:
struct foo {
int32_t a;
int64_t c;
int16_t b;
};
| 0x??????00 | a |
| 0x??????01 | |
| 0x??????02 | |
| 0x??????03 | |
| 0x??????04 | pad |
| 0x??????05 | |
| 0x??????06 | |
| 0x??????07 | |
| 0x??????08 | c |
| 0x??????09 | |
| 0x??????0A | |
| 0x??????0B | |
| 0x??????0C | |
| 0x??????0D | |
| 0x??????0E | |
| 0x??????0F | |
| 0x??????10 | b |
| 0x??????11 | |
| 0x??????12 | pad |
| 0x??????13 | |
| 0x??????14 | |
| 0x??????15 | |
| 0x??????16 | |
| 0x??????17 |
In this case (for this imaginary compiler) the entire struct is aligned on an 8 byte boundary, with the 8 byte member also aligned on an 8 byte boundary. So we can still create an array of struct foo, and things will be properly aligned. Of course, even though the individual members of the structure are 4, 8 and 2 bytes long, the entire structure with padding is 24 bytes long, so 10 bytes are “wasted”, it is best to design structs with this in mind, so that my imaginary compiler does not waste space. In the above case, changing the order of the elements is helpful:
struct foo {
int32_t a;
int16_t b;
int64_t c;
};
| 0x??????00 | a |
| 0x??????01 | |
| 0x??????02 | |
| 0x??????03 | |
| 0x??????04 | b |
| 0x??????05 | |
| 0x??????06 | pad |
| 0x??????07 | |
| 0x??????08 | c |
| 0x??????09 | |
| 0x??????0A | |
| 0x??????0B | |
| 0x??????0C | |
| 0x??????0D | |
| 0x??????0E | |
| 0x??????0F |
Now, on some systems a program will run faster if types are aligned “properly”, on others a bus error will occur for misaligned access. Misaligned access will often occur in cases like this:
char array[1024];
...
struct foo * bar = (struct foo *)array;
bar->c = 1;
In the above case, although the array certainly is large enough to hold a foo structure, there is no guarantee that a char will be aligned on an 8 byte boundary, it is a 1 byte type, so there is no need for the compiler to align it. Because of this bar->c = 1; could cause a bus error. OtherĀ bus errors can occur casting structs of the same size, but with different alignment requirements, etc.
At least firefox works for us on HP-UX now
Shantonu noted that for gcc, using -Wcast-align will at least warn about this issue.
