Code of the Day
IntermediateStructs and enums

Pointers to structs

Work with pointers to structs in C — the arrow operator, dynamic struct allocation, self-referential structs for linked lists, and function parameters.

CIntermediate10 min read
By the end of this lesson you will be able to:
  • Access struct members through a pointer with the arrow operator
  • Allocate a struct on the heap with malloc
  • Declare a self-referential struct for singly linked list nodes
  • Write a function that allocates and returns a pointer to a struct

When you work with structs beyond small local variables — especially when building linked data structures or passing large structs between functions — you use pointers to structs. This is the pattern that makes complex data structures in C possible.

The arrow operator

(*ptr).member is so common that C provides shorthand: ptr->member. Both are identical:

struct Point { double x; double y; };

struct Point p = {3.0, 4.0};
struct Point *ptr = &p;

/* These four are identical: */
printf("x = %.1f\n", p.x);
printf("x = %.1f\n", (*ptr).x);
printf("x = %.1f\n", ptr->x); /* preferred */

ptr->x = 10.0; /* modifies p.x */

Use -> with a pointer; use . with a value. If you find yourself writing (*ptr)., replace it with ptr->.

Allocating a struct on the heap

#include <stdio.h>
#include <stdlib.h>

struct Rectangle {
    double width;
    double height;
};

struct Rectangle *create_rect(double w, double h) {
    struct Rectangle *r = malloc(sizeof(struct Rectangle));
    if (r == NULL) { return NULL; }
    r->width = w;
    r->height = h;
    return r;
}

void free_rect(struct Rectangle *r) {
    free(r);
}

int main(void) {
    struct Rectangle *r = create_rect(5.0, 3.0);
    if (r == NULL) { return 1; }

    printf("Area: %.1f\n", r->width * r->height);

    free_rect(r);
    return 0;
}

The create_rect function allocates the struct on the heap and returns a pointer to it — allowing the struct to outlive the function call. The caller is responsible for calling free_rect.

Self-referential structs

A struct can contain a pointer to another instance of itself. This is how linked list nodes work:

struct Node {
    int data;
    struct Node *next; /* pointer to the next node */
};

You cannot say struct Node { int data; struct Node next; } — that would be infinite size (a node contains a node which contains a node...). But a pointer to the next node works because a pointer has a fixed size (8 bytes on 64-bit) regardless of what it points to.

Building a simple linked list:

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *new_node(int value) {
    struct Node *n = malloc(sizeof(struct Node));
    if (!n) { return NULL; }
    n->data = value;
    n->next = NULL;
    return n;
}

void print_list(struct Node *head) {
    for (struct Node *cur = head; cur != NULL; cur = cur->next) {
        printf("%d -> ", cur->data);
    }
    printf("NULL\n");
}

void free_list(struct Node *head) {
    while (head != NULL) {
        struct Node *tmp = head->next;
        free(head);
        head = tmp;
    }
}

int main(void) {
    struct Node *head = new_node(1);
    head->next = new_node(2);
    head->next->next = new_node(3);

    print_list(head); /* 1 -> 2 -> 3 -> NULL */
    free_list(head);
    return 0;
}

Note: in free_list, we save head->next before freeing head, because accessing freed memory is undefined behaviour.

Modifying a struct through a function

To write a function that modifies the caller's struct, pass a pointer to the struct:

void double_dimensions(struct Rectangle *r) {
    r->width  *= 2.0;
    r->height *= 2.0;
}

Alternatively, return a new struct by value — this is clean for small structs:

struct Rectangle doubled(struct Rectangle r) {
    return (struct Rectangle){ .width = r.width * 2, .height = r.height * 2 };
}

For large structs, passing by pointer is faster (no copy). For small structs (a few fields), returning by value is often cleaner and the compiler may optimise away the copy.

Where to go next

Next: typedef idioms — simplifying struct type names with typedef and the typedef struct pattern used throughout the C standard library and major codebases.

Finished reading? Mark it complete to track your progress.

On this page