C memory organization

Note: this was the result of a programming assignment asking students to experimentally evaluate the runtime organization of memory in programs written in C and compiled using gcc on otter. The programs, their output, and the analysis needed to answer the questions below without the need to resort to other C documentation.

Overview of general memory structure

The general structure of memory (based on the memory addresses of global variables, static locals, functions and instructions, heap dynamic variables, local variables, and parameters) apppears to be as shown below.

Each of the different aspects of memory organization will be discussed in greater detail in later sections.

Memory Address    Content description

~0x800000000000 +----------------------+
                |  main's stack frame  |
                |  nested call1 frame  |
  STACK         |  nested call2 frame  |
                |                      |
                |    |         |       |
                |    |         |       |
                |    v         v       |

                      ...........

                |    ^         ^       |
                |    |         |       |
                |    |         |       |
                |                      |
   HEAP         | third dynamic req.   |
                | second dynamic req.  |
                | first dynamic req.   |
~0x000010000000 +----------------------+
                |                      |
                |    ^         ^       |
                |    |         |       |
                |    |         |       |
                | etc. for all statics |  see the note below for static local
   GLOBALS      | second static local  |     constants, as similar rules
                | first static local   |     apply for static local variables
                +----------------------+
                |                      |
                | uninitialized global |  note that different placement rules
                |   vars and consts    |     are in place for uninitialized
                |    ^         ^       |     constants/variables than for 
                |    |         |       |     those which have been initialized
                |    |         |       |
                | etc. for all globals |
                | second global var    |
                | first global var     |
~0x000000600000 +----------------------+
                |                      |
                |    ^         ^       |
                |    |         |       |
    CONSTANTS   |    |         |       |   note static local constants
                | static local consts  |     for later functions appear
                | global constants     |     at lower addresses, and 
                |    ^         ^       |   within a function the first  
                |    |         |       |     static goes at the lowest
    CODE        |    |         |       |     address
                | etc. for all funcs   |
                | second function      |
                | first function       |
~0x000000400000 +----------------------+

Placement and alignment of contants
Shown here in a code snippet with corresponding output, illustrating how the different constant types get grouped relative to the code segments.

Note the uninitialized global variables/constants are being split off from the rest, otherwise padding is used to handle memory-alignment while maintaining the original ordering.
#include <stdio.h>

/* where are relative locations of global vars, constants, static const, etc */
int u = 0;
static const int v = 1;
const int w = 2;
int x = 3;
static const int y = 4;
const int z = 5;

/* are global variables rearranged or padded */
char  gCu1;    /* uninitialized global variables */
char  gCu2;    /* uninitialized global variables */
char  gC0 = 7;
char  gC1 = 1;
long  gL1 = 2;
int   gI1 = 3;
char  gC2 = 4;
short gS1 = 5;
long  gL2 = 6;

/* are global constants rearranged or padded */
const char  kCu1;    /* uninitialized global constants */
const char  kCu2;    /* uninitialized global constants */
const char  kC0 = 7;
const char  kC1 = 1;
const long  kL1 = 2;
const int   kI1 = 3;
const char  kC2 = 4;
const short kS1 = 5;
const long  kL2 = 6;

void f1()
{
 
    printf("global var alignment/rearrangement test\n");
    printf("   uninit char1 %p, uninit char2 %p\n", &gCu1, &gCu2);
    printf("   char0 %p, char1 %p, long1 %p, int1 %p\n", &gC0, &gC1, &gL1, &gI1);
    printf("   char2 %p, short1 %p, long2 %p\n", &gC2, &gS1, &gL2);

    printf("global const alignment/rearrangement test\n");
    printf("   uninit char1 %p, uninit char2 %p\n", &kCu1, &kCu2);
    printf("   char0 %p, char1 %p, long1 %p, int1 %p\n", &kC0, &kC1, &kL1, &kI1);
    printf("   char2 %p, short1 %p, long2 %p\n", &kC2, &kS1, &kL2);
}

void f2();

int main()
{
   static const int a = 0;
   const int b = 1;
   int c = 2;
   static const int d = 3;
   const int e = 4;
   int f = 5;

PRINTFS:
   printf("f1: %p, f2: %p, main: %p, PRINTFS: %p\n", f1, f2, main, &&PRINTFS);
   printf("a: %p, b: %p, c: %p\n", &a, &b, &c);
   printf("d: %p, e: %p, f: %p\n", &d, &e, &f);
   printf("u: %p, v: %p, w: %p\n", &u, &v, &w);
   printf("x: %p, y: %p, z: %p\n", &x, &y, &z);

   f1();
}

void f2()
{
}
f1: 0x40054c, f2: 0x4006f0, main: 0x40061c, PRINTFS: 0x400640
a: 0x400934, b: 0x7ffe829d59dc, c: 0x7ffe829d59d8
d: 0x400938, e: 0x7ffe829d59d4, f: 0x7ffe829d59d0
u: 0x600cc4, v: 0x4007b0, w: 0x4007b4
x: 0x600ca0, y: 0x4007b8, z: 0x4007bc
global var alignment/rearrangement test
   uninit char1 0x600cc9, uninit char2 0x600ccb
   char0 0x600ca4, char1 0x600ca5, long1 0x600ca8, int1 0x600cb0
   char2 0x600cb4, short1 0x600cb6, long2 0x600cb8
global const alignment/rearrangement test
   uninit char1 0x600cca, uninit char2 0x600cc8
   char0 0x4007c0, char1 0x4007c1, long1 0x4007c8, int1 0x4007d0
   char2 0x4007d4, short1 0x4007d6, long2 0x4007d8

Data sizes
The dataSizes routine gives us the following information about the size of various data elements:

Data ordering and alignment

The dataAlign() and revAlign() functions give us insight into the order in which parameters and local variables are stored on the stack.

The examineStack() function gives us insight into the ordering of local variables compared to block locals.

The mask() and examineStack() functions give us insight into the location of static local variables.

The main() routine gives us insight into the location of global variables.

Run time stack content/organization

Parameter passing mechanisms

These are investigated in the passingDefaults() function, as called from the main routine.

Here we see that primitive data types are passed by value.

Passing arrays appears to the programmer to act as pass by reference since the array "variable" is the address of the first array element, hence array contents are not copied onto the stack and back.

Unions are shown to be passed by value.

The most interesting component is the passing of structs, which are passed by value (i.e. their contents copied onto the stack) but it appears that the address of the original struct is also passed to the function!

For the function passingDefaults(long, struct, union, long[3], long) we find the stack contents look like this:

   +----------------------------------+
   | struct field 5 value             |
   | struct field 4 value             |
   | struct field 3 value             |
   | struct field 2 value             |
   | struct field 1 value             |
   +----------------------------------+
   | ptr to struct in main            |
   | ptr to struct in main            |
   | ptr to this location (?)         |
   | ptr to static var maskLocal      |
   +----------------------------------+
   | first long                       |
   +----------------------------------+
   | union value                      |
   +----------------------------------+
   | array address                    |
   +----------------------------------+
   | last long                        |
   +----------------------------------+

Analysis of dynamically-allocated memory

Location/ordering of instructions

As shown in the main routine and the instructions() routine, the executable instructions are stored in a seperate area of memory, in the order the functions appear in the source code starting in the vicinity of address 0x0400000.

Most instructions appear to take 8 bytes. Those that take more (e.g. the loop) presumably require multiple machine code instructions to implement the single C instruction.
assign5.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* global variables */
long globalx = 5;
long globaly = 9;

/* mask all but the bottom 8 bytes when printing addresses */
long* mask(long *ptr) {
   static long maskLocal = 3;
   if (ptr == 0) return (&maskLocal);
   return (long*)( ((long)(ptr))&0x00ffffffff); 
}

typedef struct { char cFld; short sFld; int iFld; char cFld2; long lFld; long lFld2; } FwdStr;
typedef struct { long lFld; long lFld2; int iFld; short sFld; char cFld; char cFld2; } RevStr;
typedef union  { char cFld; short sFld; int iFld; long lFld; float fFld; double dFld; } unionCombo;

void examineBlock(long *ptr)
{
   int i = 0;
   while (i < 8) {
      printf("%x", (*ptr));
      if (i == 7) printf("\n");
      else printf(":");
      i++;  ptr++;
   }
}

long* init()
{
   long* ptr = (long*)malloc(7*sizeof(long));
   long i = 0;
   while (i < 7) {
     ptr[i] = i;
     i++;
   }
   return ptr;
}

void freeTest()
{
   /* request a series of eight blocks of memory, each will grab a 64-byte block  
    * then free blocks 1, 4, 5, 8
    * then request four more blocks and see if you get them back
    */
   long *ptr1 = init();
   long *ptr2 = init();
   long *ptr3 = init();
   long *ptr4 = init();
   long *ptr5 = init();
   long *ptr6 = init();
   long *ptr7 = init();
   long *ptr8 = init();
   long *ptr0 = ptr1; ptr0--;
   long *ptr9 = &(ptr8[8]); 
   printf("\nAllocated blocks:\n");
   printf("   1:%p, 2:%p,\n", mask(ptr1), mask(ptr2));
   printf("   3:%p, 4:%p,\n", mask(ptr3), mask(ptr4));
   printf("   5:%p, 6:%p,\n", mask(ptr5), mask(ptr6));
   printf("   7:%p, 8:%p,\n", mask(ptr7), mask(ptr8));
   printf("contents just prior to block 1: %x\n", (*ptr0));
   printf("block 1 contents as 8 longs:"); examineBlock(ptr1);
   printf("block 2 contents as 8 longs:"); examineBlock(ptr2);
   printf("block 3 contents as 8 longs:"); examineBlock(ptr3);
   printf("block 4 contents as 8 longs:"); examineBlock(ptr4);
   printf("block 5 contents as 8 longs:"); examineBlock(ptr5);
   printf("block 6 contents as 8 longs:"); examineBlock(ptr6);
   printf("block 7 contents as 8 longs:"); examineBlock(ptr7);
   printf("block 8 contents as 8 longs:"); examineBlock(ptr8);
   printf("contents just after block 8: %x\n", (*ptr9));
   printf("\nFreeing blocks 1, 4, 5, 8\n");
   free(ptr1); free(ptr4); free(ptr5); free(ptr8);
   printf("contents just prior to block 1: %x\n", (*ptr0));
   printf("block 1 contents as 8 longs:"); examineBlock(ptr1);
   printf("block 2 contents as 8 longs:"); examineBlock(ptr2);
   printf("block 3 contents as 8 longs:"); examineBlock(ptr3);
   printf("block 4 contents as 8 longs:"); examineBlock(ptr4);
   printf("block 5 contents as 8 longs:"); examineBlock(ptr5);
   printf("block 6 contents as 8 longs:"); examineBlock(ptr6);
   printf("block 7 contents as 8 longs:"); examineBlock(ptr7);
   printf("block 8 contents as 8 longs:"); examineBlock(ptr8);
   printf("contents just after block 8: %x\n", (*ptr9));
   printf("\nRequesting replacements for 1, 4, 5, 8\n");
   ptr1 = (long*)malloc(7*sizeof(long));
   ptr4 = (long*)malloc(7*sizeof(long));
   ptr5 = (long*)malloc(7*sizeof(long));
   ptr8 = (long*)malloc(7*sizeof(long));
   printf("Newly allocated blocks:\n");
   printf("   1:%p, 4:%p,\n", mask(ptr1), mask(ptr4));
   printf("   5:%p, 8:%p,\n", mask(ptr5), mask(ptr8));
   printf("contents just prior to block 1: %x\n", (*ptr0));
   printf("block 1 contents as 8 longs:"); examineBlock(ptr1);
   printf("block 2 contents as 8 longs:"); examineBlock(ptr2);
   printf("block 3 contents as 8 longs:"); examineBlock(ptr3);
   printf("block 4 contents as 8 longs:"); examineBlock(ptr4);
   printf("block 5 contents as 8 longs:"); examineBlock(ptr5);
   printf("block 6 contents as 8 longs:"); examineBlock(ptr6);
   printf("block 7 contents as 8 longs:"); examineBlock(ptr7);
   printf("block 8 contents as 8 longs:"); examineBlock(ptr8);
   printf("contents just after block 8: %x\n", (*ptr9));
}

void chMallocTest(int size)
{
   char *c = (char*)malloc(size*sizeof(char));
   int i = 0;
   char ch = 'a';
   while (i < (size-1)) {
      c[i] = ch;
      ch++;
      i++;
   }
   c[i] = '\0';
   long *ptr = (long*)(&c);
   printf("   num chars:%2d\n", size);
   printf("      bytes  0-31: ");
   for (i=0; i <32; i++) {
       if (i < (size-1)) printf("%c", c[i]);
       else printf(" %x", (0x00ff & (int)(c[i])));
   }
   if (size > 24) { 
      printf("\n      bytes 32-63:");
      for (i=32; i <64; i++) {
         printf(" %x", (0x00ff & (int)(c[i])));
      }
   }
   printf("\n");

}

void mallocTesting()
{
   int i;
   printf("\nTesting char mallocs\n");
   for (i = 1; i <= 5; i++) chMallocTest(i);
   /* skipping 6..18 as they reveal nothing new,
    *    showing 24-27 as they show the breakpoint
    *    going from 24 byte request to 25 byte request
    */
   for (i = 24; i <= 27; i++) chMallocTest(i);
}

void passingDefaults(long pre, FwdStr s, unionCombo u, long arr[], long post)
{
   printf("\nParameter values and locations:\n");
   printf("   long[-1]       value:%ld,         addr:%p\n", pre, mask((long*)(&pre)));
   printf("   s{0,1,2,3,4,5}                   addr:%p\n", mask((long*)(&s)));
   printf("      s.cFld      value:%ld,          addr:%p\n", s.cFld, mask((long*)(&(s.cFld))));
   printf("      s.sFld      value:%ld,          addr:%p\n", s.sFld, mask((long*)(&(s.sFld))));
   printf("      s.iFld      value:%ld,          addr:%p\n", s.iFld, mask((long*)(&(s.iFld))));
   printf("      s.cFld2     value:%ld,          addr:%p\n", s.cFld2, mask((long*)(&(s.cFld2))));
   printf("      s.lFld      value:%ld,          addr:%p\n", s.lFld, mask((long*)(&(s.lFld))));
   printf("      s.lFld2     value:%ld,          addr:%p\n", s.lFld2, mask((long*)(&(s.lFld2))));
   printf("   union[5]       value:%ld,          addr:%p\n", u.lFld, mask((long*)(&u)));
   printf("   array[12,10,9] value:%p, addr:%p\n", mask(arr), mask((long*)(&arr)));
   printf("   long[7]        value:%ld,          addr:%p\n", post, mask((long*)(&post)));
}

void instructions()
{
INSTR_YDEF:     ;
                int y = 1;
INSTR_XDEF:     ;
                int x;
INSTR_LOOP:     for (x = 0; x < 3; x++) {
INSTR_INLOOP:       y = x;
                }
INSTR_PRINTF1:  printf("\nInstruction addresses:\n");
INSTR_PRINTF2:  printf("   function:     %p\n", instructions);
                printf("   nop/ydef/init:%p\n", &&INSTR_YDEF);
                printf("   nop/xdef:     %p\n", &&INSTR_XDEF);
                printf("   loop:         %p\n", &&INSTR_LOOP);
                printf("   in loop:      %p\n", &&INSTR_INLOOP);
                printf("   printf1:      %p\n", &&INSTR_PRINTF1);
                printf("   printf2:      %p\n", &&INSTR_PRINTF2);
                printf("   return:       %p\n", &&INSTR_RETURN);
INSTR_RETURN:   return;
}

void dataSizes()
{
   printf("\nData type sizes\n");
   printf("   char:%d, short:%d, int:%d, long:%d, float:%d, double:%d\n",
              sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(float), sizeof(double));
   printf("   char*:%d, int*:%d, double*:%d, char**:%d\n",
              sizeof(char*), sizeof(int*), sizeof(double*), sizeof(char**));
   printf("   FwdStruct:%d, RevStruct:%d\n", sizeof(FwdStr), sizeof(RevStr));
   printf("   unionCombo:%d\n", sizeof(unionCombo));

   printf("\nField alignment in structs\n");
   FwdStr f;
   RevStr r;
   printf("   Fwd: char:%p, short:%p, int:%p, char:%p, long:%p, long:%p\n",
              mask((long*)(&(f.cFld))),
              mask((long*)(&(f.sFld))),
              mask((long*)(&(f.iFld))),
              mask((long*)(&(f.cFld2))),
              mask((long*)(&(f.lFld))),
              mask((long*)(&(f.lFld2))));
   printf("   Rev: long:%p, long:%p, int:%p, short:%p, char:%p, char:%p\n",
              mask((long*)(&(r.lFld))),
              mask((long*)(&(r.lFld2))),
              mask((long*)(&(r.iFld))),
              mask((long*)(&(r.sFld))),
              mask((long*)(&(r.cFld))),
              mask((long*)(&(r.cFld2))));
}


void dataAlign(char pre, char a, short b, int c, long d, long post)
{
   char f;
   short g;
   int h;
   long i;
   printf("Fwd parameter addesses:\n");
   printf("   char:%p, char:%p, short:%p, int:%p, long:%p, long:%p\n",
       mask((long*)(&pre)),mask((long*)(&a)), mask((long*)(&b)), mask((long*)(&c)), mask((long*)(&d)), mask((long*)(&post)));
   printf("Fwd local variable addesses:\n");
   printf("   char:%p, short:%p, int:%p, long:%p\n",
       mask((long*)(&f)), mask((long*)(&g)), mask((long*)(&h)), mask((long*)(&i)));
   printf("\nUnion field locations:\n");
   unionCombo u;
   u.dFld = 1.2825e2; printf("   double 1.2825e2 as raw hex %p\n", (long*)(u.lFld)); 
   u.fFld = 1.2825e2; printf("   float 1.2825e2 as raw hex %p\n", (long*)(u.lFld)); 
   u.lFld = 6; printf("   long 6 as raw hex %p\n", (long*)(u.lFld)); 
   u.iFld = 6; printf("   int 6 as raw hex %p\n", (long*)(u.lFld)); 
   u.sFld = 6; printf("   short 6 as raw hex %p\n", (long*)(u.lFld)); 
   u.cFld = '0'; printf("   char '0' as raw hex %p\n", (long*)(u.lFld)); 
}

void revAlign(long d, int c, short b, char a)
{
   long pre;
   long i;
   int h;
   short g;
   char f;
   char post;
   printf("\nRev parameter addesses:\n");
   printf("   long:%p, int:%p, short:%p, char:%p\n",
       mask((long*)(&d)), mask((long*)(&c)), mask((long*)(&b)), mask((long*)(&a)));
   printf("Rev local variable addesses:\n");
   printf("   long:%p, long:%p, int:%p, short:%p, char:%p, char:%p\n",
       mask((long*)(&pre)),mask((long*)(&i)), mask((long*)(&h)), mask((long*)(&g)), mask((long*)(&f)), mask((long*)(&post)));
}

long* examineStack(long a, long b)
{
   static long examineStackLocal = 7;
   long c = 3;
   long* ptr = &c;
   ptr++;
   printf("\nContents of addresses above first local (i.e. deeper in stack)\n");
   printf("   8 bytes above: addr(%p), content(%p)\n", mask(ptr), mask((long*)(*ptr))); 
   ptr++;
   printf("  16 bytes above: addr(%p), content(%p)\n", mask(ptr), mask((long*)(*ptr))); 
   ptr++;
   printf("  24 bytes above: addr(%p), content(%p)\n", mask(ptr), mask((long*)(*ptr))); 
   ptr++;
   printf("  32 bytes above: addr(%p), content(%p)\n", mask(ptr), mask((long*)(*ptr))); 
   ptr++;
   printf("  40 bytes above: addr(%p), content(%p)\n", mask(ptr), mask((long*)(*ptr))); 
   ptr++;
   printf("  48 bytes above: addr(%p), content(%p)\n", mask(ptr), mask((long*)(*ptr))); 
   ptr++;
   printf("  64 bytes above: addr(%p), content(%p)\n", mask(ptr), mask((long*)(*ptr))); 
   long d = 4;
   long e;
   for (e = 100; e < 102; e++) {
       long f = 5;
       long g = 6;
       if (e == 100) {
          printf("\nAddresses of first for loop locals:\n");
          printf("   e:%p, f:%p, g:%p\n", mask(&e), mask(&f), mask(&g));
       }
   }
   long h = 7;
   long i = 8;
   long j;
   for (j = 200; j < 202; j++) {
       long k = 5;
       long l = 6;
       if (j == 200) {
          printf("Addresses of second for loop locals:\n");
          printf("   j:%p, k:%p, l:%p\n", mask(&j), mask(&k), mask(&l));
       }
   }
   printf("\nAddresses of examineStack parameters:\n   a:%p, b:%p\n", mask(&a), mask(&b));
   printf("\nAddresses of examineStack scope variables:\n   c:%p, d:%p, h:%p, i:%p\n", mask(&c), mask(&d), mask(&h), mask(&i));
   return &examineStackLocal;
}

int main(int argc, char *argv[])
{
   /* print raw data sizes */
   dataSizes();

   /* investigate alignment and padding */
   revAlign(12, 5, 3, 'a');
   dataAlign('-', 'a', 3, 5, 12, 25);

   /* investigate memory/stack organization */
   long x = 0;
   long y = 1;
   long z = 2;
   long *p;
   printf("\nAddresses of parameters to main:\n");
   printf("   argc:%p, argv:%p\n", mask((long*)(&argc)), mask((long*)(&argv)));
   printf("Addresses of locals to main:\n");
   printf("   x:%p, y:%p, z:%p\n", mask(&x), mask(&y), mask(&z));
BEFORE:
   p = examineStack(x, y);
AFTER:
   printf("\nFunction addresses\n   main:%p, examineStack:%p\n", mask((long*)(main)), mask((long*)(examineStack)));
   printf("\nAddresses before/after function call:\n");
   printf("   before:%p, after:%p\n", mask(&&BEFORE), mask(&&AFTER));

   printf("\nAddresses of globals:\n");
   printf("   globalx:%p, globaly:%p\n", mask(&globalx), mask(&globaly));
   printf("Addresses of static locals:\n");
   printf("   examineStackLocal:%p, maskLocal:%p\n", mask(p), mask(0));

   /* parameter passing mechanisms */
   unionCombo u;  u.lFld = 5;
   FwdStr s = { 0, 1, 2, 3, 4, 5 };
   long arr[3] = { 12, 10, 9 };
   printf("\nAddresses of variables being passed as parameters:\n");
   printf("   struct s:%p\n", mask((long*)(&s)));
   printf("   union u:%p\n", mask((long*)(&u)));
   printf("   array arr:%p\n", mask(arr));
   passingDefaults(-1, s, u, arr, 7);
   
   /* investigate instruction layout */
   instructions();

   /* investigate heap */
   mallocTesting();
   freeTest();
   return 0;
}

output

Data type sizes
   char:1, short:2, int:4, long:8, float:4, double:8
   char*:8, int*:8, double*:8, char**:8
   FwdStruct:32, RevStruct:24
   unionCombo:8

Field alignment in structs
   Fwd: char:0x3fc723b0, short:0x3fc723b2, int:0x3fc723b4, char:0x3fc723b8, long:0x3fc723c0, long:0x3fc723c8
   Rev: long:0x3fc72390, long:0x3fc72398, int:0x3fc723a0, short:0x3fc723a4, char:0x3fc723a6, char:0x3fc723a7

Rev parameter addesses:
   long:0x3fc723a8, int:0x3fc723a4, short:0x3fc723a0, char:0x3fc7239c
Rev local variable addesses:
   long:0x3fc723c8, long:0x3fc723c0, int:0x3fc723bc, short:0x3fc723ba, char:0x3fc723b9, char:0x3fc723b8
Fwd parameter addesses:
   char:0x3fc723ac, char:0x3fc723a8, short:0x3fc723a4, int:0x3fc723a0, long:0x3fc72398, long:0x3fc72390
Fwd local variable addesses:
   char:0x3fc723cf, short:0x3fc723cc, int:0x3fc723c8, long:0x3fc723c0

Union field locations:
   double 1.2825e2 as raw hex 0x4060080000000000
   float 1.2825e2 as raw hex 0x4060080043004000
   long 6 as raw hex 0x6
   int 6 as raw hex 0x6
   short 6 as raw hex 0x6
   char '0' as raw hex 0x30

Addresses of parameters to main:
   argc:0x3fc7243c, argv:0x3fc72430
Addresses of locals to main:
   x:0x3fc724a0, y:0x3fc72498, z:0x3fc72490

Contents of addresses above first local (i.e. deeper in stack)
   8 bytes above: addr(0x3fc723d8), content(0x3fc723d8)
  16 bytes above: addr(0x3fc723e0), content(0x602ef0)
  24 bytes above: addr(0x3fc723e8), content(0x3fc72498)
  32 bytes above: addr(0x3fc723f0), content(0x3fc72490)
  40 bytes above: addr(0x3fc723f8), content(0x3fc725a0)
  48 bytes above: addr(0x3fc72400), content(0x3fc724c0)
  64 bytes above: addr(0x3fc72408), content(0x4019e4)

Addresses of first for loop locals:
   e:0x3fc723c0, f:0x3fc723a0, g:0x3fc72398
Addresses of second for loop locals:
   j:0x3fc723a8, k:0x3fc72390, l:0x3fc72388

Addresses of examineStack parameters:
   a:0x3fc72378, b:0x3fc72370

Addresses of examineStack scope variables:
   c:0x3fc723d0, d:0x3fc723c8, h:0x3fc723b8, i:0x3fc723b0

Function addresses
   main:0x4018ca, examineStack:0x40154b

Addresses before/after function call:
   before:0x4019d1, after:0x4019e8

Addresses of globals:
   globalx:0x602ed8, globaly:0x602ee0
Addresses of static locals:
   examineStackLocal:0x602ee8, maskLocal:0x602ef0

Addresses of variables being passed as parameters:
   struct s:0x3fc72460
   union u:0x3fc72480
   array arr:0x3fc72440

Parameter values and locations:
   long[-1]       value:-1,         addr:0x3fc723e8
   s{0,1,2,3,4,5}                   addr:0x3fc72410
      s.cFld      value:0,          addr:0x3fc72410
      s.sFld      value:1,          addr:0x3fc72412
      s.iFld      value:2,          addr:0x3fc72414
      s.cFld2     value:3,          addr:0x3fc72418
      s.lFld      value:4,          addr:0x3fc72420
      s.lFld2     value:5,          addr:0x3fc72428
   union[5]       value:5,          addr:0x3fc723e0
   array[12,10,9] value:0x3fc72440, addr:0x3fc723d8
   long[7]        value:7,          addr:0x3fc723d0

Instruction addresses:
   function:     0x400fbc
   nop/ydef/init:0x400fc4
   nop/xdef:     0x400fcb
   loop:         0x400fcb
   in loop:      0x400fd4
   printf1:      0x400fe4
   printf2:      0x400fee
   return:       0x40108f

Testing char mallocs
   num chars: 1
      bytes  0-31:  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 e1 f 2 0 0 0 0 0
   num chars: 2
      bytes  0-31: a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 c1 f 2 0 0 0 0 0
   num chars: 3
      bytes  0-31: ab 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 a1 f 2 0 0 0 0 0
   num chars: 4
      bytes  0-31: abc 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 81 f 2 0 0 0 0 0
   num chars: 5
      bytes  0-31: abcd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 61 f 2 0 0 0 0 0
   num chars:24
      bytes  0-31: abcdefghijklmnopqrstuvw 0 41 f 2 0 0 0 0 0
   num chars:25
      bytes  0-31: abcdefghijklmnopqrstuvwx 0 0 0 0 0 0 0 0
      bytes 32-63: 0 0 0 0 0 0 0 0 11 f 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   num chars:26
      bytes  0-31: abcdefghijklmnopqrstuvwxy 0 0 0 0 0 0 0
      bytes 32-63: 0 0 0 0 0 0 0 0 e1 e 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   num chars:27
      bytes  0-31: abcdefghijklmnopqrstuvwxyz 0 0 0 0 0 0
      bytes 32-63: 0 0 0 0 0 0 0 0 b1 e 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Allocated blocks:
   1:0x1bc7160, 2:0x1bc71a0,
   3:0x1bc71e0, 4:0x1bc7220,
   5:0x1bc7260, 6:0x1bc72a0,
   7:0x1bc72e0, 8:0x1bc7320,
contents just prior to block 1: 41
block 1 contents as 8 longs:0:1:2:3:4:5:6:41
block 2 contents as 8 longs:0:1:2:3:4:5:6:41
block 3 contents as 8 longs:0:1:2:3:4:5:6:41
block 4 contents as 8 longs:0:1:2:3:4:5:6:41
block 5 contents as 8 longs:0:1:2:3:4:5:6:41
block 6 contents as 8 longs:0:1:2:3:4:5:6:41
block 7 contents as 8 longs:0:1:2:3:4:5:6:41
block 8 contents as 8 longs:0:1:2:3:4:5:6:20cb1
contents just after block 8: 0

Freeing blocks 1, 4, 5, 8
contents just prior to block 1: 41
block 1 contents as 8 longs:0:1:2:3:4:5:6:41
block 2 contents as 8 longs:0:1:2:3:4:5:6:41
block 3 contents as 8 longs:0:1:2:3:4:5:6:41
block 4 contents as 8 longs:1bc7150:1:2:3:4:5:6:41
block 5 contents as 8 longs:1bc7210:1:2:3:4:5:6:41
block 6 contents as 8 longs:0:1:2:3:4:5:6:41
block 7 contents as 8 longs:0:1:2:3:4:5:6:41
block 8 contents as 8 longs:1bc7250:1:2:3:4:5:6:20cb1
contents just after block 8: 0

Requesting replacements for 1, 4, 5, 8
Newly allocated blocks:
   1:0x1bc7320, 4:0x1bc7260,
   5:0x1bc7220, 8:0x1bc7160,
contents just prior to block 1: 41
block 1 contents as 8 longs:1bc7250:1:2:3:4:5:6:20cb1
block 2 contents as 8 longs:0:1:2:3:4:5:6:41
block 3 contents as 8 longs:0:1:2:3:4:5:6:41
block 4 contents as 8 longs:1bc7210:1:2:3:4:5:6:41
block 5 contents as 8 longs:1bc7150:1:2:3:4:5:6:41
block 6 contents as 8 longs:0:1:2:3:4:5:6:41
block 7 contents as 8 longs:0:1:2:3:4:5:6:41
block 8 contents as 8 longs:0:1:2:3:4:5:6:41
contents just after block 8: 0