malloc() / calloc() / realloc() / free()
| Since: | C89(1989) |
|---|
Allocates memory as needed during program execution and releases it when done. Use these functions when you need to determine array sizes at runtime or work with large amounts of data. These functions are declared in <stdlib.h>.
Syntax
#include <stdlib.h> // Allocates size bytes (uninitialized) void *malloc(size_t size); // Allocates num × size bytes, zero-initialized void *calloc(size_t num, size_t size); // Resizes allocated memory to new_size bytes void *realloc(void *ptr, size_t new_size); // Frees allocated memory void free(void *ptr);
Dynamic Memory Functions
| Function | Description |
|---|---|
| malloc(size) | Allocates the specified number of bytes from the heap. The initial value is indeterminate. Returns NULL if allocation fails. |
| calloc(num, size) | Allocates num elements of size bytes each and initializes all bytes to zero. Useful for initializing integer arrays. |
| realloc(ptr, new_size) | Resizes the memory block pointed to by ptr to new_size bytes. If necessary, moves the block to a new location and copies the original contents. |
| free(ptr) | Frees memory allocated by malloc, calloc, or realloc. Passing NULL is safe. Do not free a pointer that has already been freed. |
Sample Code
Allocates an array with a runtime-determined size using malloc, creates a zero-initialized array with calloc, and expands an existing array with realloc.
sample_malloc_free.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
printf("Enter the number of elements: ");
scanf("%d", &n);
// malloc: allocate array with runtime-determined size
int *arr = malloc(sizeof(int) * n);
if (arr == NULL) {
fprintf(stderr, "Memory allocation failed.\n");
return 1;
}
for (int i = 0; i < n; i++) {
arr[i] = (i + 1) * 10;
}
for (int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
arr = NULL; // Prevent dangling pointer
// calloc: all elements initialized to zero
int *zeros = calloc(5, sizeof(int));
for (int i = 0; i < 5; i++) {
printf("%d ", zeros[i]);
}
printf("\n");
free(zeros);
// realloc: dynamically expand existing array
int *buf = malloc(sizeof(int) * 3);
buf[0] = 1; buf[1] = 2; buf[2] = 3;
int *new_buf = realloc(buf, sizeof(int) * 6);
if (new_buf == NULL) {
free(buf);
return 1;
}
buf = new_buf;
buf[3] = 4; buf[4] = 5; buf[5] = 6;
for (int i = 0; i < 6; i++) {
printf("%d ", buf[i]);
}
printf("\n");
free(buf);
return 0;
}
gcc sample_malloc_free.c -o sample_malloc_free ./sample_malloc_free Enter the number of elements: 3 arr[0] = 10 arr[1] = 20 arr[2] = 30 0 0 0 0 0 1 2 3 4 5 6
malloc / calloc / realloc Comparison
| Function | Initialization | Use Case | Best Suited For |
|---|---|---|---|
| malloc(size) | None (value is indeterminate) | General-purpose allocation. Fast. | When speed matters and you will write values immediately |
| calloc(num, size) | All bytes set to zero | When zero-initialized storage is needed (integer arrays, flags) | When you need zero-initialized memory |
| realloc(ptr, size) | Extended portion is indeterminate | Resize an already-allocated block | Dynamically growing or shrinking arrays |
When you need a zero-initialized integer or flag array, using calloc() initializes all elements to zero. It is more explicit than malloc and saves you from writing a separate memset(0).
Preventing Memory Leaks
Failing to free allocated memory causes a memory leak, where memory usage grows continuously in long-running programs.
Assigning the return value of realloc directly back to the original pointer causes the original memory to be lost if realloc fails.
realloc_ng.c
#include <stdio.h>
#include <stdlib.h>
int *bad_grow(int *buf, int new_size) {
buf = realloc(buf, sizeof(int) * new_size);
return buf;
}
int main(void) {
int *buf = malloc(sizeof(int) * 3);
buf[0] = 10; buf[1] = 20; buf[2] = 30;
buf = bad_grow(buf, 6);
buf[3] = 40; buf[4] = 50; buf[5] = 60;
for (int i = 0; i < 6; i++) {
printf("%d ", buf[i]);
}
printf("\n");
free(buf);
return 0;
}
This works normally, but if realloc fails, the original memory is lost.
gcc realloc_ng.c -o realloc_ng ./realloc_ng 10 20 30 40 50 60
Use a temporary variable to check for NULL before reassigning. This ensures the original memory can be safely freed on failure.
realloc_ok.c
#include <stdio.h>
#include <stdlib.h>
int *good_grow(int *buf, int new_size) {
int *tmp = realloc(buf, sizeof(int) * new_size);
if (tmp == NULL) {
free(buf);
return NULL;
}
return tmp;
}
int main(void) {
int *buf = malloc(sizeof(int) * 3);
buf[0] = 10; buf[1] = 20; buf[2] = 30;
buf = good_grow(buf, 6);
if (buf == NULL) {
fprintf(stderr, "Memory allocation failed.\n");
return 1;
}
buf[3] = 40; buf[4] = 50; buf[5] = 60;
for (int i = 0; i < 6; i++) {
printf("%d ", buf[i]);
}
printf("\n");
free(buf);
return 0;
}
gcc realloc_ok.c -o realloc_ok ./realloc_ok 10 20 30 40 50 60
Even in functions with early returns, always call free on every return path.
early_return.c
#include <stdio.h>
#include <stdlib.h>
int process(int trigger_error) {
int *data = malloc(sizeof(int) * 100);
if (data == NULL) { return -1; }
if (trigger_error) {
free(data);
return -1;
}
data[0] = 42;
printf("data[0] = %d\n", data[0]);
free(data);
data = NULL;
return 0;
}
int main(void) {
printf("Normal: %d\n", process(0));
printf("Error: %d\n", process(1));
return 0;
}
gcc early_return.c -o early_return ./early_return data[0] = 42 Normal: 0 Error: -1
Common mistake 1: skipping the NULL check
Accessing memory without checking if malloc returned NULL causes a crash when allocation fails.
null_check_ng.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *arr = malloc(sizeof(int) * 1000000);
arr[0] = 1;
printf("%d\n", arr[0]);
free(arr);
return 0;
}
If malloc returns NULL (out of memory)
./null_check_ng Segmentation fault (core dumped)
null_check_ok.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *arr = malloc(sizeof(int) * 1000000);
if (arr == NULL) {
fprintf(stderr, "Memory allocation failed.\n");
return 1;
}
arr[0] = 1;
printf("%d\n", arr[0]);
free(arr);
return 0;
}
gcc null_check_ok.c -o null_check_ok ./null_check_ok 1
Common mistake 2: double free
Freeing the same pointer twice causes undefined behavior. Assign NULL after freeing to prevent this.
double_free_ng.c
#include <stdlib.h>
int main(void) {
int *arr = malloc(sizeof(int) * 10);
free(arr);
free(arr);
return 0;
}
gcc double_free_ng.c -o double_free_ng ./double_free_ng free(): double free detected in tcache 2 Aborted (core dumped)
double_free_ok.c
#include <stdlib.h>
int main(void) {
int *arr = malloc(sizeof(int) * 10);
free(arr);
arr = NULL;
free(arr);
return 0;
}
gcc double_free_ok.c -o double_free_ok ./double_free_ok
(normal exit, no output)
Common mistake 3: direct realloc assignment
Assigning the return value of realloc directly back to the original pointer causes a memory leak if realloc fails. Always use a temporary variable.
arr = realloc(arr, new_size);
int *tmp = realloc(arr, new_size);
if (tmp == NULL) { free(arr); return 1; }
arr = tmp;
Notes
Dynamically allocated memory is placed in a region called the "heap." Unlike automatic variables on the stack, heap memory is not released until you explicitly call free(). Failing to free allocated memory causes a "memory leak," where memory usage grows continuously over the lifetime of the program. Always free memory when you are done with it.
Using freed memory (a "dangling pointer") or freeing the same memory twice (a "double free") causes serious bugs. Always assign NULL to a pointer after freeing it.
When realloc() fails, it does not free the original pointer, so always check the return value with a separate variable before reassigning. For related functions, see also sizeof.
If you find any errors or copyright issues, please contact us.