>I really would like to see solution with macros.
Quick and dirty:
Code:
static unsigned long alloc_count = 0;
#define xmalloc(x) (++alloc_count, \
printf("alloc line %d: balance -- %ld\n", __LINE__, alloc_count), \
malloc(sizeof *(x)))
#define xnmalloc(x, n) (++alloc_count, \
printf("alloc line %d: balance -- %ld\n", __LINE__, alloc_count), \
malloc((n) * sizeof *(x)))
#define xfree(x) (alloc_count = x ? alloc_count - 1 : alloc_count, \
printf("free line %d: balance -- %ld\n", __LINE__, alloc_count), \
free(x))
xmalloc takes the pointer to be allocated memory to and allocates enough memory for an object of the type the pointer points to. xnmalloc takes does the same thing as xmalloc except it takes an extra argument to determine how much memory for N objects is allocated. xfree releases the memory. Note that all of the update the counter and report the status. xfree doesn't decrement the counter if passed a null pointer.
You can take this as far as you want. My data structures usually go through the first few revisions with something similar using a VERBOSE switch that I can turn off if the output gets to be too cluttered with status messages. I also use call tracing, something very rudimentary like this:
Code:
#if defined(VERBOSE)
# define call_trace(msg) printf(msg)
#else
# define call_trace(msg)
#endif
Then after every call to xmalloc, xnmalloc or xfree, I use call_trace to report where the call is made by passing a suitable message:
Code:
struct tree *
insert(
struct tree *root,
void *obj
)
{
struct tree *p;
...
p = xmalloc(p);
call_trace("xmalloc in insert: p = xmalloc(p)");
if (!p) {
...
}
}
It's very simple, and surprisingly effective in the early stages of data structure verification.