Static and Dynamic Memory

Static memory is what you have seen and used so far: variables (including pointer variables) and arrays of fixed size. You can work with these blocks of memory in your program code using their names as well as their addresses (see Chapter 9, “Working with Pointers”).

With static memory you define the maximum amount of space required for a variable when you write your program:

char a[1000]; // Fixed at runtime.

Whether needed or not, all of that memory will be reserved for that variable and there is no way to change the amount of static memory while the program runs.

Dynamic memory is different. It comes in blocks without names, just addresses. It is allocated when the program runs, in chunks from a large pool that the standard C library manages for you.

You use the standard library's malloc() function to request a block of a certain size—in bytes—from the available memory pool. The size is dynamic and can be determined when the program runs (Figure 10.1):

int memsize;
// memsize gets a value at runtime.
x = malloc(memsize);

Figure 10.1. A block of dynamic memory (represented in the bottom grid) does not have a name as variables and pointers do (the top grid). Referring to a dynamic memory block's address is the only way to access it.


When you are done with the block, you return it to the pool using the free() function:

free(x);

Every call to malloc() must be balanced by a call to free(). A missing or double free() call is considered a bug (specifically, a missing free() statement creates a memory leak).

If enough memory is available to satisfy your request, the malloc() function returns the starting address of the newly allocated block. You usually store this address in a pointer variable for later use.

There is a small problem, though. Remember from the previous chapter that a pointer variable can only hold an address of the appropriate type, that is, a char pointer variable can only hold the address of a char, an int pointer variable can only hold the address of an integer, and so on. However, the blocks of memory whose starting addresses malloc() returns are just that: untyped blocks of memory of the requested size. The type of the data stored in them is usually determined later on in the program, after the malloc() call.

To accommodate addresses of such untyped blocks of memory, C provides a special type of pointer variable called a void pointer. You define such a pointer like so:

void *x;

A void pointer variable can store addresses of any type, including integers, chars, floats, etc. It can also store the addresses of the untyped blocks of memory that malloc() returns:

void *x;
x = malloc();

Finally, as an extra precaution, the pointer should be set to NULL after you have freed the associated memory:

void *x;
x = malloc (size);
free(x);
x = NULL;

By taking this final step, the program knows that x no longer refers to a block of memory. This is desired so that any uses of x (unless it is assigned another value beforehand) will fail rather than create hard-to-debug oddities.

Let's write our first dynamic memory example using malloc() and free(). The example is short but introduces the basic syntax that will be used again in all subsequent examples, so it is important for you to understand this process clearly.

To request and return dynamic memory

1.
Create a new file or project in your text editor or IDE.

2.
Type the standard beginning lines of code (Script 10.1):

/* memory c - Script 10.1 */
#include <stdio.h>
#include <stdlib.h>

Script 10.1. The malloc() and free() functions are used to allocate and release blocks of dynamic memory.


Note the inclusion of the stdlib.h header file. It provides the declarations for the malloc() and free() functions required by the compiler when working with dynamic memory.

3.
Begin the main function:

int main (void) {

4.
Define a void pointer variable:

void *x;

5.
Call the malloc() function to request a block of memory 1000 bytes long:

x = malloc(1000);

The address returned by the malloc() function—which indicates the starting address of the requested block of memory—will be assigned to the void pointer x.

6.
Print the address:

printf("Starting block address:
 %p.
", x);

To allow you to confirm that the process worked and to view the minimal results, the address stored in the void pointer will be printed. The %p signifier is used, since x is a pointer.

7.
Return the block to the pool using the free() function:

free(x);
x = NULL;

The free() function takes one argument: the address of the block to be returned to the pool. After this call to free(), the specific block of memory pointed to by the address may no longer be used by the program. In other words, referring to x may cause problems since it no longer points to a reserved block of memory. For this reason, the second line sets the value of x to NULL, so that any inadvertent references to x after this will quite clearly be wrong.

8.
Complete the main function:

    getchar();
    return 0;
 }

9.
Save the file as memory.c, compile, and debug as necessary.

10.
Run the application (Figure 10.2).

Figure 10.2. The base address of the memory block returned by the malloc() function is printed to confirm that the process worked.


So far you didn't actually use the memory, but you'll be getting to that in the next sections.

✓ Tips

  • Note that the term static memory used in this section has nothing to do with the static C keyword. The static keyword was introduced in Chapter 7, “Creating Your Own Functions,” and is pertinent to function variables. The term static memory as used in this section means that the amount of memory is set at compile time and cannot be changed during runtime.

  • The block of memory returned by malloc() may be filled with random garbage. Most of the time, this is not a problem because you usually write to memory first before reading from it. In some situations, though, you might need clean memory that has been overwritten with zeros. Instead of writing a for loop to clear the memory yourself, you can use the calloc() function instead of malloc(). It will prefill the memory block with zeros, but that process will take some time. Because of the additional time needed to clear the memory, you should not needlessly use calloc() instead of malloc().

  • In case you were curious, the word malloc is short for memory allocation.


The NULL Pointer

There is a special address value called the NULL pointer (also defined in the stdlib.h header file). When a pointer variable is set to this value, it means that the pointer does not point to any object at all:

void *x;
x = NULL; /*x doesn't point anywhere.

When malloc() fails to allocate a block of the requested size, it returns the NULL pointer value instead of a real address. In your programs, you should always check the return value of malloc() to see if it is equal to NULL. Then you can react accordingly:

x = malloc(1000);
if (x == NULL) {
    printf("Unable to allocate!
");
    // Handle the error.
}

We have omitted this vital check in the examples for brevity (and because these simple exercises should work regardless), but you should start incorporating it before creating any real-world applications.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset