Structures and Dynamic Memory
Combine structs with malloc! Create dynamic arrays of structures that grow as needed. Build real data structures.
Track Your Progress
Sign in to save your learning progress
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!
The Problem: Unknown Size at Compile Time
Your Program
How many cars?
User Input
5? 50? 500?
Solution
malloc()
Static Array Problem
// Fixed size - wasteful!
struct Car cars[100];
✓Dynamic Solution
// Allocate exact size needed
struct Car *cars = malloc(3 * sizeof(struct Car));
Where Does the Struct Live?
Stack (Static)
struct Car myCar;
✓ Automatic cleanup
✗ Fixed size
✗ Limited space (~1MB)
Heap (Dynamic)
struct Car *car = malloc(...);
✓ Flexible size
✓ Large capacity (GBs)
✗ Manual cleanup (free)
| Feature | Static Struct Array | Dynamic Struct Array |
|---|---|---|
| Size | Fixed at compile time | Set at runtime |
| Can grow? | No | Yes (realloc) |
| Memory | May waste space | Efficient |
| Cleanup | Automatic | Manual (free) |
02Allocating Memory for One Struct
Step-by-Step: What Happens in Memory
struct Car {
char brand[50]; // 50 bytes
int year; // 4 bytes
};
Memory needed per Car:
Total: ~54+ bytes (may include padding)
struct Car *ptr = malloc(sizeof(struct Car));
Stack (ptr)
Heap (struct Car)
brand[50] (garbage)
year (garbage)
strcpy(ptr->brand, "Honda");
ptr->year = 2022;
Stack (ptr)
Heap (struct Car)
brand = "Honda"
year = 2022
free(ptr);
ptr = NULL; // Good practice
1#include <stdio.h>2#include <stdlib.h>3#include <string.h>45struct Car {6 char brand[50];7 int year;8};910int main() {11 // Allocate memory for one Car12 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 values24 printf("Brand: %s\n", ptr->brand);25 printf("Year: %d\n", ptr->year);26 27 // Free memory28 free(ptr);29 return 0;30}Brand: Honda
Year: 2022
Arrow vs Dot: When to Use Which?
→ Arrow Operator (ptr->member)
// When you have a POINTER
struct Car *ptr = malloc(...);
ptr->year = 2022;
Use -> when accessing through a pointer
. Dot Operator (var.member)
// When you have the STRUCT itself
struct Car myCar;
myCar.year = 2022;
Use . when accessing a direct variable
Memory Tip: ptr->member is shorthand for (*ptr).member
Why sizeof(struct Car)?
Always use sizeof(struct Car) instead of manually calculating bytes. The compiler may add padding bytes for alignment, so manual calculation can be wrong!
03Arrays of Structs
You can allocate memory for multiple structs at once, like an array. Use malloc(count * sizeof(struct)).
Memory Layout: Array of 3 Structs
Pointer (cars)
Heap: Contiguous Memory Block
cars[0]
"Ford"
2015
@0x1000
cars[1]
"BMW"
2018
@0x1036
cars[2]
"Volvo"
2023
@0x106C
Key Insight: All 3 structs are stored side by side in memory, just like a regular array!
# How Array Indexing Works
cars[0]
cars + 0 × sizeof(Car)
= First struct
cars[1]
cars + 1 × sizeof(Car)
= Second struct
cars[2]
cars + 2 × sizeof(Car)
= Third struct
1#include <stdio.h>2#include <stdlib.h>3#include <string.h>45struct Car {6 char brand[50];7 int year;8};910int main() {11 // Allocate memory for 3 cars12 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 data30 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}Ford - 2015
BMW - 2018
Volvo - 2023
Two Ways to Access Elements
Array Notation (Recommended)
cars[0].brand
cars[1].year
Cleaner, more readable
Pointer Arithmetic
(cars + 0)->brand
(cars + 1)->year
Same result, less common
Why Dot with Array Notation?
cars[i] already dereferences the pointer, giving you the struct itself. So you use . (dot) not -> (arrow).
04Growing Arrays with realloc()
Need more elements later? Use realloc() to resize! Always use a temp pointer to avoid memory leaks if realloc fails.
Visual: How realloc() Resizes Struct Arrays
cars
[0]
Toyota
2010
[1]
Audi
2019
tmp = realloc(cars, 3 * sizeof(struct Car));cars
(may move!)
[0]
Toyota
2010
✓ preserved
[1]
Audi
2019
✓ preserved
[2]
???
????
garbage!
Important: New slot [2] contains garbage values! Initialize it before use.
Safe realloc Pattern
Dangerous
// If realloc fails...
cars = realloc(cars, ...);
// cars is NULL, old memory leaked!
✓Safe
struct Car *tmp = realloc(cars, ...);
if (tmp != NULL) {
cars = tmp;
}
1#include <stdio.h>2#include <stdlib.h>3#include <string.h>45struct Car {6 char brand[50];7 int year;8};910int 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 cars20 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 326 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 fails31 printf("Realloc failed!\n");32 return 1;33 }34 cars = tmp; // Use the reallocated block35 36 // Initialize new element37 strcpy(cars[2].brand, "Kia");38 cars[2].year = 2022;39 40 // Print all cars41 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}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
Building a Self-Growing Container
Here's a practical example: a list that grows automaticallywhen you add items. This is how many real programs (and languages like Python, Java) manage dynamic data!
How the Dynamic List Grows
1#include <stdio.h>2#include <stdlib.h>34struct List {5 int *data; // Points to memory6 int count; // Items in list7 int capacity; // Total space8};910void addItem(struct List *list, int item) {11 // Grow if full12 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}2223int 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 items37 for (int i = 0; i < myList.count; i++) {38 printf("%d ", myList.data[i]);39 }40 41 free(myList.data);42 return 0;43}10 20 30 40 50 60 70 80 90 100 110 120
How It Works
- 1.Start with space for 5 items
- 2.When full,
realloc()adds 5 more slots - 3.List grows: 5 → 10 → 15 as needed
- 4.This is called a "dynamically growing array"
06Structs with Pointer Members (Deep Copy)
Important: Nested Dynamic Memory
When a struct has pointer members, you need to allocate memory for boththe struct AND what the pointer points to. This requires extra care when freeing!
The Problem: Shallow vs Deep Copy
Shallow Copy (Problem!)
Same memory!
Both point to same string! Changing one affects both!
✓Deep Copy (Correct!)
Each has its own copy! Independent!
1#include <stdio.h>2#include <stdlib.h>3#include <string.h>45struct Person {6 char *name; // Pointer - needs separate allocation!7 int age;8};910// Deep copy function11struct Person* copyPerson(struct Person *src) {12 // Allocate struct13 struct Person *copy = malloc(sizeof(struct Person));14 if (copy == NULL) return NULL;15 16 // Allocate and copy the name string (DEEP COPY)17 copy->name = malloc(strlen(src->name) + 1);18 if (copy->name == NULL) {19 free(copy);20 return NULL;21 }22 strcpy(copy->name, src->name);23 copy->age = src->age;24 25 return copy;26}2728// Free function - must free name first!29void freePerson(struct Person *p) {30 if (p != NULL) {31 free(p->name); // Free inner pointer first32 free(p); // Then free struct33 }34}3536int main() {37 // Create person 138 struct Person *p1 = malloc(sizeof(struct Person));39 p1->name = malloc(20);40 strcpy(p1->name, "Alice");41 p1->age = 25;42 43 // Create deep copy44 struct Person *p2 = copyPerson(p1);45 46 // Modify p2's name - won't affect p1!47 strcpy(p2->name, "Bob");48 49 printf("Person 1: %s, %d\n", p1->name, p1->age);50 printf("Person 2: %s, %d\n", p2->name, p2->age);51 52 // Free both53 freePerson(p1);54 freePerson(p2);55 56 return 0;57}Person 1: Alice, 25
Person 2: Bob, 25
Correct Free Order (Inside → Outside)
Free inner pointer first
Then free the struct
Wrong order = Memory leak! If you free struct first, you lose access to the name pointer.
07Common Memory Pitfalls
Using After Free
free(car);
printf("%s", car->brand);
// Undefined behavior!
Set pointer to NULL after free.
Double Free
free(car);
free(car);
// Crash or corruption!
Only free once per allocation.
Not Checking NULL
struct Car *car = malloc(...);
car->year = 2022;
// If malloc failed, crash!
Always check if malloc returned NULL.
Memory Leak
car = malloc(sizeof(Car));
car = malloc(sizeof(Car));
// First block is lost forever!
Free old memory before reassigning.
✓Best Practices Checklist
- ✓Always check
malloc()return value - ✓Use
sizeof(struct X)not manual sizes - ✓Set pointers to
NULLafter free
- ✓Free nested pointers before the struct
- ✓Use temp pointer for
realloc() - ✓Use tools like Valgrind to detect leaks
!Code Pitfalls: Common Mistakes & What to Watch For
Copied code often generates struct allocation code that looks correct but contains subtle memory management errors.
If you search online to "allocate a struct with a string member dynamically," it might give you code that allocates the struct but forgets to allocate separate memory for the string pointer. Or it might free the struct before freeing its nested pointers, causing memory leaks.
The Trap: Online sources often produce syntactically correct code but miss the fundamental rule: when a struct contains pointers, you need to allocate and free memory for both the struct and what the pointers point to. They might also confuse shallow copy (copying pointers) with deep copy (copying the actual data), leading to double-free errors or dangling pointers.
The Reality: Dynamic struct memory is a two-level problem. Always remember: allocate outside-in (struct first, then members), but free inside-out (members first, then struct). Copying code without understanding this ownership model will lead to crashes, memory leaks, and corrupted data.
09Frequently Asked Questions
Q:Why would I dynamically allocate a struct?
A: When you don't know how many structs you'll need at compile time (user input, file data), when the struct is too large for the stack, or when you need the struct to outlive the function that creates it.
Q:What's the difference between ptr->member and (*ptr).member?
A: They're identical! The arrow operator -> is just shorthand for dereferencing then accessing. ptr->name is cleaner than(*ptr).name and universally preferred.
Q:How do I free a struct with nested pointers?
A: Free in reverse order of allocation! First free all members that were malloc'd (like strings), then free the struct itself. Freeing the struct first loses access to its member pointers = memory leak.
Q:Can I use realloc on an array of structs?
A: Yes!realloc(arr, newCount * sizeof(struct))works perfectly. But always use a temp pointer in case realloc fails, and remember the new elements are uninitialized.
09Summary
Key Operations
- •One struct:
malloc(sizeof(struct Car)) - •Array:
malloc(n * sizeof(struct Car)) - •Resize:
realloc(ptr, newSize) - •Cleanup:
free(ptr)
Quick Reference
Complete Memory Lifecycle
malloc()
Allocate
if (ptr != NULL)
Check
ptr->data = ...
Use
realloc()
Resize (if needed)
free()
Release
// Complete template for struct with dynamic memory:
struct Car *car = malloc(sizeof(struct Car));
if (car == NULL) { return 1; }
strcpy(car->brand, "Honda");
car->year = 2022;
// ... use the struct ...
free(car);
car = NULL; // Good practice!
Test Your Knowledge
Related Tutorials
Dynamic Memory Allocation
Allocate memory at runtime! Use malloc, calloc, realloc, and free. Create arrays whose size you don't know until the program runs.
Pointer Arithmetic in C
Add and subtract with pointers! Learn how pointer math works with arrays and why ptr++ moves by the size of the data type.
Structures in C
Group related data together! Create your own data types with struct. Store a student's name, age, and grade in one variable.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!