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:

C
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

C
#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;
}
Output
score = 42
*p    = 42
p points to score? yes
score is now 100
SyntaxMeaning
int *p;Declare p as a pointer to an int
&xThe address of x
*pThe 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

TXT
   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):

C
#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;
}
Output
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:

C
#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;
}
Output
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:

C
#include <stdio.h>

int main(void) {
    int *p;
    *p = 5;
    printf("%d\n", *p);
    return 0;
}
Compiler warnings
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):

C
#include <stdio.h>

int main(void) {
    double price = 9.99;
    int *p = &price;
    printf("%d\n", *p);
    return 0;
}
Compiler warnings
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:

C
#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;
}
Output
watermelon

Try it yourself

Write a function that takes pointers to three integers and sorts them so that the first is the smallest.

Try it yourself · main.c
Edit the code, copy it into main.c, then compile and run it with cc -std=c17 -Wall main.c -o main && ./main. How to set up a C compiler
Output
2 5 9