SQL Injection Explained
Learn how SQL Injection works with real examples and a hands-on demo using PHP and MySQL. Explore a GitHub repo with vulnerable code to safely test and understand this critical web vulnerability.
Learn C memory management with clear examples of malloc, calloc, realloc, and free. Understand memory types, avoid common pitfalls, and optimize your C programs efficiently.
Memory is one of the most essential components in any computing system. It acts as the workspace where data and instructions are temporarily or permanently stored during a program’s execution. Without memory, a computer would not be able to hold variables, execute functions, or perform operations.
In programming, memory is used to store:
Efficient use of memory is crucial. Poor memory management can lead to slow programs, crashes, or vulnerabilities like security exploits. In C, memory usage is especially important because the language gives developers direct control over how memory is allocated and released—unlike higher-level languages that handle it automatically.
Table of contents [Show]
Memory management refers to the process of allocating, using, and freeing memory during the execution of a program. In C, the responsibility for managing memory lies largely with the programmer. This includes:
Improper memory management can lead to:
Understanding how memory works in C is not just useful—it's essential for writing reliable and efficient code.
When a C program runs, it uses different areas of memory based on the nature and lifetime of the data being handled. These regions include:
malloc()
, free()
, etc.).Dynamic memory management refers to allocating memory during runtime, rather than at compile time. This is particularly useful when:
C provides several built-in functions for dynamic memory operations:
malloc(size_t size)
:Allocates a specified number of bytes and returns a pointer to the first byte.
void*
NULL
if allocation fails.int *arr = (int*) malloc(5 * sizeof(int));
calloc(size_t num, size_t size)
:Allocates memory for an array of elements and initializes all bytes to zero.
void *
NULL
if allocation fails.int *arr = (int*) calloc(5, sizeof(int));
realloc(void ptr, size_t new_size)
:Resizes a previously allocated memory block, preserving existing contents.
void *
NULL
(original memory is still valid if NULL
is returned).arr = (int*) realloc(arr, 10 * sizeof(int));
free(void ptr)
:void
ptr
. Safe to call with NULL
.free(arr);
Here's a simple example of dynamic allocation and usage in C:
#include <stdio.h>
#include <stdlib.h>
int main() {
// --- malloc() example ---
int* malloc_arr = (int*)malloc(5 * sizeof(int));
if (malloc_arr == NULL) {
printf("malloc failed\n");
return 1;
}
printf("malloc: Initial values (may contain garbage):\n");
for (int i = 0; i < 5; i++) {
printf("malloc_arr[%d] = %d\n", i, malloc_arr[i]); // Uninitialized values
}
// Initialize malloc'd array
for (int i = 0; i < 5; i++) {
malloc_arr[i] = i + 1;
}
printf("malloc: After initialization:\n");
for (int i = 0; i < 5; i++) {
printf("malloc_arr[%d] = %d\n", i, malloc_arr[i]);
}
// --- calloc() example ---
int* calloc_arr = (int*)calloc(5, sizeof(int));
if (calloc_arr == NULL) {
printf("calloc failed\n");
free(malloc_arr);
return 1;
}
printf("\ncalloc: Values (should be zero):\n");
for (int i = 0; i < 5; i++) {
printf("calloc_arr[%d] = %d\n", i, calloc_arr[i]);
}
// --- realloc() example ---
printf("\nrealloc: Expanding malloc_arr to 10 integers\n");
int* realloc_arr = (int*)realloc(malloc_arr, 10 * sizeof(int));
if (realloc_arr == NULL) {
printf("realloc failed\n");
free(malloc_arr); // Original block should still be freed
free(calloc_arr);
return 1;
}
malloc_arr = realloc_arr; // Assign new pointer back
// Initialize new elements
for (int i = 5; i < 10; i++) {
malloc_arr[i] = i + 1;
}
printf("realloc: After resizing and initializing:\n");
for (int i = 0; i < 10; i++) {
printf("malloc_arr[%d] = %d\n", i, malloc_arr[i]);
}
// --- free() example ---
free(malloc_arr);
free(calloc_arr);
printf("\nMemory has been freed.\n");
return 0;
}
malloc: Initial values (may contain garbage):
malloc_arr[0] = 0
malloc_arr[1] = 0
malloc_arr[2] = 0
malloc_arr[3] = 0
malloc_arr[4] = 0
malloc: After initialization:
malloc_arr[0] = 1
malloc_arr[1] = 2
malloc_arr[2] = 3
malloc_arr[3] = 4
malloc_arr[4] = 5
calloc: Values (should be zero):
calloc_arr[0] = 0
calloc_arr[1] = 0
calloc_arr[2] = 0
calloc_arr[3] = 0
calloc_arr[4] = 0
realloc: Expanding malloc_arr to 10 integers
realloc: After resizing and initializing:
malloc_arr[0] = 1
malloc_arr[1] = 2
malloc_arr[2] = 3
malloc_arr[3] = 4
malloc_arr[4] = 5
malloc_arr[5] = 6
malloc_arr[6] = 7
malloc_arr[7] = 8
malloc_arr[8] = 9
malloc_arr[9] = 10
Memory has been freed.
Even experienced developers can make mistakes in memory management. Here are some common issues to watch out for:
free()
memory leads to leaks that accumulate over time.free()
memory more than once.Manual memory management is difficult and error-prone. Thankfully, there are tools available to help detect problems early:
A powerful tool for detecting memory leaks, buffer overflow, and use-after-free errors.
valgrind ./your_program
A fast memory error detector supported by GCC and Clang.
gcc -fsanitize=address -g your_program.c
Do You Need to Cast the Return Value of malloc()
, calloc()
, or realloc()
in C?
int *ptr = malloc(5 * sizeof(int)); // ✅ Valid in C
Why?
void*
returned by these functions is implicitly converted to the appropriate pointer type in C.int *ptr = (int *)malloc(5 * sizeof(int)); // ✅ Required in C++
Use without casting unless you're writing cross-compatible C/C++ code, in which case casting might be necessary.
// Recommended in C
int *ptr = malloc(10 * sizeof(int));
Your email address will not be published. Required fields are marked *
Learn how SQL Injection works with real examples and a hands-on demo using PHP and MySQL. Explore a GitHub repo with vulnerable code to safely test and understand this critical web vulnerability.
Learn how CI/CD transforms software development by automating builds, tests, and deployments. Boost speed, reduce bugs, and release confidently with continuous integration and delivery.
Learn efficient ways to append characters to C++ strings using push_back, +=, append, and +. Compare time complexity, performance, and memory usage for optimal string manipulation.