2.1 Where is each member?

In C, we access the individual member of a structure by name. In our example, obj1.c.score refers to the score member of the c member of obj1, which is type struct X.

In assembly language programming, there is no native method to define structures. As a result, a structure is defined by stating the relative offset of a member from the beginning of a structure.

The first generally accepted rule is that a member that is listed before another member has a lower address. This means that in our example, obj1.ch has an address that is lower than that of obj1.c. This rule appears to be universal across all known compilers.

However, this is just the beginning of fuzzy rules that apply to how members relate to each other in a struct. In our example, how many bytes are between the ch and c member? In other words, what is the value of (char*)(&obj1.c) - (char*)(&obj1.ch)?

This turns out to be a tricky question. A compiler has to balance performance and data compactness. For maximum performance, each data member should be aligned to the boundary of a “natural” width. For a 32-bit processor, this means it maximizes performance when the address of each member is divisible by 4. A 64-bit processor, on the other hand, will prefer alignment by 8 bytes.

Unfortunately, optimizing for performance contradicts size optimization. A char should only consume one byte with any processor that is byte-addressible.

In the end, there are compiler options and pragmas that can be used to determine how members of a structure is packed. This bit of mess should not affect a C program, as members are referred to by name. However, in assembly language programming, this makes all the differences.