Fundamentals of C Language: Dynamic Memory Management

In C language, we typically use arrays, variables, etc., to store data, but these methods share a common limitation: the size of the memory must be determined at compile time or early in the program’s execution. However, in actual development, we often encounter situations where we are unsure of how much memory is needed, need to dynamically adjust memory size, or allocate resources on demand.

At this point, C language provides a more flexible and powerful (but also more dangerous) mechanism—dynamic memory management. Through the standard library functions malloc, calloc, realloc, and free, we can dynamically request and release memory during program execution, truly achieving “on-demand usage”. This is also the cornerstone of C language’s ability to implement various complex data structures and efficient memory utilization.

However, like the two sides of a coin, dynamic memory management also brings a series of issues such as memory leaks, wild pointers, and out-of-bounds access, which are common pitfalls for many C language beginners and even experienced developers.

In this article, we will delve into the dynamic memory management mechanism in C language, including commonly used memory allocation and release functions, usage specifications, common errors, and debugging techniques, helping you master this powerful yet cautiously used important skill.

1. Stack and Heap

· Stack: Automatically managed, mainly used for local variables, function parameters, return addresses, etc., following Last In First Out (LIFO). Stack space is limited, and excessive use may lead to stack overflow.
· Heap: Manually managed by the programmer, suitable for long-lived data (such as linked lists, trees, etc.). Allocated using malloc(), calloc(), realloc(), and released using free().

1. malloc

Used to allocate a block of uninitialized memory on the heap, returning a pointer to that memory.

int *ptr = (int *)malloc(5 * sizeof(int)); // Allocate space for 5 ints
int *ptr = malloc(5); // Allocate 5 bytes
if (ptr == NULL) { // Check if allocation was successful
    printf("Memory allocation failed!\n");
    return 1;
}
Note: 
· malloc does not initialize memory, data may be random values (garbage data).
· Must use free to release memory, otherwise it will lead to memory leaks.

2. calloc

Used to allocate memory on the heap initialized to 0.

int *ptr = (int *)calloc(5, sizeof(int)); // Allocate 5 ints and initialize to 0
if (ptr == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
}
Difference between malloc and calloc:
· malloc does not initialize (data may be garbage values).
· calloc initializes to 0 (suitable for arrays).

3. realloc

Used to change the size of allocated memory.

int *ptr = (int *)malloc(2 * sizeof(int));
if (ptr == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
}
ptr[0] = 1;
ptr[1] = 2;
// Extend memory
ptr = (int *)realloc(ptr, 4 * sizeof(int)); // Reallocate 4 ints
if (ptr == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
}
ptr[2] = 3;
ptr[3] = 4;

4. free

Used to release dynamically allocated memory; after release, the memory block is no longer valid, preventing memory leaks.

free(ptr); // Release memory

2. Usage Examples

1. Include header file <stdlib.h>

#include <stdio.h>
#include <stdlib.h> // Must include this header file to use malloc/free

2. Example of using malloc

int type

#include <stdio.h>
#include <stdlib.h>  // Must include this header file to use malloc/free
int main() {
    int size = 5;  // Assume we need to store 5 integers
    int *numbers = (int*)malloc(size * sizeof(int));  // Allocate memory for 5 ints
    if (numbers == NULL) {  // Check if allocation was successful
        printf("Memory allocation failed!\n");
        return 1;
    }
    // Initialize array (1, 2, 3, 4, 5)
    for (int i = 0; i < size; i++) {
        numbers[i] = i + 1;
    }
    // Print array
    printf("Dynamically allocated int array: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");  // Output: 1 2 3 4 5
    free(numbers);  // Release memory
    numbers = NULL;  // Avoid wild pointer (optional but recommended)
    return 0;
}

char type

#include <stdio.h>
#include <stdlib.h>  // Must include this header file to use malloc/free
#include <string.h>  // For strcpy
int main() {
    // 1. Calculate string length (including the ending '\0')
    const char *original = "Hello, World!";
    size_t length = strlen(original) + 1;  // +1 for the string terminator '\0'
    // 2. Dynamically allocate memory (exactly matching string size)
    char *dynamic_str = (char*)malloc(length * sizeof(char));
    if (dynamic_str == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    // 3. Copy string content
    strcpy(dynamic_str, original);
    // 4. Output result
    printf("Dynamic string: %s\n", dynamic_str);  // Output: Hello, World!
    // 5. Release memory
    free(dynamic_str);
    dynamic_str = NULL;  // Prevent wild pointer (recommended but not necessary)
    return 0;
}

3. Example of using calloc

int type

#include <stdio.h>
#include <stdlib.h>
int main() {
    int size = 5;
    int *numbers = (int*)calloc(size, sizeof(int));  // Allocate and initialize to 0
    if (numbers == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    // Print array (default is 0, 0, 0, 0, 0)
    printf("calloc initialized int array: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);  // Output: 0 0 0 0 0
    }
    printf("\n");
    // Manually assign values (1, 2, 3, 4, 5)
    for (int i = 0; i < size; i++) {
        numbers[i] = i + 1;
    }
    // Print again
    printf("Modified int array: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);  // Output: 1 2 3 4 5
    }
    printf("\n");
    free(numbers);
    return 0;
}

char type

#include <stdio.h>
#include <stdlib.h>  // Must include this header file
#include <string.h>  // For string operations
int main() {
    int char_count = 10;  // Assume we need to store 10 characters (including '\0')
    // 1. Use calloc to allocate and initialize memory (automatically fills with 0/\0)
    char *str = (char*)calloc(char_count, sizeof(char));
    if (str == NULL) {  // Check if allocation was successful
        printf("Memory allocation failed!\n");
        return 1;
    }
    // 2. Check initialization result (verify if all are 0/\0)
    printf("calloc initialized memory content (first 5 bytes): ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", str[i]);  // Output: 0 0 0 0 0 (i.e., '\0')
    }
    printf("\n");
    // 3. Safely store string (no need to manually add '\0', because calloc has initialized)
    strncpy(str, "Hello", char_count - 1);  // Copy at most 9 characters (leave 1 for '\0')
    str[char_count - 1] = '\0';  // Ensure termination (although calloc has initialized, explicit is safer)
    // 4. Print result
    printf("Dynamic string: %s\n", str);  // Output: Hello
    // 5. Release memory
    free(str);
    return 0;
}

4. Example of using reallocint type

#include <stdio.h>
#include <stdlib.h>
int main() {
    int initial_size = 3;
    int *numbers = (int*)malloc(initial_size * sizeof(int));
    if (numbers == NULL) {
        printf("Initial memory allocation failed!\n");
        return 1;
    }
    // Initialize first 3 elements (1, 2, 3)
    for (int i = 0; i < initial_size; i++) {
        numbers[i] = i + 1;
    }
    printf("Initial array: ");
    for (int i = 0; i < initial_size; i++) {
        printf("%d ", numbers[i]);  // Output: 1 2 3
    }
    printf("\n");
    // Extend memory to 5 ints
    int new_size = 5;
    int *new_numbers = (int*)realloc(numbers, new_size * sizeof(int));
    if (new_numbers == NULL) {
        printf("Memory extension failed!\n");
        free(numbers);  // Original memory remains valid if extension fails
        return 1;
    }
    numbers = new_numbers;  // Update pointer
    // Fill new 2 elements (4, 5)
    for (int i = initial_size; i < new_size; i++) {
        numbers[i] = i + 1;
    }
    // Print final array (1, 2, 3, 4, 5)
    printf("Extended array: ");
    for (int i = 0; i < new_size; i++) {
        printf("%d ", numbers[i]);  // Output: 1 2 3 4 5
    }
    printf("\n");
    free(numbers);
    return 0;
}

char type

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
    const char *text = "Hello, World!";
    char *dynamic_str = (char*)malloc(5 * sizeof(char));  // First allocate 5 bytes
    if (dynamic_str == NULL) {
        printf("Initial memory allocation failed!\n");
        return 1;
    }
    strcpy(dynamic_str, "Hi");  // First store "Hi"
    printf("Initial string: %s\n", dynamic_str);  // Output: Hi
    // Extend memory to enough to store "Hello, World!"
    char *new_str = (char*)realloc(dynamic_str, strlen(text) + 1);
    if (new_str == NULL) {
        printf("Memory extension failed!\n");
        free(dynamic_str);  // Original memory remains valid if extension fails
        return 1;
    }
    dynamic_str = new_str;  // Update pointer
    // Now can store the full string
    strcpy(dynamic_str, text);
    printf("Extended string: %s\n", dynamic_str);  // Output: Hello, World!
    free(dynamic_str);
    return 0;
}

Comrades! Through the study of this article, we have gained an in-depth understanding of the core mechanism of dynamic memory management in C language, including: how to use malloc, calloc, and realloc to dynamically request memory, how to correctly use free to release memory, and avoid memory leaks.

The close relationship between dynamic memory and pointers, as well as common errors such as wild pointers, double freeing, and memory out-of-bounds, make dynamic memory management one of the features that best reflects the flexibility and control of C language, but it also places higher demands on programmers: You must clearly know where each block of memory comes from and where it goes.

Remember:Memory is the foundation of program execution; managing memory well is essential to writing truly robust C language programs.Keep it up, comrades!

Leave a Comment