Structures, Pointers & Functions
Working with recordsReal programs pass structures to functions and reach them through pointers. In the lab below you meet the arrow operator, see why a function needs a pointer to change your struct, give a type a short name with typedef, and walk three structs that point to each other — your first linked list.
- p->xis (*p).x
- typedefgives a type a short name
- struct Node *nextlinks one record to the next
Ready
Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode
The ideas behind the lab
Exactly what you watched happen, in plain steps.
Pointers to structs and the arrow
A pointer can hold the address of a whole struct. To reach a member through it you follow the pointer and then pick the member: (*p).x — which C lets you write as p->x. Functions usually receive structs this way: passing a pointer copies only 8 bytes and lets the function change the caller's struct. typedef gives a struct type a short name, and a struct may contain a pointer to its own type, which links records together.
typedef struct Point { int x, y; } Point; // Point = struct Point
Point a = {1, 2};
Point *p = &a;
p->x = 5; // same as (*p).x = 5
void move(Point *p, int dx) { p->x += dx; }
move(&a, 3); // a.x is now 8
typedef struct Node { int value; struct Node *next; } Node;
| Expression | Meaning | Use it when |
|---|---|---|
| a.x | member x of the struct a | you have the struct itself |
| p->x | member x of the struct p points to | you have a pointer |
| (*p).x | the same as p->x | rarely — the arrow is shorter |
| f(a) | passes a copy of the whole struct | small structs that must not change |
| f(&a) | passes the address (8 bytes) | the function changes it, or it is big |
| const Point *p | a pointer the function may only read through | large read-only arguments |
The arrow operator
- p->member is short for (*p).member.
- The brackets in (*p).x matter: *p.x would mean *(p.x).
- Use . with a struct variable and -> with a pointer to a struct.
- Chains work too: list->next->value.
- A pointer to a struct must point to a real struct (or be NULL) before ->.
Structs and functions
- A struct argument is copied completely, like an int.
- Changes to that copy are lost when the function returns.
- Pass &s and take a pointer parameter to change the caller's struct.
- A function can also return a struct: Point add(Point a, Point b).
- For big structs, pass const T * even when only reading — it avoids the copy.
typedef and self-reference
- typedef existing_type NewName; creates an alias, e.g. typedef unsigned int uint;
- typedef struct { … } Point; lets you write Point instead of struct Point.
- A struct cannot contain itself, but it can contain a pointer to its own type.
- Inside the struct use the tag: struct Node *next; (the typedef name is not known yet).
- Nodes linked by next pointers form a linked list; NULL marks the end.
A linked list built with malloc
Every line, explained.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int value;
struct Node *next;
A pointer to the next node.
} Node;
Node *push(Node *head, int value)
Adds a node in front.
{
Node *n = malloc(sizeof *n);
A new node on the heap.
if (n == NULL) return head;
n->value = value;
n->next = head;
It points to the old first node …
return n;
… and becomes the new head.
}
void printList(const Node *p)
{
for (; p != NULL; p = p->next)
Follow the links until NULL.
printf("%d -> ", p->value);
printf("NULL\n");
}
int main(void)
{
Node *head = NULL;
An empty list.
head = push(head, 3);
head = push(head, 2);
head = push(head, 1);
printList(head);
Prints 1 -> 2 -> 3 -> NULL
while (head != NULL) {
Free every node.
Node *next = head->next;
Save the link BEFORE freeing.
free(head);
head = next;
}
return 0;
}
Spot the bug
Mistakes almost every beginner makes — and how to fix them.
Using . with a pointer
Point *p = &a; p.x = 5; // error
Point *p = &a; p->x = 5;
p is an address, not a struct. Use -> (or (*p).x).
*p.x instead of (*p).x
*p.x = 5;
(*p).x = 5; // or p->x = 5;
. binds tighter than *, so *p.x means *(p.x).
Passing a struct by value to change it
void reset(Point p) { p.x = 0; }
reset(a); // a unchanged
void reset(Point *p) { p->x = 0; }
reset(&a);
The function changes only its copy. Pass a pointer.
Using the typedef name inside its own struct
typedef struct Node {
int v;
Node *next; // error: Node not yet known
} Node;
typedef struct Node {
int v;
struct Node *next;
} Node;
The name Node only exists after the typedef ends; inside, use struct Node.
Freeing a node before reading its next
free(head); head = head->next; // use after free
Node *next = head->next; free(head); head = next;
After free the node may already be overwritten.
Check your understanding
Answer each question — you will see the explanation straight away.
Point a = {1, 2}; Point *p = &a; p->y = 7; — what is a.y?
p->y is a.y.
Which expression is the same as p->x?
Follow the pointer, then select the member.
void f(Point p) { p.x = 0; } — Point a = {5, 5}; f(a); What is a.x?
f changed a copy.
What does typedef struct { int x, y; } Point; let you write?
Point is now a name for the struct type.
In a linked list, what does the last node's next usually hold?
NULL marks the end of the list.
Structures, Pointers & Functions quiz
Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.