student@ubuntu:~$
advanced-c Lesson 11 9 min read

Struct Pointers & Arrow Operator

Passing structs efficiently and accessing fields through pointers

Reading: Hanly & Koffman: §10.3 (pp. 584–587), §10.5 (pp. 596–606)

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.


Check Your Understanding
When should you use -> instead of . to access a struct field?
AWhen the struct is on the stack
BWhen the field is an array
CWhen you have a pointer to the struct
DWhen the struct uses typedef
Answer: C. The arrow operator (->) 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.

Next: File I/O in C