At Sixes and Sevens
Suppose you're writing a pay-per-program app that accepts users' requests to listen to a certain radio program. Each request is represented as a struct that contains the following data members:
char timezone; // 'Z' for Zulu, etc.
int frequency; //88000000 for 88FM
short timeofday; //e.g., 1530
On a typical 32-bit system, the individual sizes of these members are:
Many programmers don't know that size of Broadcast
isn't necessarily equivalent to the sum of these values. In other words,
Is often larger than:
There are two reasons for this. First, compilers usually round up a data structure's size to make it divisible by 2, 4, or 8depending on the hardware's memory alignment
requirements. Secondly, compilers insert padding bytes
between data members to ensure that each member's address is properly aligned. The problem is that when members are declared in a random order, the compiler may need to insert more padding bytes between them, thereby inflating the data structure's size. This is why in C++ Builder, for example, struct Broadcast
occupies 12 bytes rather than 8. Your compiler is likely to produce a similar result (try it!). Clearly, space is wasted here. This waste is particularly noticeable when you create a large array of Broadcast
Brute Force Downsizing
It's tempting to play with your compiler's settings to force a more stringent alignment policy. You can either use a #pragma directive for this purpose or modify the appropriate command line option. In C++ Builder, you do it like this: go to Project>Options... In the Advanced Compiler tab, change the Data Alignment value to Byte or Word (as seen in Figure 1).
|Figure 1. The image shows C++ Builder's data alignment setting.|
This will cause the size of Broadcast to shrink to 7 or 8 bytes, respectively. It's too early to celebrate, though. Forcing a struct to align on a byte boundary severely compromises portability; some compilers and operating systems won't run such code. Even if your platform tolerates this and you're not aiming at cross-platform development, this workaround is still problematic because you have to make sure that all source file and headers are compiled with the same configuration. Failing to do so will cause incompatible binary layout among different .obj files leading to unpredictable behavior at runtime. Even if you recompile and re-link your entire project with this setting, your program might exhibit a speed penalty. Sounds bad, doesn't it? What if I told you that you can optimize members' layout without resorting to compiler-dependent hacks and more importantlywithout sacrificing speed?