Dynamic Memory
Currently, we can only declare arrays to be of a constant size (like 10). This is not always
convenient – sometimes we want to make the size based on some user input. If we want to
allocate a dynamic amount of space, we need to use C’s dynamic memory functions. Each of
these functions is in <stdlib.h>
.
malloc
This function allocates a contiguous block of memory with the specifies size (number of bytes). It returns a void pointer to the block of memory. (This pointer will be automatically cast to the correct type when you store it.)
Here is the prototype:
void* malloc(int numBytes);
For example, we could allocate an array like this:
int nums1[5];
Or we could do the same thing using malloc
. If we use malloc
, we need to specify the
number of bytes to reserve. We want 5 ints, and each int takes up sizeof(int)
bytes. So,
the total needed is 5*sizeof(int)
:
//The result of malloc is automatically cast to an int*
int* nums2 = malloc(5*sizeof(int));
Now, we can treat nums2
just like an array. For instance, if we wanted to initialize all elements
in nums2
to 0:
int i;
for (i = 0; i < 5; i++) {
nums2[i] = 0; //The compiler converts this to *(nums2+i) = 0
}
Allocating arrays with malloc has several key difference from standard array allocation:
malloc
can handle a variable for the desired size; a standard array cannot- The result of
malloc
is a pointer; the result of a standard array allocation is a constant pointer malloc
memory is allocated on the heap. If there is not enough space to do the allocation,malloc
will return NULL. An array is allocated on the program stack – if there is not enough space, the program simply won’t compile.
calloc
The calloc
function is very similar to malloc
. The only difference is that when arrays are
allocated using calloc
, all elements are automatically initialized to 0.
Here is the prototype:
void* calloc(int numElems, int sizePerElem);
The prototype of calloc
is also a little different than the one for malloc
. It takes two
arguments – the number of elements you want in the array, and the number of bytes needed for
each elements. Like malloc
, calloc
returns a void pointer to the contiguous block of
memory it allocated. This pointer will be automatically cast to the appropriate type when you
store it.
Here’s how to create an array of 10 ints, all initialized to 0:
int* nums = calloc(10, sizeof(int));
Now you can use nums
just like an array. For example:
nums[0] = 4;
Like malloc
, calloc
will return NULL if there is not enough space to do the allocation. In
both cases, it’s a good idea to check if the pointer is NULL before you use it.
For example:
int* nums = calloc(10, sizeof(int));
if (nums == NULL) {
printf("Not enough space.\n");
}
else {
//Use nums as usual
}
realloc
The realloc
function allows you to easily expand and shrink the space allocated for an array.
Here is the prototype:
void* realloc(void* origPtr, int newSize);
This function takes your original pointer and the desired new size in bytes. It looks for a contiguous block of memory with the desired size. If it can find one, it copies the contents of the old array into the new block of memory. Then it releases the space needed for the old array, and returns a void pointer to the new block of memory.
The realloc
function doesn’t always behave as you intend. Here are the possible return
values of realloc
:
- NULL (if not enough space is found)
- The original pointer (if there is enough space at that location)
- A new pointer to a different spot in memory
Suppose we allocate the nums
array like this:
int* nums = malloc(10*sizeof(int));
Now we decide that we want nums
to hold 15 elements instead of 10. Here’s what we might
try:
nums = realloc(nums, 15*sizeof(int));
Suppose that realloc
could not find enough space to grant the request – so it returns NULL.
This means that we assign NULL to our original nums
pointer. Now nums
does not reference
the original array – in fact, nothing does. The original array is stuck in memory with no way to
get at it – this is called a memory leak.
To fix this problem, assign a temporary pointer to the result of realloc
. Then, if it’s not
NULL, reassign the original pointer. This keeps you from losing your array:
int *temp = realloc(nums, 15*sizeof(int));
if (temp != NULL) {
nums = temp;
}
free
In Java and C#, any memory that you’re no longer using will be cleaned up by the garbage collector. C has no garbage collector, so you are in charge of releasing memory that you’re done with. If you never release any allocated memory, you will eventually run out of space.
The C function that releases dynamic memory is called free
. Here is the prototype:
void free(void* pointer);
Note that even though free
takes a void pointer, it can take any type of pointer that has been
dynamically allocated.
Here’s an example of using free
:
int* nums = malloc(10*sizeof(int));
int i;
for (i = 0; i < 10; i++) {
nums[i] = i;
}
//done using nums
free(nums);