Struct Pointers & Arrow Operator
Passing structs efficiently and accessing fields through pointers
Quick check before you start: Can you define a struct and access its fields with the dot operator? If yes, read on. If not, review Lesson 8a.
Practice this topic: Struct Pointers skill drill
After this lesson, you will be able to:
- Use the
->operator to access fields through a struct pointer - Allocate structs dynamically on the heap
- Handle nested dynamic allocations
- Free nested structs in the correct order
The Arrow Operator
When you have a pointer to a struct, you cannot use the dot operator directly. You need ->:
Student *ptr = &s1;
printf("GPA: %.2f\n", ptr->gpa);
The arrow operator dereferences the pointer and accesses the field in one step. It is exactly equivalent to (*ptr).gpa, but cleaner.
Rule of thumb: variable uses ., pointer uses ->.
Dynamic Struct Allocation
Structs on the stack disappear when the function returns. To keep a struct alive, allocate it on the heap:
Student *s = calloc(1, sizeof(Student));
if (s == NULL) {
fprintf(stderr, "Error: calloc failed\n");
exit(EXIT_FAILURE);
}
strcpy(s->name, "Alice");
s->id = 12345;
s->gpa = 3.7;
Now s points to heap memory that persists until you free it.
Nested Dynamic Allocation
Sometimes a struct field itself needs heap memory. For example, a student with a dynamically sized name:
typedef struct {
char *name; // pointer, not array
int id;
double gpa;
} Student;
Student *create_student(const char *name, int id, double gpa) {
Student *s = calloc(1, sizeof(Student));
if (s == NULL) { return NULL; }
s->name = calloc(strlen(name) + 1, sizeof(char));
if (s->name == NULL) {
free(s);
return NULL;
}
strcpy(s->name, name);
s->id = id;
s->gpa = gpa;
return s;
}
Two allocations: one for the struct, one for the name string inside it.
Freeing Order Matters
When you free a struct with nested allocations, free the inner allocations first:
void free_student(Student *s) {
if (s == NULL) { return; }
free(s->name); // free inner first
s->name = NULL;
free(s); // then free the struct
}
If you free the struct first, you lose your only pointer to s->name — that is a memory leak. Think of it like unpacking a box: take out the contents before you throw away the box.
Passing Structs to Functions
Pass struct pointers to avoid copying the entire struct:
void print_student(const Student *s) {
printf("%s (ID: %d, GPA: %.2f)\n", s->name, s->id, s->gpa);
}
The const keyword tells the compiler (and the reader) that this function will not modify the struct. Use it whenever a function only reads the data.
-> instead of . to access a struct field?->) is used when you have a pointer to a struct. It dereferences the pointer and accesses the field in one step. If you have the struct itself (not a pointer), use the dot operator.
What Comes Next
You can create and manage structs in memory. Next, you will learn to read and write files in C — the bridge between your program and persistent data on disk.