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.
- 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.
Nested structs and arrays of structs
Compose complex types from simpler structs — nested struct members, arrays of structs, and how to work with them efficiently.
typedef idioms
Simplify C type names with typedef — the typedef struct pattern, function pointer typedefs, and when typedef helps versus hinders readability.