C Tutorial (26) : Heap memory

The heap is the collection of unused memory in your computer. The memory left over “after your program, your program’s variables, and your operating system’s workspace” comprises your computer’s available heap space.

c_heap_1

The heap is unused memory.

Many times you’ll want access to the heap, because your program will need more memory than you initially defined in variables and arrays.

The only way to access data stored in heap memory is through pointer variables.

Note : The free heap memory is called free heap or unallocated heap memory. The part of the heap in use by your program at any one time is called the allocated heap. Your program might use varying amounts of heap space as the program executes.

The heap is—the unused section of contiguous memory.

You’ll be allocating (using) and deallocating (freeing back up) heap memory as your program runs. When you request heap memory, you don’t know exactly from where on the heap the new memory will come. Therefore, if one statement in your program grabs heap memory, and then the very next statement also grabs another section of heap memory, that second section of the heap might not physically reside right after the first section you allocated.

If you allocate 10 bytes of heap memory at once, those 10 bytes will be contiguous. The important thing to know is that the next section of heap memory you allocate will not necessarily follow the first, so you shouldn’t count on anything like that.

Your operating system uses heap memory along with your program. If you work on a networked computer or use a multitasking operating environment such as Windows, other tasks might be grabbing heap memory along with your program. Therefore, another routine might have come between two of your heap-allocation statements and allocated or deallocated memory.

The pointer always points to the first heap value of the section you just allocated. If you want to access the memory after the first value on the heap, you can use pointer notation or array notation to get to the rest of the heap section you allocated.

Why Heap?

The problem with the variables you’ve learned about so far is that you must know in advance exactly what kind and how many variables you will want. If you define an array to hold 100 customer IDs, but the user has 101 customers to enter, your program can’t just expand the array at runtime. Some programmers have to change the array definition and recompile the program before the array can hold more values.

With the heap memory, however, you don’t have to know in advance how much memory you need. The heap memory your program uses can grow or shrink as needed. If you need another 100 elements to hold a new batch of customers, your program can allocate that new batch at runtime without needing another compilation.

Commercial programs such as spreadsheets and word processors must rely heavily on the heap. After all, the programmer who designs the program cannot know exactly how large or small a spreadsheet or word processing document will be. Therefore, as you type data into a spreadsheet or word processor, the underlying program allocates more data. The program likely does not allocate the data 1 byte at a time as you type because memory allocation is not always extremely efficient when done 1 byte at a time. More than likely, the program allocates memory in chunks of code, such as 100-byte or 500-byte sections.

So why can’t the programmers simply allocate huge arrays that can hold a huge spreadsheet or document instead of messing with the heap? For one thing, memory is one of the most precious resources in your computer. As we move into networked and windowed environments, memory becomes even more precious. Your programs can’t allocate huge arrays for those rare occasions when a user might need that much memory. Your program would solely use all that memory, and other tasks could not access that allocated memory.

Note : The heap enables your program to use only as much memory as it needs. When your user needs more memory, your program can allocate the memory. When your user is finished using that much memory you can deallocate the memory, making it available for other tasks that might need it.

Allocate Heap

The malloc() (for memory allocate) function allocates heap memory, and the free() function deallocates heap memory.

Tip : Be sure to include the stdlib.h header file in all the programs you write that use malloc() and free().

int * temps;       /* Will point to the first heap value */

Here is how you can allocate 10 integers on the heap using malloc():

temps = (int *) malloc(10 * sizeof(int));

That’s a lot of code just to get 10 integers. The line is actually fairly easy to understand when you see it broken into pieces. The malloc() function requires only a single value: the number of bytes you want allocated. Therefore, if you wanted 10 bytes, you could do this:

malloc(10);

The problem is that the previous description required not 10 bytes, but 10 integers. How many bytes of memory do 10 integers require? 10? 20? The answer, of course, is that it depends. Only sizeof() knows for sure.

10 contiguous integer locations on the heap. In a way, the computer puts a fence around those 10 integer locations so that subsequent malloc() calls do not intrude on this allocated memory.

malloc() always performs the following two steps (assuming that enough heap memory exists to satisfy your allocation request):

  1. Allocates the number of bytes you request and makes sure no other program can overwrite that memory until your program frees it 
  1. Assigns your pointer to the first allocated value

Subsequent malloc() function calls will go to other parts of the heap and will not tread on the allocated 10 integers.

c_heap_2

After allocating the 10 integers.

You know that you access contiguous memory using array notation, even if that memory begins with a pointer. Also remember that each set of allocated memory will be contiguous, so the 10 integers will follow each other just as if you allocated temps as a 10-integer array.

The (int *) is a typecast.

The * inside a typecast means that the typecast is a pointer typecast. malloc() always returns a character pointer. If you want to use malloc() to allocate integers, floating points, or any kind of data other than char, you have to typecast the malloc() so that the pointer variable that receives the allocation (such as temps) receives the correct pointer data type.

Note : You can use the malloc() function anywhere in your program, not just where you define variables and arrays. Therefore, when your program is ready for 100 double values, you can allocate those 100 double values. If you used a regular array, you would need a statement like this toward the top of main():

double myVals[100];    /* A regular array of 100 doubles */

Those 100 double values would sit around for the life of the program, taking up memory resources from the rest of the system, even if the program only needed the 100 double values for only a short time. With malloc(), you need to define only the pointer that points to the top of the allocated memory for the program’s life, not the entire array.

If There’s Not Enough Heap Memory

In extreme cases, not enough heap memory might exist to satisfy malloc()’s request. The user’s computer might not have a lot of memory, another task might be using a lot of memory, or your program might have previously allocated everything already. If malloc() fails, its pointer variable points to a null value, 0. Therefore, many programmers follow a malloc() with an if, like this:

temps = (int *) malloc(10 * sizeof(int)); 
if (temps == 0)
{
printf("Oops! Not Enough Memory!\n"); 
exit(1); // Terminate the program early
}

// Rest of program would follow...

If malloc() works, temps contains a valid address that points to the start of the allocated heap. If malloc() fails, the invalid address of 0 is pointed to (heap memory never begins at address zero) and the error prints onscreen.

Tip : Programmers often use the not operator, !, instead of testing a value against 0, as done here. Therefore, the previous if test would more likely be coded like this:

if (!temps)               /* Means, if not true */

Freeing Heap Memory

When you’re done with the heap memory, give it back to the system. Use free() to do that. free() is a lot easier than malloc(). To free the 10 integers allocated with the previous malloc(), use free() in the following manner:

free(temps);       /* Gives the memory back to the heap */

If you originally allocated 10 values, 10 are freed. If the malloc() that allocated memory for temps had allocated 1,000 values, all 1,000 would be freed. If you use temps after the previous free(), you run the risk of overwriting memory and possibly locking up your computer, requiring a reboot.

If you fail to free allocated memory, your operating system reclaims that memory when your program ends. However, forgetting to call free() defeats the purpose of using heap memory in the first place. The goal of the heap is to give your program the opportunity to allocate memory at the point the memory is needed and deallocate that memory when you’re finished with it.

Multiple Allocations

An array of pointers often helps you allocate many different sets of heap memory.

Here is how you could allocate an array of 50 pointers:

int * temps[50];       /* 50 integer pointers */

The array will not hold 50 integers (because of the dereferencing operator in the definition); instead, the array holds 50 pointers. The first pointer is called temps[0], the second pointer is temps[1], and so on. Each of the array elements (each pointer) can point to a different set of allocated heap memory.

Consider the following section of code that the forecaster might use:

for (ctr = 0; ctr < 50; ctr++)
{
puts("How many readings for the city?") 
scanf(" %d", &num);

// Allocate that many heap values
temps[ctr] = (int *)malloc(num * sizeof(int));
// This next section of code would ask for each temperature reading for the city
}

// Next section of code would probably be calculations related
// to the per-city data entry 
// Don't forget to deallocate the heap memory when done

for (ctr = 0; ctr < 50; ctr++)
{
free(temps[ctr]);
}

Throughout the program, some sections might need extra memory, whereas other sections do not. The heap lets you use memory efficiently.

Figure shows you what the heap memory might look like while allocating the temps array memory (after the first 4 of the 50 malloc() calls). As you can see, temps belongs to the program’s data area, but the memory each temps element points to belongs to the heap.

c_heap_3

                                Each temps element points to a different part of the heap.

/* The program looks for a number of random integers, 
   allocates an array and fills it with numbers between 1 and 500 
   and then loops through all the numbers and figures out the
   smallest,the biggest, and the average. It then frees the memory. */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

main()
{
int i, aSize;
int * randomNums;
time_t t;
double total = 0;
int biggest, smallest; 
float average;

srand(time(&t));

printf("How many random numbers do you want in your array? "); 
scanf(" %d", &aSize);

// Allocate an array of integers equal to the number of 
// elements requested by the user

randomNums = (int *) malloc(aSize * sizeof(int));

// Test to ensure that the array allocated properly
if (!randomNums)
{
printf("Random array allocation failed!\n"); 
exit(1);
}

/* Loops through the array and assigns a random number 
   between 1 and 500 to each element */
for (i = 0; i < aSize; i++)
{
randomNums[i] = (rand() % 500) + 1;
}

/* Initialize the biggest and smallest number for comparison's sake */
biggest = 0; 
smallest = 500;

/* Loop through the now-filled array testing for the random
   numbers that are biggest, smallest, and adding all numbers
   together to calculate an average */
for (i = 0; i < aSize; i++)
 {
 total += randomNums[i];
 if (randomNums[i] > biggest)
{
biggest = randomNums[i];
}
if (randomNums[i] < smallest)
{
smallest = randomNums[i];
}
}
average = ((float)total)/((float)aSize);
printf("The biggest random number is %d.\n", biggest); 
printf("The smallest random number is %d.\n", smallest); 
printf("The average of the random numbers is %.2f.\n", average);

// When you use malloc, remember to then use free
free(randomNums);
return(0);
}

In fact, when writing this program originally total was originally an int, and when I set the array to 10 million values, the sum total of the random numbers was bigger than the allowed maximum for an int variable. My average calculation was thus wrong. When that variable was increased to a double, I was able to build even bigger arrays of random numbers.

 

Array & Pointers < Prev                                                                      Next > Data structure

Advertisements

2 comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s