Perfect, thanks. If anyone's interested this was for creating a behind-the-scenes method of solving memory leak issues. Dangling pointers are handled as well. Below is an example of a fictional library that incorporates these ideas. Code that handles dangling pointers will be highlighted in blue, and code that handles memory leaks will be highlighted in green.
Code:
/* TransUnit.h */
#ifndef _TRANS_UNIT_H_
#define _TRANS_UNIT_H_
#include <stddef.h>
typedef
struct TransUnit *
TransUnit_p;
TransUnit_p TransUnit_Create(void);
void TransUnit_Destroy_(TransUnit_p unit);
#ifdef NDEBUG
#define TransUnit_Destroy(p) TransUnit_Destroy_(p)
#else
#define TransUnit_Destroy(p) do { TransUnit_Destroy_(p); (p) = NULL; } while (0)
#endif
#endif
Code:
/* TransUnit.c */
#include "TransUnit.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
struct TransUnit {
/* whatever */
};
#ifndef NDEBUG
static int RegisteredCheckMemLeak = 0;
static unsigned int TransUnitAllocations = 0;
static void CheckMemLeak(void);
#endif
TransUnit_p
TransUnit_Create(void)
{
TransUnit_p unit;
unit = malloc(sizeof(struct TransUnit));
if (unit == NULL)
return NULL;
#ifndef NDEBUG
if ( ! RegisteredCheckMemLeak ) {
atexit(CheckMemLeak);
RegisteredCheckMemLeak = 1;
}
TransUnitAllocations++;
#endif
return unit;
}
void
TransUnit_Destroy_(TransUnit_p unit)
{
assert(unit != NULL);
free(unit);
#ifndef NDEBUG
TransUnitAllocations--;
#endif
}
#ifndef NDEBUG
void
CheckMemLeak(void)
{
assert(TransUnitAllocations == 0);
}
#endif
Now a few main functions to test.
Code:
#include "TransUnit.h"
int
main(void)
{
TransUnit_p unit_a, unit_b, unit_c;
unit_a = TransUnit_Create();
unit_b = TransUnit_Create();
unit_c = TransUnit_Create();
TransUnit_Destroy(unit_a);
TransUnit_Destroy(unit_b);
TransUnit_Destroy(unit_c);
return 0;
}
All is nice and tidy. Program executes smoothly.
Code:
#include "TransUnit.h"
int
main(void)
{
TransUnit_p unit_a, unit_b, unit_c;
unit_a = TransUnit_Create();
unit_b = TransUnit_Create();
unit_c = TransUnit_Create();
TransUnit_Destroy(unit_a);
TransUnit_Destroy(unit_b);
TransUnit_Destroy(unit_c);
TransUnit_Destroy(unit_a); /* whoops */
return 0;
}
Attempting to work with a dandling pointer. Program terminates with a failed assertion.
Code:
#include "TransUnit.h"
int
main(void)
{
TransUnit_p unit_a, unit_b, unit_c;
unit_a = TransUnit_Create();
unit_b = TransUnit_Create();
unit_c = TransUnit_Create();
TransUnit_Destroy(unit_a);
TransUnit_Destroy(unit_b);
/* whoops */
return 0;
}
Forgot to deallocate memory. Program terminates with a failed assertion.
Additionally, all debugging code is enclosed in preprocessing directives, so once you have worked out all the bugs in your program you can compile your code with the -NDEBUG flag, which will leave you with a compact, clean and efficient program.