Dynamic Memory Allocation in C

Dynamic Memory Allocation in C

Master malloc(), calloc(), realloc() and free() with examples

Memory Allocation Types

  1. Static Memory Allocation

  2. Automatic Memory Allocation

  3. Dynamic Memory Allocation

FreeRTOS Memory Management

Comparison

StaticDynamic
Memory allocated at compile timeMemory allocated at runtime
Fixed memory sizeFlexible memory size
No overheadMemory management overhead
SimpleMore complex
No memory leaksPotential for memory leaks

Dynamic Memory Allocation

Need for dynamic memory allocation

  • Static memory allocation allocates memory at compile time which has a fixed size

  • The size remains the same throughout the lifetime of the program

  • This can lead to a waste of memory if the allocated size is more than the actual requirement

  • It can also cause issues if the allocated size is less than the actual requirement

To solve these issues, C provides functions for dynamic memory allocation:

  • Memory is allocated at runtime based on the actual requirements of the program

  • The allocated memory size can grow and shrink as needed

Memory allocation functions in C

  • malloc(): Allocates a block of memory of the specified size and returns a pointer to the allocated memory. If memory cannot be allocated, it returns NULL.

  • calloc(): Similar to malloc() but initializes all the bytes of the allocated memory block to 0.

  • realloc(): Changes the size of the memory block pointed to by the given pointer. If the area pointed by the pointer is larger, the extra memory is not changed. If the new size is larger, the extra memory has an indeterminate value.

  • free(): Frees the memory block pointed to by the given pointer. The pointer becomes invalid after calling free().

Malloc()

Syntax of malloc()

  pointer_variable = malloc(size);

Here,

  • pointer_variable is a pointer variable of the required type (int, float etc)

  • size is the number of bytes to allocate

Usage

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

  float *fptr = malloc(sizeof(float) * m); // Allocate memory for m floats

Here ptr and fptr will point to the allocated memory blocks of the specified sizes.

Example

Allocating memory for an array:

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

  int main() {
    int n;
    printf("Enter number of integers: ");
    scanf("%d", &n);

    int *ptr = malloc(sizeof(int) * n);

    if (ptr == NULL) {
      printf("Error! Unable to allocate memory\n");
      exit(0);  
    }

    // Use ptr array

    free(ptr);
    return 0;
  }

Calloc()

Syntax of calloc()

  pointer_variable = calloc(n, size);

Here,

  • pointer_variable is a pointer variable of the required type (int, float etc.)

  • n is the number of elements to allocate memory for

  • size is the size of each element in bytes

calloc() allocates memory for an array of n elements of size size each and initialize all bytes to 0.

Differences between malloc() and calloc()

  • malloc() just allocates raw memory without initializing

  • calloc() allocates memory and initializes all bytes to 0 (or NULL)

  • calloc() takes the number of elements and size, while malloc() takes only total size

Example

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

  int main() {
    int *ptr, n, i;

    printf("Enter number of integers: ");
    scanf("%d", &n);

    ptr = calloc(n, sizeof(int));

    if (ptr == NULL) {
      printf("Error! Unable to allocate memory.");
      exit(0);
    }  

    // Use allocated memory 

    for (i = 0; i < n; i++)
      printf("%d ", ptr[i]);  // Prints 0 0 0 ...  

    free(ptr);
    return 0;  
  }

Realloc():

Syntax of realloc()

  pointer_variable = realloc(pointer_variable, new_size);

Here,

  • pointer_variable is the pointer to the previously allocated memory block

  • new_size is the new size in bytes to resize the memory block to

Usage

  int *ptr = malloc(sizeof(int) * 10);

  // Resize the memory block to hold 20 integers
  ptr = realloc(ptr, sizeof(int) * 20);

Example

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

  int main() {
    int *ptr, n = 10, i;

    ptr = malloc(n * sizeof(int));

    // Taking input and storing in array 
    for (i = 0; i < n; i++) {
      scanf("%d", &ptr[i]);  
    }

    n = n + 5;  // Increase array size by 5

    ptr = realloc(ptr, n * sizeof(int));

    if (ptr == NULL) {
      printf("Error! Unable to allocate memory.");
      exit(0); 
    }

    // Taking 5 more inputs  
    for (i = 10; i < n; i++) {
      scanf("%d", &ptr[i]);  
    }

    // Print all elements  
    for (i = 0; i < n; i++) {
      printf("%d", ptr[i]);  
    }

    free(ptr);
    return 0;
  }

Free():

Syntax of free()

  free(pointer_variable);

Here, pointer_variable is the pointer to the memory block that was allocated using malloc(), calloc() or realloc().

Usage

  int *ptr = malloc(sizeof(int) * 10);

  // Use ptr...

  free(ptr);  // Free the memory block pointed to by ptr

Example

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

  int main()  {
    int *ptr, n, i;

    printf("Enter number of integers: "); 
    scanf("%d", &n);

    ptr = malloc(n * sizeof(int));

    if (ptr == NULL) {
      printf("Error! Unable to allocate memory.");
      exit(0);
    }

    // Take input and store in array
    for (i = 0; i < n; i++) {
      scanf("%d", &ptr[i]);  
    }

    // Use allocated memory

    free(ptr);  // Free the memory block pointed to by ptr

    return 0;
  }

Memory Leak

What is memory leak?

A memory leak occurs when a program allocates memory but fails to free it after use. This leads to a gradual buildup of unused memory blocks over time.

Causes of memory leak

The main causes of memory leaks are:

  • Forgetting to free allocated memory. For example, by losing the pointer to the allocated memory.

  • Holding onto pointers longer than necessary. For example, storing pointers in global variables.

  • Creating reference cycles between data structures that point to each other.

  • Handling allocation failure incorrectly and not freeing previously allocated memory.

How to avoid memory leak

Some ways to avoid memory leaks are:

  • Free all allocated memory using free().

  • Set pointers to NULL after freeing to avoid accidental reuse.

  • Limit the scope of pointers as much as possible.

  • Use tools like Valgrind to detect memory leaks and debug memory issues.

Dangling Pointer

What is a dangling pointer?

A dangling pointer is a pointer that references a memory location that may have been deleted or reallocated. This can lead to undefined behaviour and crashes.

How dangling pointers are created

Dangling pointers are created when:

  • A pointer refers to a memory location that is freed. The pointer still refers to the same memory location even though the memory has been freed.

      int *ptr = malloc(sizeof(int));
      free(ptr); 
    
      // ptr is now a dangling pointer!
    
  • A pointer refers to a local variable that has gone out of scope.

      int *func() {
        int x = 5;  
        int *ptr = &x;  
        return ptr; 
      }
    
      // ptr now dangles as x is out of scope
    

How to avoid dangling pointers

To avoid dangling pointers:

  • Set pointers to NULL after freeing. This helps avoid accidental reuse.

      free(ptr);
      ptr = NULL;
    
  • Do not return pointers to local variables.

  • Limit the scope of pointers as much as possible.

The consequences of dangling pointers are undefined behavior such as program crashes. Dereferencing a dangling pointer may access invalid memory locations.