Pointers
A pointer is a variable that holds a memory address — the location of another value. Pointers are what make C powerful: they let functions change their caller's variables, give access to arrays and dynamically allocated memory, and build linked data structures. They're also where most C bugs live, so this lesson goes slowly.
Addresses
Every variable lives somewhere in memory, and the & (address-of) operator gives its address. %p prints an address; the exact numbers differ on every run and every machine, so don't be surprised by your values:
int score = 42; printf("score is %d and lives at %p\n", score, (void *) &score); // e.g. "score is 42 and lives at 0x16d8a2f4c"
Declaring and using a pointer
#include <stdio.h> int main(void) { int score = 42; int *p = &score; // p is a "pointer to int", holding score's address printf("score = %d\n", score); printf("*p = %d\n", *p); // * follows the pointer: the value at that address printf("p points to score? %s\n", p == &score ? "yes" : "no"); *p = 100; // write through the pointer printf("score is now %d\n", score); return 0; }
score = 42 *p = 42 p points to score? yes score is now 100
| Syntax | Meaning |
|---|---|
int *p; | Declare p as a pointer to an int |
&x | The address of x |
*p | The value p points to (dereferencing) — can be read or assigned |
The * means different things in a declaration ("this is a pointer") and in an expression ("follow the pointer"). Read int *p as "*p is an int".
A picture
variable address contents ┌────────┐ │ score │ 0x…f4c → 100 ├────────┤ │ p │ 0x…f40 → 0x…f4c (the address of score) └────────┘
A pointer is just a number that happens to be an address. Its type (int *) tells the compiler what kind of value lives there, so that *p reads the right number of bytes.
Pointers change the caller's variables
This is the classic reason to use pointers (lesson 22 previewed it):
#include <stdio.h> void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } int main(void) { int x = 3, y = 8; swap(&x, &y); printf("x = %d, y = %d\n", x, y); return 0; }
x = 8, y = 3
NULL
NULL is a special pointer value meaning "points to nothing". Use it to initialise pointers you don't have a target for yet, and to signal "no result". Dereferencing a null pointer is undefined behaviour — usually an immediate crash — so check first:
#include <stdio.h> #include <string.h> int main(void) { const char *found = strchr("hello", 'z'); if (found == NULL) { printf("no 'z' found\n"); } else { printf("found at %s\n", found); } return 0; }
no 'z' found
if (p) and if (!p) are common shorthands for testing against NULL. C23 adds the keyword nullptr for the same purpose (lesson 55).
Uninitialised pointers
A pointer declared without a value holds a random address. Writing through it scribbles on unknown memory:
#include <stdio.h> int main(void) { int *p; *p = 5; printf("%d\n", *p); return 0; }
main.c:5:6: warning: variable 'p' is uninitialized when used here [-Wuninitialized]
Always initialise a pointer — to a real address or to NULL.
Pointer types must match
An int * should point to an int, a double * to a double. Mixing them is a warning in Clang and an error in GCC 14 and later (and reading memory as the wrong type is generally undefined):
#include <stdio.h> int main(void) { double price = 9.99; int *p = &price; printf("%d\n", *p); return 0; }
main.c:5:10: warning: incompatible pointer types initializing 'int *' with an expression of type 'double *' [-Wincompatible-pointer-types]
The exception is void *, a generic pointer that can hold any object's address but can't be dereferenced until converted back — malloc returns one (lesson 36).
Pointers to pointers
A pointer is itself a variable with an address, so you can point to it. You'll meet char ** with command-line arguments (lesson 49) and when a function needs to change a pointer the caller owns:
#include <stdio.h> void pick_longer(const char **result, const char *a, const char *b, size_t len_a, size_t len_b) { *result = len_a >= len_b ? a : b; } int main(void) { const char *winner = NULL; pick_longer(&winner, "pear", "watermelon", 4, 10); printf("%s\n", winner); return 0; }
watermelon
Try it yourself
Write a function that takes pointers to three integers and sorts them so that the first is the smallest.
2 5 9