home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1990
/
08
/
schimand.lst
< prev
next >
Wrap
File List
|
1990-06-20
|
18KB
|
892 lines
_ENCAPSULATING C MEMORY ALLOCATION_
by Jim Schimandle
[LISTING ONE]
/* junk.c -- Junk list build/destroy test
* $Log: E:/vcs/junk/junk.c_v $
* Rev 1.1 20 Nov 1989 09:42:00 set
* Added name field for junk node tagging
* Rev 1.0 09 Nov 1989 18:12:30 jvs
* Initial revision.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Junk item structure */
typedef struct jnod {
struct jnod *junk_next ;
char *junk_name ;
int junk_data[3000] ;
} JUNK ;
/* Function prototypes */
JUNK *junk_list_build(void) ;
void junk_list_destroy(JUNK *) ;
JUNK *junk_open(char *) ;
void junk_close(JUNK *) ;
/* main() -- Entry point for test */
void main()
{
JUNK *jlist ;
while ((jlist = junk_list_build()) != NULL)
{
junk_list_destroy(jlist) ;
}
printf("*** Should never get here! ***\n") ;
}
/* junk_list_build() -- Build a list of junk items */
JUNK *junk_list_build()
{
JUNK *jlist ;
JUNK *jnew ;
jlist = NULL ;
while ((jnew = junk_open("name to identify junkitem")) != NULL)
{
jnew->junk_next = jlist ;
jlist = jnew ;
}
return jlist ;
}
/* junk_list_destroy() -- Destroy a list of junk items */
void junk_list_destroy(JUNK *jlist)
{
JUNK *jtmp ;
while (jlist != NULL)
{
jtmp = jlist ;
jlist = jlist->junk_next ;
junk_close(jtmp) ;
}
}
/* junk_open() -- Create a junk item */
JUNK *junk_open(char *name)
{
JUNK *jnew ;
jnew = (JUNK *) malloc(sizeof(JUNK)) ;
if (jnew != NULL)
{
jnew->junk_name = strdup(name) ;
if (jnew->junk_name == NULL)
{
return NULL ;
}
}
return jnew ;
}
/* junk_close() -- Close a junk item */
void junk_close(JUNK *jold)
{
free(jold) ;
}
[LISTING TWO]
/* junk.c -- Junk list build/destroy test
* $Log: E:/vcs/junk/junk.c_v $
* Rev 1.2 28 Nov 1989 10:13:07 jvs
* Addition of memory shell
* Rev 1.1 20 Nov 1989 09:42:00 set
* Added name field for junk node tagging
* Rev 1.0 09 Nov 1989 18:12:30 jvs
* Initial revision.
*/
#include <stdio.h>
#include <stdlib.h>
#include "mshell.h"
/* Junk item structure */
typedef struct jnod {
struct jnod *junk_next ;
char *junk_name ;
int junk_data[3000] ;
} JUNK ;
/* Function prototypes */
JUNK *junk_list_build(void) ;
void junk_list_destroy(JUNK *) ;
JUNK *junk_open(char *) ;
void junk_close(JUNK *) ;
/* main() -- Entry point for test */
void main()
{
JUNK *jlist ;
while ((jlist = junk_list_build()) != NULL)
{
junk_list_destroy(jlist) ;
if (Mem_Used() != 0)
{
printf("*** Memory list not empty ***\n") ;
Mem_Display(stdout) ;
exit(1) ;
}
}
printf("*** Should never get here! ***\n") ;
}
/* junk_list_build() -- Build a list of junk items */
JUNK *junk_list_build()
{
JUNK *jlist ;
JUNK *jnew ;
jlist = NULL ;
while ((jnew = junk_open("name to identify junkitem")) != NULL)
{
jnew->junk_next = jlist ;
jlist = jnew ;
}
return jlist ;
}
/* junk_list_destroy() -- Destroy a list of junk items */
void junk_list_destroy(JUNK *jlist)
{
JUNK *jtmp ;
while (jlist != NULL)
{
jtmp = jlist ;
jlist = jlist->junk_next ;
junk_close(jtmp) ;
}
}
/* junk_open() -- Create a junk item */
JUNK *junk_open(char *name)
{
JUNK *jnew ;
jnew = (JUNK *) malloc(sizeof(JUNK)) ;
if (jnew != NULL)
{
jnew->junk_name = strdup(name) ;
if (jnew->junk_name == NULL)
{
return NULL ;
}
}
return jnew ;
}
/* junk_close() -- Close a junk item */
void junk_close(JUNK *jold)
{
free(jold) ;
}
[LISTING THREE]
/*----------------------------------------------------------------------
*++
* mshell.h -- Dynamic memory handler interface
* Description: mshell.h provides the interface definitions for the dynamic
* memory handler.
* See mshell.c for complete documentation.
*+-
* $Log$
*--
*/
/* Compilation options */
#define MEM_LIST /* Build internal list */
#define MEM_WHERE /* Keep track of memory block source */
/* Interface functions */
unsigned long Mem_Used(void) ;
void Mem_Display(FILE *) ;
/* Interface functions to access only through macros */
#if defined(MEM_WHERE)
void *mem_alloc(size_t, char *, int) ;
void *mem_realloc(void *, size_t, char *, int) ;
void mem_free(void *, char *, int) ;
char *mem_strdup(char *, char *, int) ;
#else
void *mem_alloc(size_t) ;
void *mem_realloc(void *, size_t) ;
void mem_free(void *) ;
char *mem_strdup(char *) ;
#endif
/* Interface macros */
#if !defined(__MSHELL__)
#if defined(MEM_WHERE)
#define malloc(a) mem_alloc((a),__FILE__,__LINE__)
#define realloc(a,b) mem_realloc((a),(b),__FILE__,__LINE__)
#define free(a) mem_free((a),__FILE__,__LINE__)
#define strdup(a) mem_strdup((a),__FILE__,__LINE__)
#else
#define malloc(a) mem_alloc(a)
#define realloc(a, b) mem_realloc((a),(b))
#define free(a) mem_free(a)
#define strdup(a) mem_strdup(a)
#endif
#endif
/*----------------------------------------------------------------------*/
[LISTING FOUR]
/*----------------------------------------------------------------------
*++
* mshell.c
* Memory management utilities
*
* Description
*
* mshell.c contains routines to protect the programmer
* from errors in calling memory allocation/free routines.
* The programmer must use the memory calls defined
* in mshell.h. When these calls are used, the
* allocation routines in this module add a data structure
* to the top of allocated memory blocks which tags them as
* legal memory blocks.
*
* When the free routine is called, the memory block to
* be freed is checked for legality tag. If the block
* is not legal, the memory list is dumped to stderr and
* the program is terminated.
*
* Compilation Options
*
* MEM_LIST Link all allocated memory blocks onto
* an internal list. The list can be
* displayed using Mem_Display().
*
* MEM_WHERE Save the file/line number of allocated
* blocks in the header.
* Requires that the compilier supports
* __FILE__ and __LINE__ preprocessor
* directives.
* Also requires that the __FILE__ string
* have a static or global scope.
*
*+-
*
* $Log$
*
*--
*/
#define __MSHELL__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mshell.h"
/* Constants */
/* --------- */
#define MEMTAG 0xa55a /* Value for mh_tag */
/* Structures */
/* ---------- */
typedef struct memnod /* Memory block header info */
{ /* ---------------------------- */
unsigned int mh_tag ; /* Special ident tag */
size_t mh_size ; /* Size of allocation block */
#if defined(MEM_LIST)
struct memnod *mh_next ; /* Next memory block */
struct memnod *mh_prev ; /* Previous memory block */
#endif
#if defined(MEM_WHERE)
char *mh_file ; /* File allocation was from */
unsigned int mh_line ; /* Line allocation was from */
#endif
} MEMHDR ;
/* Alignment macros */
/* ---------------- */
#define ALIGN_SIZE sizeof(double)
#define HDR_SIZE sizeof(MEMHDR)
#define RESERVE_SIZE (((HDR_SIZE+(ALIGN_SIZE-1))/ALIGN_SIZE) \
*ALIGN_SIZE)
/* Conversion macros */
/* ----------------- */
#define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
#define HDR_2_CLIENT(a) ((void *) (((char *) (a)) + RESERVE_SIZE))
/* Local variables */
/* --------------- */
static unsigned long mem_size = 0 ; /* Amount of memory used */
#if defined(MEM_LIST)
static MEMHDR *memlist = NULL ; /* List of memory blocks */
#endif
/* Local functions */
/* --------------- */
void mem_tag_err(void *, char *, int) ; /* Tag error */
#if defined(MEM_LIST)
void mem_list_add(MEMHDR *) ; /* Add block to list */
void mem_list_delete(MEMHDR *) ; /* Delete block from list */
#define Mem_Tag_Err(a) mem_tag_err(a,fil,lin)
#else
#define Mem_Tag_Err(a) mem_tag_err(a,__FILE__,__LINE__)
#endif
/************************************************************************/
/**** Functions accessed only through macros ****************************/
/************************************************************************/
/*----------------------------------------------------------------------
*+
* mem_alloc
* Allocate a memory block
*
* Usage
*
* void *
* mem_alloc(
* size_t size
* )
*
* Parameters
*
* size Size of block in bytes to allocate
*
* Return Value
*
* Pointer to allocated memory block
* NULL if not enough memory
*
* Description
*
* mem_alloc() makes a protected call to malloc()
*
* Notes
*
* Access this routine using the malloc() macro in mshell.h
*
*-
*/
void *
mem_alloc(
#if defined(MEM_WHERE)
size_t size,
char *fil,
int lin
#else
size_t size
#endif
)
{
MEMHDR *p ;
/* Allocate memory block */
/* --------------------- */
p = malloc(RESERVE_SIZE + size) ;
if (p == NULL)
{
return NULL ;
}
/* Init header */
/* ----------- */
p->mh_tag = MEMTAG ;
p->mh_size = size ;
mem_size += size ;
#if defined(MEM_WHERE)
p->mh_file = fil ;
p->mh_line = lin ;
#endif
#if defined(MEM_LIST)
mem_list_add(p) ;
#endif
/* Return pointer to client data */
/* ----------------------------- */
return HDR_2_CLIENT(p) ;
}
/*----------------------------------------------------------------------
*+
* mem_realloc
* Reallocate a memory block
*
* Usage
*
* void *
* mem_realloc(
* void *ptr,
* size_t size
* )
*
* Parameters
*
* ptr Pointer to current block
* size Size to adjust block to
*
* Return Value
*
* Pointer to new memory block
* NULL if memory cannot be reallocated
*
* Description
*
* mem_realloc() makes a protected call to realloc().
*
* Notes
*
* Access this routine using the realloc() macro in mshell.h
*
*-
*/
void *
mem_realloc(
#if defined(MEM_WHERE)
void *ptr,
size_t size,
char *fil,
int lin
#else
void *ptr,
size_t size
#endif
)
{
MEMHDR *p ;
/* Convert client pointer to header pointer */
/* ---------------------------------------- */
p = CLIENT_2_HDR(ptr) ;
/* Check for valid block */
/* --------------------- */
if (p->mh_tag != MEMTAG)
{
Mem_Tag_Err(p) ;
return NULL ;
}
/* Invalidate header */
/* ----------------- */
p->mh_tag = ~MEMTAG ;
mem_size -= p->mh_size ;
#if defined(MEM_WHERE)
mem_list_delete(p) ; /* Remove block from list */
#endif
/* Reallocate memory block */
/* ----------------------- */
p = (MEMHDR *) realloc(p, RESERVE_SIZE + size) ;
if (p == NULL)
{
return NULL ;
}
/* Update header */
/* ------------- */
p->mh_tag = MEMTAG ;
p->mh_size = size ;
mem_size += size ;
#if defined(MEM_LIST)
p->mh_file = fil ;
p->mh_line = lin ;
#endif
#if defined(MEM_WHERE)
mem_list_add(p) ; /* Add block to list */
#endif
/* Return pointer to client data */
/* ----------------------------- */
return HDR_2_CLIENT(p) ;
}
/*----------------------------------------------------------------------
*+
* mem_strdup
* Save a string in dynamic memory
*
* Usage
*
* char *
* mem_strdup(
* char *str
* )
*
* Parameters
*
* str String to save
*
* Return Value
*
* Pointer to allocated string
* NULL if not enough memory
*
* Description
*
* mem_strdup() saves the specified string in dynamic memory.
*
* Notes
*
* Access this routine using the strdup() macro in mshell.h
*
*-
*/
char *
mem_strdup(
#if defined(MEM_WHERE)
char *str,
char *fil,
int lin
#else
char *str
#endif
)
{
char * s ;
#if defined(MEM_WHERE)
s = mem_alloc(strlen(str)+1, fil, lin) ;
#else
s = mem_alloc(strlen(str)+1) ;
#endif
if (s != NULL)
{
strcpy(s, str) ;
}
return s ;
}
/*----------------------------------------------------------------------
*+
* mem_free
* Free a memory block
*
* Usage
*
* void
* mem_free(
* void *ptr
* )
*
* Parameters
*
* ptr Pointer to memory to free
*
* Return Value
*
* None
*
* Description
*
* mem_free() frees the specified memory block. The
* block must be allocated using mem_alloc(), mem_realloc()
* or mem_strdup().
*
* Notes
*
* Access this routine using the free() macro in mshell.h
*
*-
*/
void
mem_free(
#if defined(MEM_WHERE)
void *ptr,
char *fil,
int lin
#else
void *ptr
#endif
)
{
MEMHDR *p ;
/* Convert client pointer to header pointer */
/* ---------------------------------------- */
p = CLIENT_2_HDR(ptr) ;
/* Check for valid block */
/* --------------------- */
if (p->mh_tag != MEMTAG)
{
Mem_Tag_Err(p) ;
return ;
}
/* Invalidate header */
/* ----------------- */
p->mh_tag = ~MEMTAG ;
mem_size -= p->mh_size ;
#if defined(MEM_LIST)
mem_list_delete(p) ; /* Remove block from list */
#endif
/* Free memory block */
/* ----------------- */
free(p) ;
}
/************************************************************************/
/**** Functions accessed directly ***************************************/
/************************************************************************/
/*----------------------------------------------------------------------
*+
* Mem_Used
* Return amount of memory currently allocated
*
* Usage
*
* unsigned long
* Mem_Used(
* )
*
* Parameters
*
* None.
*
* Description
*
* Mem_Used() returns the number of bytes currently allocated
* using the memory management system. The value returned is
* simply the sum of the size requests to allocation routines.
* It does not reflect any overhead required by the memory
* management system.
*
* Notes
*
* None
*
*-
*/
unsigned long
Mem_Used(
void)
{
return mem_size ;
}
/*----------------------------------------------------------------------
*+
* Mem_Display
* Display memory allocation list
*
* Usage
*
* void
* Mem_Display(
* FILE *fp
* )
*
* Parameters
*
* fp File to output data to
*
* Description
*
* Mem_Display() displays the contents of the memory
* allocation list.
*
* This function is a no-op if MEM_LIST is not defined.
*
* Notes
*
* None
*
*-
*/
void
Mem_Display(
FILE *fp
)
{
#if defined(MEM_LIST)
MEMHDR *p ;
int idx ;
#if defined(MEM_WHERE)
fprintf(fp, "Index Size File(Line) - total size %lu\n", mem_size) ;
#else
fprintf(fp, "Index Size - total size %lu\n", mem_size) ;
#endif
idx = 0 ;
p = memlist ;
while (p != NULL)
{
fprintf(fp, "%-5d %6u", idx++, p->mh_size) ;
#if defined(MEM_WHERE)
fprintf(fp, " %s(%d)", p->mh_file, p->mh_line) ;
#endif
if (p->mh_tag != MEMTAG)
{
fprintf(fp, " INVALID") ;
}
fprintf(fp, "\n") ;
p = p->mh_next ;
}
#else
fprintf(fp, "Memory list not compiled (MEM_LIST not defined)\n") ;
#endif
}
/************************************************************************/
/**** Memory list manipulation functions ********************************/
/************************************************************************/
/*
* mem_list_add()
* Add block to list
*/
#if defined(MEM_LIST)
static void
mem_list_add(
MEMHDR *p
)
{
p->mh_next = memlist ;
p->mh_prev = NULL ;
if (memlist != NULL)
{
memlist->mh_prev = p ;
}
memlist = p ;
#if defined(DEBUG_LIST)
printf("mem_list_add()\n") ;
Mem_Display(stdout) ;
#endif
}
#endif
/*----------------------------------------------------------------------*/
/*
* mem_list_delete()
* Delete block from list
*/
#if defined(MEM_LIST)
static void
mem_list_delete(
MEMHDR *p
)
{
if (p->mh_next != NULL)
{
p->mh_next->mh_prev = p->mh_prev ;
}
if (p->mh_prev != NULL)
{
p->mh_prev->mh_next = p->mh_next ;
}
else
{
memlist = p->mh_next ;
}
#if defined(DEBUG_LIST)
printf("mem_list_delete()\n") ;
Mem_Display(stdout) ;
#endif
}
#endif
/************************************************************************/
/**** Error display *****************************************************/
/************************************************************************/
/*
* mem_tag_err()
* Display memory tag error
*/
static void
mem_tag_err(
void *p,
char *fil,
int lin
)
{
fprintf(stderr, "Memory tag error - %p - %s(%d)\n", p, fil, lin) ;
#if defined(MEM_LIST)
Mem_Display(stderr) ;
#endif
exit(1) ;
}
/*----------------------------------------------------------------------*/
[Example 1: Output from junk1 shows that there are really
two sources for the memory leaks. The first comes from the
malloc() of the JUNK structure at line 80. The second comes from
the strdup() of the name at line 83.]
*** Memory list not empty ***
Index Size File(Line) - total size 6238
0 6004 junk1.c(80)
1 26 junk1.c(83)
2 26 junk1.c(83)
3 26 junk1.c(83)
4 26 junk1.c(83)
5 26 junk1.c(83)
6 26 junk1.c(83)
7 26 junk1.c(83)
8 26 junk1.c(83)
9 26 junk1.c(83)
[Example 2: Obtuse coding for the allocation of memory in
junk_open() can hide the possibility of a memory leak when the
first allocation succeeds and the second allocation fails.]
if (((jnew = (JUNK *) malloc(sizeof(JUNK))) == NULL) ||
((jnew->junk_name = strdup(name)) == NULL))
{
return NULL ;
}