Back to C Programming
2026-02-028 min read

16.9 Arrays of Variable Length (C Programming)

Learn 16.9 Arrays of Variable Length (C Programming) step by step with clear examples and exercises.

Title: Arrays of Variable Length in C Programming

Why This Matters

Arrays of variable length, also known as dynamic arrays, are essential for managing data structures that can change size during program execution. They allow us to handle situations where the size of an array is not known at compile time or when the amount of data to be stored exceeds the initial size of the array. Understanding dynamic arrays will help you solve real-world problems and tackle more complex programming tasks.

Dynamic arrays can be particularly useful in scenarios like reading user input, processing large datasets, or working with data from external files where the exact size is not known beforehand.

Prerequisites

To follow this lesson, you should have a good understanding of:

  1. Basic C syntax and control structures (if-else statements, loops)
  2. Pointers in C
  3. Memory allocation functions like malloc() and free()
  4. Understanding the difference between static and dynamic memory allocation
  5. Familiarity with standard input/output functions such as scanf(), printf(), and fopen()
  6. Comfort working with multi-dimensional arrays (optional but helpful)
  7. Understanding the concept of sentinel values for loop termination (optional but helpful)
  8. Knowledge about conditional compilation directives like #define and #ifdef (optional but helpful)

Core Concept

In C programming, arrays are typically declared with a fixed size. However, when dealing with variable-length data, we can create dynamic arrays by using pointers to manage the array's memory dynamically.

A dynamic array is an array whose size can be changed during runtime. It is created and managed using pointer variables that point to the starting address of the allocated memory block. Here are the steps to create a dynamic array:

  1. Allocate memory for the array using malloc().
  2. Store the base address returned by malloc() in a pointer variable.
  3. Use this pointer variable to access and manipulate the elements of the array.
  4. When needed, reallocate memory for the array to accommodate additional elements using the realloc() function.
  5. Free the allocated memory using free() when it is no longer required.
  6. To handle sentinel values for loop termination, you can use a separate variable or a special value within the data itself (e.g., -1).
  7. When using conditional compilation directives, you can define a constant to control the initial size of the array and adjust it as needed during runtime.

Worked Example

Let's create a simple dynamic array program that reads integers from the user and stores them in the array until the user enters a sentinel value (-1).

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

#define INITIAL_SIZE 10 // Define an initial size for the dynamic array

int main() {
int *arr = NULL; // Initialize the array pointer to NULL
size_t capacity = INITIAL_SIZE; // Initialize the initial capacity with a defined constant
size_t size = 0; // Initialize the number of elements to 0
int n, i;
int sentinelValue = -1; // Define the sentinel value

printf("Enter integers (enter %d to stop): \n", sentinelValue);

// Start with a defined initial capacity
arr = malloc(capacity * sizeof(int));

while (1) {
scanf("%d", &n);

if (n == sentinelValue) {
break;
}

// If the current size is equal to the capacity, double the capacity and reallocate memory
if (size == capacity) {
capacity *= 2;
arr = realloc(arr, capacity * sizeof(int));
if (!arr) {
printf("Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
}

// Store the input integer in the array at the current position
arr[size] = n;
size++;
}

printf("\nThe entered integers are:\n");
for (i = 0; i < size; i++) {
printf("%d ", arr[i]);
}

// Free the allocated memory when no longer needed
free(arr);

return 0;
}

In this example, we start with a defined initial capacity and double it whenever the current size equals the capacity. This approach ensures that we allocate just enough memory to store our data without wasting resources on unnecessary large arrays.

Common Mistakes

  1. Forgetting to allocate memory for the array: If you forget to allocate memory using malloc(), you will try to access uninitialized memory, leading to undefined behavior.
  2. Not checking for memory allocation failure: Always check if malloc() or realloc() succeeds in allocating memory. If not, handle the error gracefully and exit the program.
  3. Not reallocating memory when necessary: If you don't reallocate memory when the current size is insufficient, you may end up with a segmentation fault or overwriting adjacent memory.
  4. Leaking memory: Don't forget to free the allocated memory using free() when it is no longer required to prevent memory leaks.
  5. Not handling sentinel value correctly: Make sure to handle the sentinel value (e.g., -1) properly, as it signals the end of input. Failing to do so may cause an infinite loop or incorrect data processing.
  6. ### Subheadings under Common Mistakes:
  • Forgetting to initialize pointer variables: Initializing pointer variables to NULL is important to avoid accessing uninitialized memory.
  • Using invalid indices: Ensure that the indices used for accessing array elements are within the valid range (i.e., between 0 and size-1).
  • Not rechecking the capacity after reallocation: After reallocating memory, verify that the new capacity is indeed greater than or equal to the current size to avoid potential issues.
  • Not handling overflow or underflow: Check for cases where the capacity or size may exceed the available memory or become negative, respectively.
  • Not using conditional compilation directives properly: Make sure to use conditional compilation directives correctly when adjusting the initial size of the array during runtime.

Practice Questions

  1. Write a program that creates a dynamic array of strings and reads words from the user until a blank line is encountered. Store each word in the array, and then print out the contents of the array.
  2. Modify the worked example to handle negative numbers differently. For example, store them in a separate array or calculate their absolute values and add them to the original array.
  3. Write a program that dynamically allocates memory for an array of integers and initializes each element with a specific value (e.g., 0 or 1). The size of the array should be determined by the user at runtime using conditional compilation directives.
  4. Implement a dynamic array function that can perform common operations like adding, removing, and searching elements.
  5. Write a program that reads lines from a file and stores them in a dynamic array of strings. Calculate the total number of words and the average word length in the file.
  6. Write a program that creates a dynamic array of structures, where each structure represents a student with fields like name, roll number, and marks. Read data for multiple students from a file and store them in the dynamic array. Sort the array based on marks and print out the sorted list.
  7. ### Subheadings under Practice Questions:
  • Using sentinel values: Use sentinel values to terminate input loops when reading data from files or user input.
  • Handling errors: Implement error handling mechanisms to gracefully handle cases where memory allocation fails, files cannot be opened, or invalid data is encountered.
  • Optimizing memory usage: Explore techniques for optimizing memory usage in dynamic arrays, such as using smaller initial sizes, adjusting capacity based on the number of elements, and freeing memory when no longer needed.

FAQ

  1. What happens if I don't free the allocated memory?: If you don't free the dynamically allocated memory, it will not be returned to the operating system, leading to a memory leak. This can cause your program to consume more memory than necessary and potentially lead to performance issues or crashes.
  2. Why use dynamic arrays instead of fixed-size arrays?: Dynamic arrays are useful when you don't know the exact size of the data at compile time or when the data size may change during runtime. They provide flexibility in managing the memory required for the array, making them suitable for various real-world applications.
  3. What is the difference between malloc() and calloc()?: Both malloc() and calloc() are used to dynamically allocate memory, but they differ in how they initialize the allocated memory. malloc() allocates uninitialized memory, while calloc() initializes the memory to zero.
  4. What is the advantage of using realloc() over malloc()?: realloc() allows you to resize an already-allocated memory block. This means you can grow or shrink the size of the allocated memory without having to free and reallocate it from scratch, which can save time and reduce memory fragmentation.
  5. How do I find memory leaks in my program?: Memory leaks can be difficult to detect, but tools like Valgrind (for Linux) or Visual Studio's Memory Usage Analyzer (for Windows) can help you identify and fix memory leaks in your C programs.
  6. ### Subheadings under FAQ:
  • How can I prevent memory fragmentation?: To minimize memory fragmentation, try to allocate large contiguous blocks of memory when possible, and use realloc() instead of malloc() followed by multiple free() calls.
  • What is the best way to handle dynamic arrays in a multi-threaded environment?: In a multi-threaded environment, consider using thread-safe memory allocation functions like pthread_malloc(), pthread_calloc(), and pthread_realloc(). Also, ensure proper synchronization when accessing and modifying the dynamic array from multiple threads.
  • Can I use dynamic arrays with multi-dimensional arrays?: Yes, you can create multi-dimensional dynamic arrays by allocating memory for each dimension separately using nested loops and pointer arithmetic. However, be aware of potential performance issues due to increased memory fragmentation and slower access times compared to fixed-size multi-dimensional arrays.
  • How do I optimize the performance of dynamic arrays?: To optimize the performance of dynamic arrays, consider techniques like preallocating memory for large data structures, using efficient algorithms for common operations, and minimizing the number of reallocations by adjusting capacity based on the number of elements.