home *** CD-ROM | disk | FTP | other *** search
File List | 1990-06-20 | 17.1 KB | 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 ;
- }
-
-
-