views:

155

answers:

4

I'm aware of the following:

  • malloc
  • calloc
  • realloc

What are the differences between these? Why does malloc seem to be used almost exclusively? Are there behavioral differences between compilers?

+8  A: 

malloc allocates memory. The contents of the memory are left as-is (filled with whatever was there before).

calloc allocates memory and sets its contents to all-zeros.

realloc changes the size of an existing allocated block of memory, or copies the contents of an existing block of memory into a newly allocated block of the requested size, and then deallocates the old block.

Obviously, realloc is a special-case situation. If you don't have an old block of memory to resize (or copy and deallocate), there's no reason to use it. The reason that malloc is normally used instead of calloc is because there is a run-time cost for setting the memory to all-zeros and if you're planning to immediately fill the memory with useful data (as is common), there's no point in zeroing it out first.

These functions are all standard and behave reliably across compilers.

Tyler McHenry
`calloc` is particularly useful for allocating arrays of structures, since it takes 2 args: `calloc(num_of_items, size_of_item)`. It's more work to fill such an array with useful data (as Tyler says) so there's a fair argument in favor of the extra cost to initialize the memory to a known (zero) value.
Stephen P
`calloc` also has the advantage of performing the multiplication for you, so that on a non-buggy implementation it will check and fail on overflow.
R..
+1  A: 

calloc is likely just implemented as something similar to:

void * calloc(size_t nmemb, size_t size) {
      size_t len = nmemb * size);
      void * ptr = malloc(len);
      if (ptr) {
          return memset(ptr, 0, len);
      }
      return ptr;
}

So it just adds a multiply before and a clear after the malloc.

malloc could be (but probably isn't ever) implemented as:

void * malloc(size_t size) {
      return realloc(NULL, size);
}

since you can pass realloc a NULL pointer as the previous pointer, but malloc is most likely not implemented like that because it would slow down all mallocs and because realloc probably uses malloc.

The main thing to know about realloc is that it is often capable of determining the actual size of the block of memory that any of the heap allocation routines returned and see if the block is big enough already or in some cases if it would be best try to shrink the block or move it.

void realloc(void * ptr, size_t size) {
    size_t alen = MALLOC_LENGTH_OF(ptr); // this just stands for some method of determining
                                     // the size of the block that was allocated, and could
                                     // be looking it up in a table or looking at a place
                                     // several bytes before the pointer, or some other
                                     // implementation specific thing
    if (0 == size) {
       free(ptr);
       return NULL;
    }
    if (alen >= size) {
        return ptr; // big enough, and not worrying about it being too big
    }
    void new_ptr = malloc(size); // since I said that malloc most likely isn't
                                // implemented using realloc using malloc here is ok
    if (new_ptr && ptr) {
       memcpy(new_ptr, ptr, alen);
    }
    free(ptr);
    return new_ptr;
}

Malloc is used more because it is simplest. Programmers can often make their allocations big enough to begin with that they don't have to worry about resizing them with realloc, and very often clearing the memory is not needed.

There are different behaviors among different libraries, and you can even link programs with other versions just to get this different behavior without (usually without changing the compiler).

Some malloc libraries are for debugging and error detection. Others may offer better performance. Some are optimized for allocating memory for several different threads at the same time, and avoid the different threads needing to lock down the entire heap to perform an allocation or free.

All of them should provide the same semantics from the applications perspective, though.

nategoose
vote down for using start curly braces on the same line as the block! (not really tho)
Firoso
@Firoso: Doing that makes _diff_ produce better output when comparing versions, as well as shrinks the code. Since I also use curly braces around one line blocks it really helps to put them on the same line as the block opening code.
nategoose
@nategoose: if you refer to diff -p option, I believe the heuristic is that a line contains a function name if it has letters at column 1.
ninjalj
@nategoose: As R. said elsewhere, calloc() on many implementations (including glibc, FreeBSD libc and HP-UX libc IIRC) checks for integer overflow, to avoid potential security vulnerabilities.
ninjalj
+3  A: 

Besides the ones that you are mentioning (some are extensions):

  • variables on the stack are also dynamically allocated. Recursion is a way to allocate and use that.
  • C99 has variable length arrays (as BlueRaja mentions).
  • On some systems you even have an alloca call that lets you allocate variable length chunks in the stackframe.
  • POSIX has mapping of memory segments and files, with combinations of shm_open or open and mmap.
  • SysV IPC has shmget etc calls
Jens Gustedt
Windows also has ways to share memory between processes and map files into memory.
RBerteig
+2  A: 

One method to allocate memory that hasn't been mentioned is alloca(size_t size) which reserves size bytes of memory on the current stack frame and automatically frees the memory again when you leave the stack frame.

gilligan