Recommended resources
The freely available resources this lesson will direct you to.
Hello! In our last lesson, you learned the fundamentals of managing memory on the heap with malloc() and free(). We established that this manual control is essential for creating data structures that need to exist beyond a single function's scope or whose size isn't known when you compile your code.
Today, we will focus on the most common and powerful application of this skill: creating dynamically-sized arrays. While we touched on this in the previous lesson's example, this lesson is dedicated to mastering the complete lifecycle of a dynamic array—from determining its size at runtime to safely destroying it. This technique frees you from the rigid constraints of static arrays and is a prerequisite for building more advanced data structures.
By the end of this lesson, you will be able to confidently write C programs that create, populate, and manage arrays whose size is determined by user input or other runtime conditions.
1. The Limitations of Fixed-Size Arrays
Before we dive into the "how," let's solidify the "why." You've used standard arrays like int grades[30]; before. They are simple and effective, but they have a significant drawback: their size is fixed when the program is compiled. This forces you to make a choice:
- Allocate a huge array: You might declare
int grades[5000];to be safe. But if the user only enters 20 grades, you've wasted nearly all of that memory. - Allocate a small array: If you declare
int grades[50];and the user needs to enter 51, your program will fail or, worse, corrupt other memory.
Dynamic memory allocation solves this dilemma by allowing you to request exactly the amount of memory you need, right when you need it. The following reading explains this problem in more detail.
8.1. What is dynamic memory allocation? — Snefru
This text from the "Snefru" C programming guide provides an excellent motivation for dynamic allocation.
Read the introduction and the section "Different options when array size is unknown at compile-time." Focus on understanding the trade-offs of using a fixed-size array versus the flexibility offered by allocating memory on the heap. Pay attention to the diagram showing the memory layout (code, global, heap, stack), which reinforces what we discussed in the last lesson.
As the text mentions, while some versions of C support Variable Length Arrays (VLAs) like int arr[size];, they are allocated on the stack. This means a very large user-provided size could cause a "stack overflow." Allocating on the much larger heap is the more robust and universally supported solution.
2. The Dynamic Array Recipe
Creating a dynamic array in C follows a reliable pattern. Let's walk through it step-by-step. We will use a clear and practical video to demonstrate each part of the process.
Step 1: Get the Size and Calculate Bytes
First, your program needs to determine how many elements the array will hold. This usually comes from user input. Once you have the number of elements, you must calculate the total memory required in bytes.
Remember the rule: number of elements × size of one element.
Watch the beginning of the video "Malloc in C is easy!" from the Bro Code channel. It sets up the problem and shows how to get the user's desired array size and calculate the bytes needed for malloc.
Watch from the start up to the size calculation. Notice how the user's input (number) is multiplied by sizeof(char) to get the total byte size for the malloc call.
Step 2: Allocate Memory with malloc()
With the size in bytes calculated, you call malloc(). As we learned, malloc() returns a generic void* pointer, which you must cast to the correct pointer type (e.g., int*, float*, char*). This tells the compiler how to interpret the memory block.
int n;
// ... get value for n from user ...
// Allocate a block of memory for 'n' integers
int* arr = (int*)malloc(n * sizeof(int));
The video you just watched demonstrated this. For a more formal explanation of why malloc returns void* and why the cast is necessary, the following resource is helpful.
Dynamic Memory Allocation using malloc()
This video from Neso Academy provides a more theoretical look at the malloc function's design.
Watch the segment from void pointer explanation. The key idea is that malloc only knows about bytes; it doesn't know what type of data you plan to store. It's your job to tell the compiler by casting the pointer.
Step 3: Verify the Allocation
This step is non-negotiable. If malloc() cannot find enough contiguous memory on the heap, it returns NULL. You must check for this before using the pointer.
if (arr == NULL) {
printf("Memory allocation failed!\n");
// Exit or handle the error gracefully
return 1;
}
Step 4: Use the Array
Once you have a valid pointer, you can use it just as you would a regular array name. The [] bracket notation works perfectly. The compiler understands that arr[i] means "go to the starting address arr, move i elements forward, and access the value there."
The following video clip shows how to populate the newly allocated array with user input and then print the elements back out, treating the pointer grades exactly like a static array.
Let's continue with the Bro Code video to see how the dynamically allocated array is used.
First, watch the segment on populating the array using a for loop and scanf. Then, see how it's printed in the next loop, from printing the grades.
Behind the scenes, the array notation arr[i] is equivalent to the pointer arithmetic *(arr + i). While bracket notation is usually more readable, understanding the pointer arithmetic is key to mastering C. The Neso Academy video explains this connection clearly.
Dynamic Memory Allocation using malloc()
This short clip explains how pointer arithmetic is used to access elements in a dynamically allocated block.
Watch from the ptr + i explanation. It shows how adding i to a pointer automatically scales by the size of the data type.
Step 5: Free the Memory
Finally, when you are finished with the array, you must return the memory to the heap using free(). Forgetting this step causes a memory leak. As a best practice, you should also set the pointer to NULL immediately after freeing it to prevent it from becoming a dangling pointer.
free(arr);
arr = NULL;
We covered this in the last lesson, and the Bro Code video also provides a good recap of this responsibility, using the analogy of returning a rented apartment and its key.
3. Complete Code Example
Now, let's look at a complete, self-contained example that puts all these steps together. The following resource provides a clean and simple program that dynamically creates an integer array.
This page from TutorialsPoint provides another excellent, concise example of our dynamic array recipe.
Read the section "Dynamic Array Using malloc() Function" and carefully study the example C program within it. Trace its logic: It prompts the user for the size n. It calls malloc and casts the result to (int*). It performs the essential if (ptr == NULL) check. It uses a for loop to assign values. It uses another for loop to print the values. Finally, it calls free(ptr).
Your Turn: Practice Creating a Dynamic Array
It's time to solidify your understanding by writing some code. This exercise will test your ability to follow the complete dynamic array recipe.
Task:
Write a C program that does the following:
- Asks the user for an integer,
n. - Dynamically allocates an array of
nintegers. - Performs an error check to ensure the allocation was successful.
- Fills the array with the first
neven numbers (i.e.,arr[0] = 0,arr[1] = 2,arr[2] = 4, and so on). - Prints all the elements of the array to the console, separated by spaces.
- Frees the dynamically allocated memory and sets the pointer to
NULLbefore the program exits.
For example, if the user enters 5, the output should be: 0 2 4 6 8
Click for a hint if you're stuck
To calculate the i-th even number, you can simply use the formula i * 2. Your loop for filling the array might look something like this:
for (int i = 0; i < n; i++) {
my_array[i] = i * 2;
}
Click to see a possible solution
#include <stdio.h>
#include <stdlib.h> // For malloc() and free()
int main() {
int n;
int* even_numbers;
// 1. Ask for size
printf("Enter the number of even numbers to generate: ");
scanf("%d", &n);
// 2. Allocate memory
even_numbers = (int*)malloc(n * sizeof(int));
// 3. Verify allocation
if (even_numbers == NULL) {
printf("Error: Memory allocation failed!\n");
return 1; // Exit with an error code
}
// 4. Fill the array
for (int i = 0; i < n; i++) {
even_numbers[i] = i * 2;
}
// 5. Print the array
printf("Generated even numbers: ");
for (int i = 0; i < n; i++) {
printf("%d ", even_numbers[i]);
}
printf("\n");
// 6. Clean up
free(even_numbers);
even_numbers = NULL;
return 0;
}
Conclusion
You have now mastered a fundamental and powerful technique in C programming. By moving from fixed-size, stack-allocated arrays to dynamically-sized, heap-allocated arrays, you can write far more flexible, efficient, and robust programs.
Key Takeaways:
- Dynamic arrays solve the problem of not knowing the required array size at compile time.
- The creation process follows a reliable 5-step recipe:
- Get Size: Determine the number of elements needed at runtime.
- Allocate: Use
malloc(n * sizeof(type))to reserve memory on the heap. - Verify: Always check if the returned pointer is
NULL. - Use: Treat the pointer like a regular array using
[]notation. - Free: Release the memory with
free()and set the pointer toNULL.
Preview of the Next Lesson:
So far, we've only allocated arrays of simple types like int and char. But what about more complex, custom data types? In the next lesson, we will extend this skill to dynamically allocate memory for instances of structs. This will allow you to create and manage collections of complex objects, a crucial step toward building sophisticated data structures.