Memory allocation and the associated pointer management is a (the?) major source of bugs in C
. To assist in dealing with this problem I shall introduce the xalloc()/xfree()
macros and show the mechanics of using them. Unfortunately, their full power will be explained only later in the section on memory debugging.
Even something as simple as malloc()
is can introduce bugs. For instance
N
element array of int
, we might mistakenly
write:
int * arrayN = malloc(N); /* should be malloc(N*sizeof(int)) */
struct T
, we might
mistakenly write:
struct T * oneT = malloc(sizeof(struct S));
/* should be malloc(sizeof(struct T)) */
To avoid this, define the macros:
#define xalloc(_t) ((_t*)malloc(sizeof(_t)))
#define xnalloc(_t,_n) ((_t*)malloc((_n)*sizeof(_t)))
Using these, the example would be:
int * arrayN = xnalloc(int, N);
struct T * oneT = xalloc(struct T);
Note that if we would get a compile time error if we made the second error shown above by writing:
struct T * oneT = xalloc(struct S);
Further in place of calloc()
, use:
#define xalloc0(_t) ((_t*)calloc(1,sizeof(_t)))
#define xnalloc0(_t,_n) ((_t*)calloc(_n,sizeof(_t)))
We also replace free
with a pair of macros:
#define xfree(_t,_ptr) free(_ptr)
#define xnfree(_t,_n,_ptr) free(_ptr)
The intent is that xfree(Type,ptr)
is used to free a pointer ptr
that was allocated using xalloc(Type)
, and xnfree(Type,N,ptr)
should be used to free a pointer allocated using xnalloc(Type,N)
. The advantages of using this shall become clear later.
Using these macros, make_int_stack()
and destroy_int_stack()
are rewritten as
int_stack
make_int_stack(void)
{
int_stack stack = xalloc(struct int_stack);
x_max_int_stack(stack) = 16;
x_len_int_stack(stack) = 0;
x_vals_int_stack(stack) = xnalloc(int, 16);
}
void
destroy_int_stack(
int_stack stack
)
{
xnfree(int, max_int_stack(stack), vals_int_stack(stack));
xfree(struct int_stack, stack);
}