C Memory Management

Introduction

In the previous chapters, we explored various aspects of C programming, including pointers. In this chapter, we will focus on memory management. Proper memory management is crucial for writing efficient and robust C programs. It involves allocating, using, and freeing memory correctly to avoid memory leaks, fragmentation, and other issues.

Memory Allocation in C

Memory allocation in C can be categorized into two types:

  1. Static Memory Allocation: Memory is allocated at compile-time.
  2. Dynamic Memory Allocation: Memory is allocated at runtime.

Static Memory Allocation

Static memory allocation occurs when memory is allocated during the compile time. Variables declared inside functions, global variables, and static variables are examples of static memory allocation. The memory for these variables is automatically managed by the compiler.

Example: Static Memory Allocation

#include <stdio.h>

int global_var;          // Global variable
static int static_var;   // Static variable

void function() {
    int local_var;       // Local variable
    static int static_local_var; // Static local variable

    printf("Local variable address: %p\n", (void*)&local_var);
    printf("Static local variable address: %p\n", (void*)&static_local_var);
}

int main() {
    printf("Global variable address: %p\n", (void*)&global_var);
    printf("Static variable address: %p\n", (void*)&static_var);
    function();

    return 0; // Returning 0 to indicate successful execution
}

Output:

Global variable address: 0x56555630
Static variable address: 0x56555634
Local variable address: 0x7ffc0fae0eac
Static local variable address: 0x56555638

In this example, the addresses of global, static, and local variables are printed. The global and static variables are allocated at compile time.

Dynamic Memory Allocation

Dynamic memory allocation occurs when memory is allocated during runtime. It allows you to allocate memory as needed and free it when it is no longer required. Dynamic memory allocation is performed using the following functions:

  1. malloc: Allocates memory blocks.
  2. calloc: Allocates memory blocks and initializes them to zero.
  3. realloc: Reallocates memory blocks to resize them.
  4. free: Frees allocated memory.

malloc Function

The malloc function allocates a block of memory of the specified size and returns a pointer to the beginning of the block. The memory content is not initialized.

Syntax:

void *malloc(size_t size);

Example: Using malloc

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr;
    int n = 5;

    // Allocating memory for n integers
    ptr = (int*)malloc(n * sizeof(int));

    if (ptr == NULL) {
        printf("Memory allocation failed\n");
        return 1; // Exiting the program if memory allocation fails
    }

    // Storing values in the allocated memory
    for (int i = 0; i < n; i++) {
        ptr[i] = i + 1;
    }

    // Accessing and printing the values
    printf("Allocated array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // Freeing the allocated memory
    free(ptr);

    return 0; // Returning 0 to indicate successful execution
}

Output:

Allocated array: 1 2 3 4 5

calloc Function

The calloc function allocates memory for an array of elements, initializes the memory to zero, and returns a pointer to the memory.

Syntax:

void *calloc(size_t num, size_t size);

Example: Using calloc

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr;
    int n = 5;

    // Allocating memory for n integers
    ptr = (int*)calloc(n, sizeof(int));

    if (ptr == NULL) {
        printf("Memory allocation failed\n");
        return 1; // Exiting the program if memory allocation fails
    }

    // Accessing and printing the initialized values
    printf("Allocated array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // Freeing the allocated memory
    free(ptr);

    return 0; // Returning 0 to indicate successful execution
}

Output:

Allocated array: 0 0 0 0 0

realloc Function

The realloc function changes the size of the previously allocated memory block.

Syntax:

void *realloc(void *ptr, size_t new_size);

Example: Using realloc

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr;
    int n = 5;

    // Allocating memory for n integers
    ptr = (int*)malloc(n * sizeof(int));

    if (ptr == NULL) {
        printf("Memory allocation failed\n");
        return 1; // Exiting the program if memory allocation fails
    }

    // Storing values in the allocated memory
    for (int i = 0; i < n; i++) {
        ptr[i] = i + 1;
    }

    // Reallocating memory for n*2 integers
    ptr = (int*)realloc(ptr, n * 2 * sizeof(int));

    if (ptr == NULL) {
        printf("Memory reallocation failed\n");
        return 1; // Exiting the program if memory reallocation fails
    }

    // Storing additional values in the reallocated memory
    for (int i = n; i < n * 2; i++) {
        ptr[i] = i + 1;
    }

    // Accessing and printing the values
    printf("Reallocated array: ");
    for (int i = 0; i < n * 2; i++) {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // Freeing the allocated memory
    free(ptr);

    return 0; // Returning 0 to indicate successful execution
}

Output:

Reallocated array: 1 2 3 4 5 6 7 8 9 10

free Function

The free function deallocates the memory previously allocated by malloc, calloc, or realloc.

Syntax:

void free(void *ptr);

Example: Comprehensive Memory Management

Here is a comprehensive example demonstrating dynamic memory allocation, reallocation, and deallocation:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr;
    int initial_size = 5, new_size;

    // Allocating memory for initial_size integers
    arr = (int*)malloc(initial_size * sizeof(int));

    if (arr == NULL) {
        printf("Memory allocation failed\n");
        return 1; // Exiting the program if memory allocation fails
    }

    // Storing values in the allocated memory
    for (int i = 0; i < initial_size; i++) {
        arr[i] = i + 1;
    }

    // Accessing and printing the values
    printf("Initial array: ");
    for (int i = 0; i < initial_size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // Reallocating memory for new_size integers
    printf("Enter new size of the array: ");
    scanf("%d", &new_size);
    arr = (int*)realloc(arr, new_size * sizeof(int));

    if (arr == NULL) {
        printf("Memory reallocation failed\n");
        return 1; // Exiting the program if memory reallocation fails
    }

    // Storing additional values in the reallocated memory
    for (int i = initial_size; i < new_size; i++) {
        arr[i] = i + 1;
    }

    // Accessing and printing the values
    printf("Reallocated array: ");
    for (int i = 0; i < new_size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // Freeing the allocated memory
    free(arr);

    return 0; // Returning 0 to indicate successful execution
}

Output:

Initial array: 1 2 3 4 5
Enter new size of the array: 10
Reallocated array: 1 2 3 4 5 6 7 8 9 10

Conclusion

Proper memory management is crucial for writing efficient and robust C programs. By understanding static and dynamic memory allocation, you can efficiently allocate, use, and free memory in your applications. Dynamic memory allocation functions such as malloc, calloc, realloc, and free are essential tools for managing memory in C. Mastering memory management techniques helps prevent memory leaks, fragmentation, and other issues, ensuring your programs run smoothly and efficiently.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top