[Introduction]

Unix Incompatibility Notes:
Memory Management Functions

Jan Wolter

This page describes portability issues related to various Unix memory allocation functions. It is incomplete.

Peter Burden's Using of Memory Allocation Functions page is a good introduction to the use of dynamically allocated memory in C.

Performance Notes

The actual performance of Unix memory management systems varies widely in how efficiently they use memory, how fast they are, and how agressively they reuse freed memory. (The djgpp new malloc page is interesting.)

Most modern Unixes have memory allocation systems that perform pretty well for ordinary applications. Some cleverness in coding can give you performance improvements though. As a simple example, instead of doing:

  thing= (struct whatever *)malloc(sizeof(struct whatever));
  thing->buf= (char *)malloc(100);
  :
  /* process, process, process */
  :
  free(thing->buf);
  free(thing);
you might do
  thing= (struct whatever *)malloc(sizeof(struct whatever) + 100);
  thing->buf= (char *)(thing+1)
  :
  /* process, process, process */
  :
  free(thing);
If we are allocating and freeing two hunks of memory together, why not just allocate one bigger hunk? It should be faster, and less memory should be wasted in rounding up hunk sizes (as many implementations do). This basic idea can be extended to routines that read in whole linked lists. The cost, of course, is that the code become more obscure and more brittle. For example, if you later decide you'd like to do a realloc() to increase the size of thing->buf, you'll have to do it in a non-obvioius way. I suspect that in most applications, this kind of thing is not worth doing.

Some versions of Unix include more than one library of memory management functions. In Solaris 2, for example, if you link with "-lbsdmalloc" you get a library that is faster (but less space efficient) than the default one, and if you link with "-lmalloc" you get one that is slower, but more space efficient. All include the standard malloc(), free(), and realloc() entry points. Similar SGI has a "-lmalloc" option that is faster than it's default. It may also be possible to find third party memory management libraries that will fit the performance needs of your application better.

Debugging

Memory management errors can be exceedingly difficult to debug. A wide variety of tools are available. A simple one available on most Linux and GNU libc systems is to set the environment variable MALLOC_CHECK. If set, a slower but more fault-tolerant memory management library is used. If the variable is set to 0, errors are silently ignored. If set to 1, diagnostics are printed to stderr. If set to 2, the process aborts with an error message on the first error. This will, however, not detect errors caused by writing to unallocated memory or memory leaks. More sophisticated tools exist that can help with those.

Headers

For ANSI C, you typically want
    #include <stdlib.h> 
On systems that don't have stdlib.h you might try including malloc.h or alloc.h instead. However, I usually just fall back to declaring things directly:
    #ifdef HAVE_STDLIB_H
    #  include <stdlib.h>
    #else
       char *malloc(), *realloc();
    #endif
Here we declare as a char * instead of as a void * because many systems old enough not to have stdlib also don't have void.

Functions

Here are the memory allocation functions:
alloca()
This allocates temporary memory in the calling functions stack frame, which will be automatically freed when the calling function returns. This can be very fast - on many machines it is implemented with a single inlined instruction adjusting the stack pointer.

However, alloca() should not be used in portable code. Though it exists on most machines, it is not in the standards and is apparantly difficult to implement on architectures without a conventional stack. It is often buggy.

You may need the special header file alloca.h since stdlib.h often doesn't define this non-standard call.

brk(), sbrk()
These functions directly adjust the size of the data segment. They are not portable to all machines and not supported by any standard.

The type of the parameter for sbrk() varies widely. Possible types include int, ptrdiff_t and intptr_t.

calloc()
Available everywhere.

Note that it sets all bits to zero, which means that technically the values are not necessarily valid null pointer values. However, machines where null pointers are something other than all-zero-bits are exceedingly rare if not completely imaginary.

Modern implementations always return a void * pointer, but older ones often returned a char *. The return values from calloc(), malloc() and realloc() should always be explicitly type cast to the appropriate pointer type, like buf= (char *)calloc(20).

cfree()
Apparantly an alternate name for free() that exists on SunOS. Not portable.

free()
Available everywhere.

memalign()
Allocates blocks with a particular alignment, that is, with addresses that are multiples of the alignment value given in the call. This alignment value must be a power of two. Memory alignment can be important when doing things like allocating buffers for direct I/O to a block device.

Memalign() comes from BSD Unix and exists on some BSD derivatives like SunOS. It is not in the standards. It is not portable.

On most modern systems, malloc() returns addresses which are multiples of 8 (or 16 on 64-bit systems), which mostly eliminates the need for memalign().

On some systems, memory allocated with memalign() cannot be released with free() or in any other way, short of exit().

malloc()
Available everywhere.

malloc(0) may either return NULL or a pointer to a size zero hunk of memory.

Note that on machines with 16 bit ints, you may not be able to allocate chunks of memory bigger than MAX_INT.

mallop(), mallinfo()
These are unique to the Solaris -lmalloc library.

posix_memalign()
Just like memalign(), but the boundary must be a power of two times the size of a void pointer. Memory allocated with posix_memalign() can be deallocated with free().

This was introduced in POSIX 1003.1.d, but doesn't seem to be around everywhere.

realloc()
Available everywhere.

The ANSI standard says calling realloc(NULL,size) is equivalent to malloc(size) and realloc(ptr,0) is equivalent to free(ptr), but not all pre-ANSI libraries support this.

Some systems allow pointers to freed blocks to be passed to realloc, so long as there have been no memory allocation calls (eg malloc() or calloc()) since the block was freed. This is non-portable.

valloc()
Aligns memory aligned on a page boundary. Should be avoided diligently on systems with very large page sizes.

Not portable. On systems that don't have it, it is reasonable to just use malloc() instead. Originated in 3.0 BSD and declared obsolete in 4.3 BSD. Not included in ANSI or POSIX standards.

In some systems, memory allocated by valloc() can not be passed to realloc() or free().


Jan Wolter (E-Mail)
Thu Mar 28 11:00:07 EST 2002 - Original Release.
Fri Apr 4 10:53:22 EST 2003 - Various additions.
Wed Dec 21 10:41:51 EST 2005 - Various clarifications.