122 3. Fundamentals of Software Engineering for Games
example of such a diagram for the struct Foo listed below is shown in Fig-
ure 3.11.
struct Foo
{
U32 mUnsignedValue;
F32 mFloatValue;
I32 mSignedValue;
};
The sizes of the data members are important and should be represented
in your diagrams. This is easily done by using the width of each data member
to indicate its size in bits—i.e., a 32-bit integer should be roughly four times
the width of an 8-bit integer (see Figure 3.12).
struct Bar
{
U32 mUnsignedValue;
F32 mFloatValue;
bool mBooleanValue; // diagram assumes this is 8 bits
};
3.2.5.1. Alignment and Packing
As we start to think more carefully about the layout of our structs and classes
in memory, we may start to wonder what happens when small data members
are interspersed with larger members. For example:
struct InefficientPacking
{
U32 mU1; // 32 bits
F32 mF2; // 32 bits
U8 mB3; // 8 bits
I32 mI4; // 32 bits
bool mB5; // 8 bits
char* mP6; // 32 bits
};
You might imagine that the compiler simply packs the data members into
memory as tightly as it can. However, this is not usually the case. Instead,
the compiler will typically leave “holes” in the layout, as depicted in Fig-
ure 3.13. (Some compilers can be requested not to leave these holes by us-
ing a preprocessor directive like #pragma pack, or via command-line op-
tions; but the default behavior is to space out the members as shown in Fig-
ure 3.13.)
mU1
mF2
mB3
mI4
mB5
mP6
+0x0
+0x4
+0x8
+0xC
+0x10
+0x14
Figure 3.13. Ineffi cient
struct packing due to
mixed data member
sizes.
mUnsignedValue
mFloatValue
mSignedValue
+0x0
+0x4
+0x8
Figure 3.11. Memory
layout of a simple
struct.
mUnsignedValue
mFloatValue
mBooleanValue
+0x0
+0x4
+0x8
Figure 3.12. A memory
layout using width to
indicate member sizes.