2.2 Finding out the member offsets

We can guess, we can observe, and we can generalize. In the end, however, it is up to a C compiler (not the assembler) to determine how members are placed in a structure. In order to make assembly language programs access structures consistently, it is best to make a C program to print out those offsets.

For example, our structure can generate the following C code for this purpose:

/* call this x_struct.c */ 
#include ”x.h” /* where the struct is defined */ 
#define OFFSET(m) ((char*)(&obj.m)-(char*)(&obj)) 
int main(void
{ 
  struct X obj; 
  printf(”X_i_=_%d”,OFFSET(i)); 
  printf(”X_ch_=_%d”,OFFSET(ch)); 
  printf(”X_c_=_%d”,OFFSET(c)); 
  printf(”X_c_answer_=_%d”,OFFSET(c.answer)); 
  printf(”X_c_score_=_%d”,OFFSET(c.score)); 
  printf(”X_size_=_%d”,sizeof(obj)); 
  return 0; 
}

When this program runs, it prints out all the symbol definitions to stdout. The output can be redirected to x.inc, which becomes the include file of assembly language source files. For example, an assembly language source file that needs to access struct X may start with the following line:

#include x.inc

Any assembly language source file that has #include needs to be pre-processed. As such, it should end with an extension of .S instead of .s (upper case instead of lower case). A properly constructed Makefile should take care of this. See 0041 for more details.

Note that additional rules should be explicitly included in the Makefile to generate x.inc:

x.inc: x_struct 
        x_struct > x.inc 
 
x_struct: x_struct.o

This also means that x_struct.c should be listed in CSRC (so that its dependency file can be automatically generated), but it should not be included in the dependency of the “normal” target. This can be done by explicitly listing the .o files needed by the “normal” target.