Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Dynamic Struct Allocation

Hello! In our last lesson, we mastered creating dynamically-sized arrays for simple data types like int and char. You learned how to request memory from the heap based on runtime needs, a crucial skill for writing flexible programs.

Today, we will build directly on that knowledge. Instead of allocating memory for simple integers, we will learn how to dynamically allocate memory for instances of structs. This is where the true power of dynamic memory allocation begins to shine, as it allows us to create and manage collections of complex, custom data objects. This is a fundamental building block for nearly all advanced data structures you will encounter in programming.

By the end of this lesson, you will be able to write C programs that create, manipulate, and properly deallocate both single struct instances and entire arrays of structs on the heap.

1. Allocating Memory for a Single Struct

In previous lessons, you worked with structs that were created on the stack, like this:

struct Point {
    int x;
    int y;
};

int main() {
    struct Point p1; // p1 is created on the stack
    p1.x = 10;
    p1.y = 20;
    // ...
}

This is fine, but just like with arrays, it requires the struct to exist only within the scope where it's declared. To create a struct that can persist and be passed around more flexibly, we need to allocate it on the heap.

The process is very similar to what you've already learned with malloc():

  1. Declare a pointer to your struct type.
  2. Allocate memory using malloc(), providing sizeof(your_struct_type) as the argument.
  3. Check for NULL to ensure the allocation succeeded.

The main difference lies in how you access the struct's members. Since you have a pointer, not the struct itself, you can't use the dot (.) operator directly. You need a special operator: the arrow operator (->).

Let's see this in action. The following video demonstrates how to allocate a single struct and introduces both ways to access its members.

Dynamically Allocate An Array Of Structs | C Programming Tutorial

This video from Portfolio Courses, "Dynamically Allocate An Array Of Structs," starts by clearly explaining how to allocate a single struct instance.

Watch from the single struct allocation. Pay close attention to these two syntaxes for accessing the members: (*p2).x: This dereferences the pointer p2 to get the actual struct, then uses the dot operator. The parentheses are mandatory because the dot operator has higher precedence than the dereference operator (*). p2->x: This is the arrow operator. It's syntactic sugar that does the exact same thing as (*p2).x but is much cleaner and is the standard way to access members through a pointer to a struct.

To summarize, here is the complete pattern for a single dynamically allocated struct:




#include <stdio.h>
#include <stdlib.h> // For malloc() and free()

// Define a struct for a student
struct Student {
    int student_id;
    float gpa;
};

int main() {
    // 1. Declare a pointer to the struct
    struct Student *new_student;

    // 2. Allocate memory on the heap
    new_student = (struct Student*) malloc(sizeof(struct Student));

    // 3. ALWAYS check if allocation was successful
    if (new_student == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }

    // 4. Assign values using the arrow operator ->
    printf("Allocating new student...\n");
    new_student->student_id = 101;
    new_student->gpa = 3.8;

    // Access values using the arrow operator
    printf("Student ID: %d\n", new_student->student_id);
    printf("GPA: %.1f\n", new_student->gpa);

    // 5. Free the memory when done
    free(new_student);
    new_student = NULL; // Good practice to avoid a dangling pointer

    return 0;
}

2. Allocating an Array of Structs

Now, let's scale this up. What if you need to store data for a whole class of students, but you don't know the class size at compile time? You need a dynamic array of structs.

The allocation logic is a direct combination of what you know about dynamic arrays and sizeof(struct):

ptr = (struct Student*) malloc(number_of_students * sizeof(struct Student));

Once allocated, you have a pointer to the start of a contiguous block of memory, where each chunk is the size of one Student struct. How do you access an element?

You can use the familiar array bracket notation []. When you write my_array[i], C calculates the memory address of the i-th struct in the block. Since my_array[i] refers to the actual struct object at that position (not a pointer to it), you use the standard dot operator (.) to access its members.

The next part of the video you started watching explains this beautifully.

Dynamically Allocate An Array Of Structs | C Programming Tutorial

Let's continue with the same video from Portfolio Courses. It transitions smoothly from a single struct to an array of them.

First, watch the segment on allocating the array. Notice the pattern malloc(length * sizeof(point)). Next, watch the section on accessing array elements. This is a key part. The presenter explains why you use array[i].x (the dot operator) and how it's equivalent to the pointer arithmetic (*(array + i)).x. This is an excellent clarification. Finally, see how this is put into practice by looping through the array to print all its contents.

For a text-based reference that you can easily review, the following W3Schools page provides clear, complete code examples for both single and array struct allocation.

C Structures and Dynamic Memory

This guide from W3Schools provides excellent, runnable examples for the concepts we've just discussed.

First, review the code under the heading "Allocating Memory for a Struct." It's a great summary of what we covered in our first section. Then, focus on the code under "Using Arrays of Structs." Notice how it allocates memory for 3 Car structs and then uses a for loop with cars[i].brand to access the members. This reinforces what you just saw in the video.

3. A Critical Detail: Structs Containing Pointers

This is an important topic that catches many C programmers by surprise. What happens if one of your struct's members is itself a pointer to dynamically allocated memory?

Consider this struct:

struct Book {
    char *title; // A pointer to a string on the heap
    int year;
};

If you create a dynamic array of Book structs, you will likely perform two malloc calls for each book: one for the Book struct itself (as part of the array) and another for its title.

When you call free(book_array), you are only freeing the memory for the array of Book structs. The memory you allocated for each title is not freed, resulting in a memory leak.

The Rule: You must free every piece of memory you explicitly allocate. To do this correctly, you must free the "inner" pointers before you free the "outer" container.

The final segment of the video demonstrates this exact scenario and its solution.

Dynamically Allocate An Array Of Structs | C Programming Tutorial

This is the most advanced part of the lesson and is crucial for writing correct C programs.

Watch from the nested allocation example. The presenter adds a char *description member to the struct. Observe carefully: How memory is allocated for description inside the loop for each struct. The two-step process for deallocation: a for loop to free each individual description, followed by a single free for the array itself.

Your Turn: Practice with Dynamic Structs

Let's put this all together with a practical exercise.

Task:
Write a C program that models a small company's employee directory.

  1. Define a struct Employee with two members: int id; and char name[100];.
  2. Ask the user how many employees they want to enter.
  3. Dynamically allocate an array of Employee structs of the requested size.
  4. Perform an error check to ensure the malloc call was successful.
  5. Use a for loop to prompt the user to enter the ID and name for each employee. Store this data in your dynamic array.
  6. After all data is entered, use another for loop to print the details of all employees in a clean format.
  7. Finally, free the allocated memory and set the pointer to NULL.

Example Interaction:

How many employees to enter? 2
---
Enter details for Employee 1:
ID: 101
Name: Alice
---
Enter details for Employee 2:
ID: 102
Name: Bob
---

Employee Directory:
Employee 1: ID=101, Name=Alice
Employee 2: ID=102, Name=Bob
Click for a hint if you're stuck When reading the name, be careful. If you use `scanf("%s", ...)` it will stop at the first space. If you want to read a full name like "Alice Smith", you might need a different approach. However, for this exercise, you can assume names have no spaces. Remember to access the members for the i-th employee using `directory[i].id` and `directory[i].name`.

Click to see a possible solution



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

struct Employee {
    int id;
    char name[100];
};

int main() {
    int count;
    struct Employee *directory;

    // 1. Ask for size
    printf("How many employees to enter? ");
    scanf("%d", &count);

    // 2. Allocate memory for the array of structs
    directory = (struct Employee*) malloc(count * sizeof(struct Employee));

    // 3. Verify allocation
    if (directory == NULL) {
        printf("Error: Memory allocation failed!\n");
        return 1;
    }

    // 4. Fill the array
    for (int i = 0; i < count; i++) {
        printf("---\n");
        printf("Enter details for Employee %d:\n", i + 1);
        printf("ID: ");
        // Access struct members using array index and dot operator
        scanf("%d", &directory[i].id);
        printf("Name: ");
        scanf("%s", directory[i].name);
    }

    // 5. Print the array
    printf("\n---\nEmployee Directory:\n");
    for (int i = 0; i < count; i++) {
        printf("Employee %d: ID=%d, Name=%s\n",
               i + 1, directory[i].id, directory[i].name);
    }

    // 6. Clean up
    free(directory);
    directory = NULL;

    return 0;
}

Conclusion

You've now taken a significant step forward, moving from dynamic arrays of simple types to dynamic collections of complex objects. This skill is the bedrock upon which data structures like linked lists, queues, and trees are built.

Key Takeaways:

  • To allocate a single struct on the heap, use malloc(sizeof(struct_type)).
  • To allocate an array of n structs, use malloc(n * sizeof(struct_type)).
  • When you have a pointer to a struct, use the arrow operator (->) to access its members (e.g., ptr->member).
  • When you have a dynamically allocated array of structs, use array notation and the dot operator (.) to access members (e.g., array[i].member).
  • If a struct contains pointers to other dynamically allocated memory, you must free the inner memory first before freeing the struct container itself to avoid memory leaks.

Preview of the Next Lesson:
Our discussion today about nested dynamic allocation is a perfect gateway to our next topic. We will take a broader look at common memory management pitfalls. You will learn how to formally identify and prevent common memory errors like memory leaks and dangling pointers, ensuring your C programs are not just functional, but also robust and reliable.

Can't find a good explanation? Sign up and we'll make it for you

Sign up