Node:Memory allocation, Next:, Previous:struct, Up:Data structures



Memory allocation

Most variables in C have a fixed size. For example, a string declared to be 200 bytes long will always be 200 bytes long throughout the program. Sometimes, however, you will need variables whose size can vary. You might want a string whose size can vary between 0 and 100 kilobytes, for instance. We have already seen occasions where this sort of string is needed with the getline function. (See getline.)

This is where dynamic data, or data whose size can vary, comes in. Dynamic data is created via the process of memory allocation, that is, assigning a block of memory to a variable. Blocks of memory are usually assigned with the malloc function (the function name is from the phrase "memory allocation"), and can be resized with the realloc ("memory reallocation") function, and even merged back into the pool of available memory with the free function.

The malloc function takes one argument, the number of bytes to allocate. It returns a void pointer, which provides the address of the beginning of a block of memory that the program can use. This void pointer can be assigned to any other type of pointer. The only way to make use of the block of memory that has been allocated is through its pointer; in that sense, the block is not a "real" variable, that is to say, you cannot assign a value to the memory block directly. Instead, the address returned by malloc enables you to use the block indirectly; in this way, the block can contain any kind of value a real variable can. Having to use blocks indirectly through pointers is a small price to pay for the flexibility of dynamic data.

The following code example allocates a ten-byte string:

char *my_string;
my_string = (char *) malloc(10+1);

Notice that the void pointer returned by malloc is cast to a character pointer (type char *) before it is assigned to my_string. (See The cast operator.) Also notice that we have actually allocated 11 bytes of space; this is because the 11th byte must contain a null character that terminates the string but does not count toward its actual length. Careful! The newly-allocated block will be filled with garbage.

To reallocate the memory, use the realloc function. This function takes two parameters. The first is the pointer to the memory block to be reallocated, and the second is a number of type size_t that specifies the new size for the block. It returns a void pointer to the newly reallocated block. Here is how to reallocate the block allocated for my_string above, to a new size of 1000 bytes:

my_string = (char *) realloc (my_string, 1001);

The new block will contain all the data in the old block, followed by enough space to pad out the block to the new length. The new space will be filled with garbage.

Finally, to free up the memory allocated to a block and return it to the common pool of memory available to your program, use the free function, which takes only one argument, the pointer to the block you wish to free. It does not return a value.

free (my_string);

It is also possible to allocate the memory for a structure when it is needed and use the -> operator to access the members of the structure, since we must access the structure via a pointer. (See the code sample following the next paragraph for an example of how to do this.) If you are creating complex data structures that require hundreds or thousands of structure variables (or more), the ability to create and destroy them dynamically can mean quite a savings in memory.

It's easy enough to allocate a block of memory when you know you want 1000 bytes for a string, but how do you know how much memory to allocate for a structure? For this task, C provides the sizeof function, which calculates the size of an object. For example, sizeof (int) returns the numbers of bytes occupied by an integer variable. Similarly, sizeof (struct personal_data) returns the number of bytes occupied by our personal_data structure. To allocate a pointer to one of these structures, then set the year_of_birth member to 1852, you would write something like the following:

struct personal_data* my_struct_ptr;

my_struct_ptr = (struct personal_data*)
  malloc (sizeof (struct personal_data));
my_struct_ptr->year_of_birth = 1852;