C

Pointers

A pointer is a variable whose value is a memory address — not a number you compute with, but a location you can follow to reach the actual value stored there. Pointers are the single most feared topic in beginner C, almost always because of notation, not concept — the underlying idea is simple: "the address of a thing" and "the thing at an address" are two different, related ideas, and C gives you an operator for each.

Quick reference
What it is
A variable that stores a memory address instead of an ordinary value.
Mental model
A sticky note with someone else's house address written on it — the note itself isn't the house, but it tells you exactly where to go to find it.
Key numbers
A pointer's own size is fixed by the platform (commonly 4 or 8 bytes) regardless of what type it points to — a pointer to a char and a pointer to a double take the same space.
Reach for it when
A function needs to modify a caller's variable, you need to refer to one piece of data from multiple places without copying it, or you're working with arrays/strings (which are built directly on this idea).

The address-of operator: &

Every variable lives somewhere in memory. The & operator, placed before a variable's name, gives you that variable's address instead of its value:

int age = 25;
printf("%d\n", age);    // 25 — the VALUE
printf("%p\n", (void*)&age);   // e.g. 0x7ffee2a1 — the ADDRESS

Declaring and dereferencing a pointer

A pointer variable is declared with a * before its name, and its type says what kind of value it points to:

int age = 25;
int *p = &age;   // p is a pointer to an int, holding age's address

printf("%d\n", *p);   // 25 — dereferencing: "the value AT this address"
*p = 30;               // changes age itself, through the pointer
printf("%d\n", age);   // 30
The variable age holds 25 at address 0x1000; the pointer p is a separate variable at address 0x2000 whose own value is 0x1000, pointing back at age 25 age — address 0x1000 0x1000 p — address 0x2000 p's own VALUE is age's ADDRESS — that's the entire mechanism
p is itself an ordinary variable, with its own address (0x2000) — the only thing special about it is that the value stored inside it is interpreted as an address pointing somewhere else.

Read int *p as "*p is an int" — the declaration describes what you get after dereferencing, which is why the pointer's declared type has to match what it points to.

Passing by reference

Recall from Functions that C passes arguments by value — a function only ever gets a copy. Passing a pointer to a variable lets a function reach back and modify the original, because the address it receives points at the real thing:

void increment(int *x) {
    *x = *x + 1;    // modifies whatever x points at
}

int main(void) {
    int n = 5;
    increment(&n);       // pass n's ADDRESS, not n itself
    printf("%d\n", n);   // 6 — n was actually changed
    return 0;
}

This is the single most common reason to reach for a pointer as a beginner: it's how a function returns more than one value, or modifies something the caller owns.

NULL

NULL is a special pointer value meaning "points at nothing valid" — the conventional way to represent "no address yet" or "this pointer isn't set up." Dereferencing a NULL pointer (trying to read or write "the value at nothing") is undefined behavior, and on most desktop operating systems crashes immediately (a "segmentation fault") — which is actually the friendly outcome compared to what can happen on hardware with no memory protection at all.

int *p = NULL;
if (p != NULL) {
    printf("%d\n", *p);   // only dereference after checking
}

Pointer arithmetic, briefly

Adding to a pointer moves it forward by that many elements of its pointed-to type, not that many bytes — p + 1 on an int* moves 4 bytes ahead (one whole int), not 1 byte:

int scores[3] = {10, 20, 30};
int *p = scores;        // points at scores[0] — recall array decay from Arrays
printf("%d\n", *p);       // 10
printf("%d\n", *(p + 1)); // 20 — one int forward, not one byte forward

This is exactly the mechanism Arrays relies on for indexing — this section is intentionally brief; the full depth (arithmetic on other pointer types, subtracting two pointers, common bugs) is covered properly in Embedded C's Pointers, worth reading once you're comfortable here.

Real-world applications

Gotchas

Field notes