You could "roll you own". The following may reduce memory usage -- marginally -- but it may not improve speed as you'd have to translate your short pointer to absolute pointer, and that adds overhead, also you lose most of the benefits of typechecking.
It would look something like this:
typedef unsigned short ptr;
...
// pre-allocate all memory you'd ever need
char* offset = malloc(256); // make sure this size is less than max unsigned int
// these "pointers" are 16-bit short integer, assuming sizeof(int) == 4
ptr var1 = 0, var2 = 4, var3 = 8;
// how to read and write to those "pointer", you can hide these with macros
*((int*) &offset[var1]) = ((int) 1) << 16;
printf("%i", *((int*) &offset[var1]));
with a bit more tricks, you can invent your own brk() to help allocating the memory from the offset.
Is it worth it? IMO no.