Home C Programming All Sections
 
C Memory Management
Simple explanations of memory management in C

Manpages
Man malloc
Man free
Man realloc
Man calloc
Man strcpy
Man memcpy
Man memmove
Man memset
 

See Also
String Functions in C
 


Hosted by...
Linode
 

Sponsored Links

 
Introduction

C is an extremely powerful programming language. One of the most powerful aspects of the C programming language is the ability to point to locations in memory. This ability allows for easy memory access, but it can also lead to programming disasters. If not used properly, pointers can cause erratic behavior and cause program crashes.


Basics - What is "Allocating Memory"?

In order to "set aside" or "allocate" memory in C, we first need a variable to "point to" a position in memory. This is simply done by declaring a pointer of the correct type. Assuming we wish to declare a character pointer:

char *ptr;

Note that the only difference between declaring a character and a character pointer is the asterisk (*) character before the variable name. Now that we have a valid pointer, we should do something with it. We do not know what the pointer is pointing to, so we can't read it yet - first we have to point to something. We can have the pointer point to a string:

ptr = "Hello World!";

Or we could set our pointer to point to a specified amount of memory:

size_t size = 64;
ptr = (char*)malloc(size);

The malloc function sets aside memory of at LEAST size bytes and places the address of the allocated memory into our ptr variable. Ptr is now ready to be written to! Assuming we've allocated enough space, we could put a string into our memory:

strcpy(ptr, "Hello World!");

Both of the above ways have ptr point to the string "Hello World", but only the malloc allows the memory to be cleaned by the user. This is done by the free() function call:

free(ptr);

Freeing the pointer allows the memory to be re-used by another part of the program and keep the overall memory usage for the application smaller. In this particular example, the memory free'd is negligable - but in many instances, it is essential to free unused memory.


Different Ways to Alloc - Malloc, Calloc, Realloc

There are three ways for a programmer to allocate variable-length memory: (void*)malloc(), (void*)calloc(), and (void*)realloc(). Each of these allocation methods has a specific purpose.

  • (void*)calloc(size_t nmemb, size_t size) allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory. The memory is initialized to NULL bytes (all bytes are set to 0).
  • (void*)malloc(size_t size) allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized.
  • (void*)realloc(void *ptr, size_t size) changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged to the minimum of the old and new sizes - newly allocated memory will be uninitialized.

Note that realloc(void *ptr, size_t size) can be used in place of calloc(size_t nmemb, size_t size) by sending a NULL pointer as the first argument. It can also be used in place of free(void *ptr) by indicating a size of 0.


Moving Memory

Now that we have our allocated memory, we want to store some data. There are a few ways to move data into our allocated space, including but not limited to:

  • (char*)strcpy(char *dest, const char *src) copies a string including the terminating NULL ('/0') from src to dest. This function could be dangerous if the source string is larger than the size of the destination. A safer alternative would be strncpy which includes the maximum size of the destination as an argument. These functions are useful for copying strings. See C String Functions for more on String Functions.
  • (void*)memcpy(void *dest, const void *src, size_t n) copies n bytes from src to dest. This is similar to strcpy() except the copy does not stop at a NULL byte. This function is useful for copying binary data which may contain at least one NULL byte. The memory areas may not overlap, so the original memory is left unaltered (hence memcpy). Remember that the size n can be sizeof(struct) or multiples thereof in the case of copying structures.
  • (void*)memmove(void *dest, const void *src, size_t n) is the same as memcpy except that the source and destination are allowed to overlap. Allowing overlapping has a potential for destroying the source data (hence memmove).
  • (void*)memset(void *s, int c, size_t n) sets the first n bytes starting at s with constant byte c. If c is 0 (zero), memset is the same as bzero.

Cleaning Memory

When allocated memory is no longer needed, it should be freed. Freeing memory makes it once again available to be allocated by the program. This means that the next time memory needs to be allocated, it can potentially be done without requiring the program to get more memory from the kernel. Memory no longer in use should always be freed in order to reduce the amount of required memory.


Memory Leaks

When managing memory, it is absolutely essential that memory br freed before using the memory to point elsewhere in memory. If this is not done, then the original memory can never be freed because you no longer point to it. This is known as a memory leak, and is something that should be avoided when writing a program.

If you think that a program has memory leaks, there are a few simple ways to track where the leaks are coming from:

  • Make sure that a pointer to allocted memory is never assigned until after it is freed. That is, do NOT do anything like the following:

    char *ptr;
    ptr = (char*)malloc(size);
    ptr = "Hello World!";

    or:

    char *ptr;
    ptr = (char*)malloc(size);
    strcpy(ptr, "Hello World!");
    for( ; *ptr; ptr++) { ... }

    because after either of these segments of code, there is no pointer to the beginning of the allocated memory.

  • Make sure that all allocated memory is freed before returning from a function. Unless a static variable is used as a pointer to allocated memory, the pointer will be lost upon returning from a function. This will effectively keep the memory allocated, but keep no way of knowing where that memory is. Do NOT do anything like the following:

    char *ptr;
    ptr = (char*)malloc(size);
    { ... }
    /* normally 'free(ptr);' here... but not in this example!*/
    return 0;

    If ptr is a static variable, then there is no need to free, but...

  • Make sure not to malloc() a static pointer a second time! If a static pointer is used, the value should be checked for a value before it is assigned as a memory poiter. The following simple check should suffice:

    static char *ptr = NULL;
    if(!ptr) { ptr = (char*)malloc(size); }
    { ... }
    /* no need to 'free(ptr);' - we will still point to this next time we call this function */
    return 0;

    If the size of the memory segment pointed to by ptr needs to grow, a simple call to realloc() is all that's needed. The only problem with pointing to memory with a static pointer within a function is that there is no easy way to free() the memory (unless it's global...) upon termination of the program. This is not a major issue, though, because upon termination, all memory is automatically freed.


Now what do I do? - Conclusions

These functions allow us to safely allocate specified chunks of data. This data can then be written/read by the program as needed. Memory management can be hard to initially grasp, but once it is mastered it proves to be a priceless tool.

 
Manpages About Webmaster