Chapter 24Advanced

Structures and Dynamic Memory

Combine structs with malloc! Create dynamic arrays of structures that grow as needed. Build real data structures.

18 min readUpdated 2024-12-16
struct mallocdynamic structarray of structsarrow operator->

What You Will Learn

  • Allocate structs dynamically
  • Use arrow operator (->) with pointers
  • Create dynamic arrays of structs
  • Build growing lists with realloc

01Why Dynamic Memory with Structures?

🚗 Real-World Example: Car Dealership

Imagine you're building a car dealership program. You don't know how many cars you'll have in advance. Dynamic memory lets you allocate exactly what you need!

❌ Static Array Problem

struct Car cars[100];

✅ Dynamic Solution

struct Car *cars = malloc(n * sizeof(struct Car));

FeatureStatic Struct ArrayDynamic Struct Array
SizeFixed at compile timeSet at runtime
Can grow?NoYes (realloc)
MemoryMay waste spaceEfficient
CleanupAutomaticManual (free)

02Allocating Memory for One Struct

struct_one.c
C
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5struct Car {
6 char brand[50];
7 int year;
8};
9
10int main() {
11 // Allocate memory for one Car
12 struct Car *ptr = malloc(sizeof(struct Car));
13
14 if (ptr == NULL) {
15 printf("Allocation failed!\n");
16 return 1;
17 }
18
19 // Set values using -> (arrow operator)
20 strcpy(ptr->brand, "Honda");
21 ptr->year = 2022;
22
23 // Print values
24 printf("Brand: %s\n", ptr->brand);
25 printf("Year: %d\n", ptr->year);
26
27 // Free memory
28 free(ptr);
29 return 0;
30}
Output

Brand: Honda

Year: 2022

💡 Arrow Operator (→)

When you have a pointer to a struct, use ptr->memberinstead of ptr.member. The arrow is shorthand for (*ptr).member.

03Arrays of Structs

You can allocate memory for multiple structs at once, like an array. Use malloc(count * sizeof(struct)).

struct_array.c
C
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5struct Car {
6 char brand[50];
7 int year;
8};
9
10int main() {
11 // Allocate memory for 3 cars
12 struct Car *cars = malloc(3 * sizeof(struct Car));
13
14 if (cars == NULL) {
15 printf("Allocation failed!\n");
16 return 1;
17 }
18
19 // Fill the data (use array notation!)
20 strcpy(cars[0].brand, "Ford");
21 cars[0].year = 2015;
22
23 strcpy(cars[1].brand, "BMW");
24 cars[1].year = 2018;
25
26 strcpy(cars[2].brand, "Volvo");
27 cars[2].year = 2023;
28
29 // Print the data
30 for (int i = 0; i < 3; i++) {
31 printf("%s - %d\n", cars[i].brand, cars[i].year);
32 }
33
34 free(cars);
35 return 0;
36}
Output

Ford - 2015

BMW - 2018

Volvo - 2023

💡 Array Notation with Pointers

With an array of structs, use cars[i].member (dot notation). Only use arrow -> when accessing through a single pointer.

04Growing Arrays with realloc()

Need more elements later? Use realloc() to resize! Always use a temp pointer to avoid memory leaks if realloc fails.

struct_realloc.c
C
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5struct Car {
6 char brand[50];
7 int year;
8};
9
10int main() {
11 int count = 2;
12 struct Car *cars = malloc(count * sizeof(struct Car));
13
14 if (cars == NULL) {
15 printf("Allocation failed!\n");
16 return 1;
17 }
18
19 // Initialize first 2 cars
20 strcpy(cars[0].brand, "Toyota");
21 cars[0].year = 2010;
22 strcpy(cars[1].brand, "Audi");
23 cars[1].year = 2019;
24
25 // Need one more car -> grow to 3
26 int newCount = 3;
27 struct Car *tmp = realloc(cars, newCount * sizeof(struct Car));
28
29 if (tmp == NULL) {
30 free(cars); // Free original if realloc fails
31 printf("Realloc failed!\n");
32 return 1;
33 }
34 cars = tmp; // Use the reallocated block
35
36 // Initialize new element
37 strcpy(cars[2].brand, "Kia");
38 cars[2].year = 2022;
39
40 // Print all cars
41 for (int i = 0; i < newCount; i++) {
42 printf("%s - %d\n", cars[i].brand, cars[i].year);
43 }
44
45 free(cars);
46 return 0;
47}
Output

Toyota - 2010

Audi - 2019

Kia - 2022

⚠️ New Space is Uninitialized!

When realloc() expands memory, the new space contains garbage values. Always initialize new elements before use!

05Real-Life Example: Dynamic List

Here's a practical example: a list that grows automaticallywhen you add items. This is how many real programs manage dynamic data.

dynamic_list.c
C
1#include <stdio.h>
2#include <stdlib.h>
3
4struct List {
5 int *data; // Points to memory
6 int count; // Items in list
7 int capacity; // Total space
8};
9
10void addItem(struct List *list, int item) {
11 // Grow if full
12 if (list->count == list->capacity) {
13 int newCap = list->capacity + 5;
14 int *tmp = realloc(list->data, newCap * sizeof(int));
15 if (tmp == NULL) return;
16 list->data = tmp;
17 list->capacity = newCap;
18 }
19 list->data[list->count] = item;
20 list->count++;
21}
22
23int main() {
24 struct List myList;
25 myList.count = 0;
26 myList.capacity = 5;
27 myList.data = malloc(5 * sizeof(int));
28
29 if (myList.data == NULL) return 1;
30
31 // Add 12 items (will grow automatically!)
32 for (int i = 0; i < 12; i++) {
33 addItem(&myList, (i + 1) * 10);
34 }
35
36 // Print all items
37 for (int i = 0; i < myList.count; i++) {
38 printf("%d ", myList.data[i]);
39 }
40
41 free(myList.data);
42 return 0;
43}
Output

10 20 30 40 50 60 70 80 90 100 110 120

📝 How It Works

  1. 1.Start with space for 5 items
  2. 2.When full, realloc() adds 5 more slots
  3. 3.List grows: 5 → 10 → 15 as needed
  4. 4.This is called a "dynamically growing array"

06Summary

🎯 Key Takeaways

  • One struct: malloc(sizeof(struct Car))
  • Array of structs: malloc(n * sizeof(struct Car))
  • Pointer to struct: Use arrow ptr->member
  • Array notation: Use dot cars[i].member
  • Growing: Use temp pointer with realloc()
  • Always: Check NULL, free() when done

struct Car *car = malloc(sizeof(struct Car));

if (car == NULL) return 1;

strcpy(car->brand, "Honda");

car->year = 2022;

free(car);

08Next Steps

Learn to read and write files in C: