Dynamic Structs and Memory Layout
You know how to create structs. But so far, you’ve been creating them like a packed suitcase - decided ahead of time, fixed size, always the same.
What if you need to create structs while your program is running? What if you don’t know how many you’ll need until someone tells you?
Time to learn dynamic struct allocation.
Creating Structs with malloc
You can use malloc to create structs at runtime:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[50];
int age;
} Person;
int main(void) {
// Allocate space for one Person
Person *p = malloc(sizeof(Person));
if (p == NULL) {
printf("Couldn't allocate memory!\n");
return 1;
}
// Use -> because p is a pointer
strcpy(p->name, "Alice Smith");
p->age = 17;
printf("%s is %d years old\n", p->name, p->age);
free(p); // Don't forget!
return 0;
}Remember: when you have a pointer to a struct, use -> instead of . to access fields.
Arrays of Dynamic Structs
Need a bunch of structs? Allocate an array:
int count;
printf("How many heroes? ");
scanf("%d", &count);
// Allocate array of Person structs
Person *heroes = malloc(count * sizeof(Person));
if (heroes == NULL) {
printf("Out of memory!\n");
return 1;
}
// Use like a normal array
for (int i = 0; i < count; i++) {
printf("Hero %d name: ", i + 1);
scanf("%49s", heroes[i].name);
heroes[i].age = 25; // Default age
}
// Print them
for (int i = 0; i < count; i++) {
printf("%s\n", heroes[i].name);
}
free(heroes); // Free the whole arrayStructs with Pointer Members
Sometimes a struct contains a pointer to other memory. Think of a library card catalog - each card (struct) contains a reference to where the actual book is stored (dynamically allocated data).
typedef struct {
char *name; // Pointer to dynamically allocated string
int power_level;
} Hero;Why use a pointer instead of a fixed array like char name[50]?
- Names can be any length (not limited to 50 characters)
- Uses only the memory you need
- More flexible
But it requires more care:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
int power_level;
} Hero;
Hero *create_hero(const char *name, int power) {
// Allocate the struct
Hero *h = malloc(sizeof(Hero));
if (h == NULL) return NULL;
// Allocate space for the name (+1 for null terminator)
h->name = malloc(strlen(name) + 1);
if (h->name == NULL) {
free(h); // Clean up if second malloc fails!
return NULL;
}
// Copy the data
strcpy(h->name, name);
h->power_level = power;
return h;
}
void destroy_hero(Hero *h) {
if (h != NULL) {
free(h->name); // Free the name FIRST
free(h); // THEN free the struct
}
}
int main(void) {
Hero *hero = create_hero("Gandalf", 9001);
if (hero == NULL) {
return 1;
}
printf("%s has power level %d\n", hero->name, hero->power_level);
destroy_hero(hero);
return 0;
}The Order Matters!
When freeing a struct with pointer members:
- Free the members first
- Then free the struct
If you do it backwards, you lose access to the pointers inside. It’s like demolishing a parking garage before driving the cars out - now you can’t reach them!
// WRONG - memory leak!
free(h); // Struct is gone
free(h->name); // Can't access h->name anymore!
// RIGHT
free(h->name); // Free what's inside first
free(h); // Then free the containerStruct Memory Layout and Padding
Here’s something that surprises people: structs sometimes use more memory than you’d expect.
typedef struct {
char a; // 1 byte
int b; // 4 bytes
char c; // 1 byte
} Example;
printf("Size: %zu\n", sizeof(Example)); // Probably 12, not 6!Why 12 instead of 6?
The computer adds empty space called padding. It’s like how a shipping company puts foam peanuts in a box - the actual items are smaller than the box, but the padding makes handling easier.
Computers read memory fastest when data is “aligned” - when 4-byte numbers are at addresses divisible by 4. The padding makes this happen. In this example, a 1-byte char is followed by 3 bytes of padding, then a 4-byte int, then another 1-byte char followed by 3 more bytes of padding - totaling 12 bytes instead of the expected 6.
The dots are wasted space. Empty. Like filler issues in a comic book series.
Reducing Padding
Order your fields from largest to smallest:
// Wasteful order
typedef struct {
char a; // 1 byte + 3 padding
int b; // 4 bytes
char c; // 1 byte + 3 padding
} Wasteful; // Total: 12 bytes
// Better order
typedef struct {
int b; // 4 bytes
char a; // 1 byte
char c; // 1 byte + 2 padding
} Better; // Total: 8 bytesThis matters when you have millions of structs (like in a game with lots of enemies) or when saving data to files.
Resizing Dynamic Arrays
What if you need to grow an array? Use realloc:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int capacity = 2;
int count = 0;
int *numbers = malloc(capacity * sizeof(int));
// Keep adding numbers
for (int i = 0; i < 10; i++) {
// Need more space?
if (count >= capacity) {
capacity *= 2; // Double the size
int *temp = realloc(numbers, capacity * sizeof(int));
if (temp == NULL) {
printf("Out of memory!\n");
free(numbers);
return 1;
}
numbers = temp;
printf("Grew to capacity %d\n", capacity);
}
numbers[count] = i * 10;
count++;
}
// Print them
for (int i = 0; i < count; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
free(numbers);
return 0;
}realloc either:
- Extends your existing memory (if there’s room)
- Allocates new memory, copies your data, and frees the old memory
Either way, you get a pointer to memory with the new size.
Important: Always use a temporary variable with realloc. If it fails and returns NULL, you don’t want to lose your original pointer!
Complete Example: Dynamic Hero Roster
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
char *power;
int strength;
} Hero;
typedef struct {
Hero *heroes;
int count;
int capacity;
} Roster;
void roster_init(Roster *r) {
r->heroes = NULL;
r->count = 0;
r->capacity = 0;
}
int roster_add(Roster *r, const char *name, const char *power, int strength) {
// Need more space?
if (r->count >= r->capacity) {
int new_cap = (r->capacity == 0) ? 4 : r->capacity * 2;
Hero *temp = realloc(r->heroes, new_cap * sizeof(Hero));
if (temp == NULL) return 0;
r->heroes = temp;
r->capacity = new_cap;
}
// Create the hero
Hero *h = &r->heroes[r->count];
h->name = malloc(strlen(name) + 1);
h->power = malloc(strlen(power) + 1);
if (h->name == NULL || h->power == NULL) {
free(h->name);
free(h->power);
return 0;
}
strcpy(h->name, name);
strcpy(h->power, power);
h->strength = strength;
r->count++;
return 1;
}
void roster_print(Roster *r) {
printf("\n=== HERO ROSTER ===\n");
for (int i = 0; i < r->count; i++) {
printf("%s - %s (Strength: %d)\n",
r->heroes[i].name,
r->heroes[i].power,
r->heroes[i].strength);
}
printf("Total: %d heroes\n", r->count);
}
void roster_free(Roster *r) {
for (int i = 0; i < r->count; i++) {
free(r->heroes[i].name);
free(r->heroes[i].power);
}
free(r->heroes);
r->heroes = NULL;
r->count = 0;
r->capacity = 0;
}
int main(void) {
Roster team;
roster_init(&team);
roster_add(&team, "Ada Lovelace", "Programming", 95);
roster_add(&team, "Alan Turing", "Codebreaking", 98);
roster_add(&team, "Grace Hopper", "Compilers", 92);
roster_add(&team, "Dennis Ritchie", "C Language", 99);
roster_add(&team, "Linus Torvalds", "Linux", 94);
roster_print(&team);
roster_free(&team);
return 0;
}Try It Yourself
- Create a
Bookstruct with dynamictitleandauthorstrings. Write create and destroy functions. - Modify the hero roster to support removing heroes
- Add a function to find the strongest hero in the roster
- What happens if you forget to free a hero’s name before freeing the hero? (Memory leak!)
Common Mistakes
- Freeing struct members in wrong order (losing access to pointers)
- Forgetting to check malloc/realloc return values
- Not initializing pointer members to NULL
- Losing the original pointer when realloc fails
Next Up
In Part 15, we’ll learn about function pointers - storing functions in variables and passing them as arguments.
Enjoyed This?
If this helped something click, subscribe to my YouTube channel. More content like this, same approach - making things stick without insulting your intelligence. It’s free, it helps more people find this stuff, and it tells me what’s worth making more of.