Dynamic Memory Allocation in C
Posted: November 25th, 2012 | Author: Aneesh Dogra | Filed under: C, Computer Science | Tags: C, dynamic memory allocation, memory, Programming | 1 Comment »C provides a suite of functions to provide robust and concrete dynamic memory allocation namely malloc, realloc, calloc and free.
1) Malloc
void * malloc ( size_t size );
size: The number of bytes you wish to allocate.
Malloc allocates a block of memory and returns a void pointer to the start of that memory location. If the function failed to allocate the requested block of memory, a null pointer is returned.
When you malloc a block, it actually allocates a bit more memory than you asked for. This extra memory is used to store information such as the size of the allocated block, and a link to the next free/used block in a chain of blocks, and sometimes some “guard data” that helps the system to detect if you write past the end of your allocated block. This data serves as an easy and fast reference to the system when you use other calls like realloc or free.
2) Calloc
void * calloc ( size_t num, size_t size );
num: Number of elements
size: Size of each element
Calloc allocates a block of memory for an array of “num” elements each of size “size”.
A successful calloc call results in the allocation of a block num * size bytes long, each initialized to 0.
The initialization part differs it from malloc.
3) Realloc
void * realloc ( void * ptr, size_t size );
ptr: Pointer to a memory block previously allocated with malloc, calloc or realloc, or a null pointer (to allocate a new block)
size: New size for the memory block, in bytes
Realloc increases the size of the memory block pointed by ptr. If the current address of the memory block doesn’t has “size” free bytes after it. Realloc allocates a completely new memory block at a different location. So, realloc may move the memory block to a different address, so you might get a different ptr in return.
When the ptr is NULL, realloc exactly behaves like malloc. It allocated a memory block of size bytes long.
4) Free
void free ( void * ptr );
ptr: Pointer to a memory block previously allocated with malloc, calloc or realloc.
Free deallocates the previously allocated memory using a call to malloc, calloc or realloc. Making it available again for further allocations.
Note: The ptr should exactly point to the memory location returned by malloc, calloc or realloc (why? check the malloc section.). If Even if its off by 1 byte the result is undefined (mostly result in crash).
Usage Examples
1) Basic malloc, free usage :-
#include <stdio.h>
#include <stdlib.h> // for access to the memory allocation functions
#define NO_OF_INTS 5
#define NO_OF_CHARS 20
#define NO_OF_LONGS 5
int main()
{
void *upper_bound; // To avoid invalid reads.
int *int_ptr;
char *char_ptr;
// Many people would suggest to initialize your pointers to NULL
// but I don't insist upon it as it just wastes a mov instruction
// but be sure to never dereference a pointer you didn't initialized
// as that would result in a read from a random address which will result
// in a segmentation fault, so initialize them if you are gonna use them
// when required
// Tip 1: Always cast the returned pointer from malloc, Most of the compilers are kind enough to give you warnings for that.
int_ptr = (int *)malloc(sizeof(int) * NO_OF_INTS); // same as "int array[NO_OF_INTS]"
// Tip 2: Always check the output of malloc
if (int_ptr == NULL) {
printf("Failed to allocate memory for ints.");
return -1;
}
char_ptr = (char *)malloc(sizeof(char) * (NO_OF_CHARS + 1)); // we need an extra byte for the NULL, same as "char array[NO_OF_CHARS]"
if (char_ptr == NULL) {
fprintf(stderr, "Failed to allocate memory for chars.");
}
// Now, we have 2 pointers each pointing to the start (array[0]) of their respective allocated memory blocks.
// Let's populate the memory blocks
printf("Notice the intervals in the addresses.\n");
// int_ptr
upper_bound = int_ptr + NO_OF_INTS;
for (; int_ptr < (int *)upper_bound; int_ptr++) {
printf("Setting %p to : 1\n", int_ptr);
*int_ptr = 1;
}
// char_ptr
upper_bound = char_ptr + NO_OF_CHARS;
for (; char_ptr < (char *)upper_bound; char_ptr++) {
*char_ptr = 'a';
}
*char_ptr = '\0'; // terminate the string
printf("This should display a string of %d A's : %s\n", NO_OF_CHARS, char_ptr - NO_OF_CHARS);
// Tip 3: Always free your pointers
free(int_ptr - NO_OF_INTS);
free(char_ptr - NO_OF_CHARS);
// Note: Free takes in the address of the start of the memory block you allocated. (the one returned by malloc)
// if you input any other memory location. It will result in crash.
// For proof, try the following :-
//free(int_ptr);
//free(int_ptr - NO_OF_INTS);
//free(char_ptr - NO_OF_CHARS);
// even one byte off the address would result in a crash.
return 0;
}
2. Basic realloc usage :-
#include <stdio.h>
#include <stdlib.h> // for access to the memory allocation functions
#define NUMBER_NEW_INTS 10
// Try to fiddle with this value, and you'll notice that
// for small values, you'll get the same memory location back
// from realloc with an extended size.
int main()
{
int *int_ptr;
int *temp_ptr; // for realloc
int_ptr = (int *)malloc(sizeof(int));
if (int_ptr == NULL) {
printf("Failed to allocate memory for");
return -1;
}
printf("Allocated a memory block for 1 int @ %p.\n", int_ptr);
// Tip: Always use a temp pointer with realloc.
// Reason :-
// If realloc fails to get a new block it will return NULL, which will
// overwrite your ptr and result in a memory leak because the previous memory
// location which we allocated with malloc (or calloc) would still be unfreed.
temp_ptr = realloc((void *)int_ptr, sizeof(int) * NUMBER_NEW_INTS); // please give me some more memory
if (temp_ptr == NULL) {
// Can't get any memory
free(int_ptr);
return -1;
}
// Now you are safe to overwrite int_ptr
int_ptr = temp_ptr;
printf("Allocated a memory block for %d int(s) @ %p.\n", NUMBER_NEW_INTS + 1, int_ptr);
free(int_ptr);
return 0;
}
just a few comments:
1) It’s relatively dangerous to increment/decrement a pointer then pass it to free. even if your math seems to work out to get the original pointer, a small oversight or mistake in the math will cause a crash due to invalid free. it’s safer to save the original pointer for freeing.
2) you don’t have to cast void*
3) it’s also generally safer to use sizeof(*variable) instead of sizeof(type). in case the type is changed later and you overlook any of the sizeof().
4) might be good to mention that malloc(0) is implementation-defined so it’s best to avoid that possibility in your code rather than assume it will return NULL.