C in 100 Seconds: Pointers | Episode 17
Video: C in 100 Seconds: Pointers — Two Ways to Access the Same Memory | Episode 17 by Taught by Celeste AI - AI Coding Coach
C Pointers: Two Ways to Access the Same Memory
A pointer holds a memory address.
int *ptr = &x;makesptrpoint atx.*ptrreads or writes through the pointer. Two names, one memory location.
Pointers are C's most powerful and most dangerous feature. They're how the language exposes memory directly. Once you internalize them, the rest of C clicks.
The basic shape
#include <stdio.h>
int main() {
int x = 42;
int *ptr = &x;
printf("Value of x: %d\n", x); // 42
printf("Address of x: %p\n", &x); // 0x7ffe...
printf("ptr holds: %p\n", ptr); // same as &x
printf("Value at ptr: %d\n", *ptr); // 42
*ptr = 99;
printf("x is now: %d\n", x); // 99
return 0;
}
Three operators
int x = 42;
int *ptr; // declare: ptr is a pointer to int
ptr = &x; // assign: ptr now holds the address of x
*ptr = 99; // dereference: write 99 to the location ptr points at
*in declaration —int *ptrdeclaresptras "pointer to int." (Some style guides writeint* ptr; both compile.)&x— address-of operator. Returns the memory address ofx.*ptr— dereference operator. Returns or writes the value at the address.
The same * symbol means different things in different places:
int *ptr— declaration: pointer type.*ptr— expression: dereference the pointer.
Memory model
Variable Address Value
x 0x1000 42
ptr 0x2000 0x1000
x lives at some address (let's say 0x1000) and holds the value 42. ptr lives at some other address (0x2000) and holds the value 0x1000 — the address of x.
Reading *ptr says "go to the address in ptr (0x1000), read what's there (42)."
Writing *ptr = 99 says "go to that address, write 99 there." x and *ptr are now both 99.
Why pointers?
Three main uses:
- Modify the caller's variable. Pass
&xto a function; the function writes through the pointer (episode 19). - Avoid copying large data. Pass a pointer to a struct, not the whole struct.
- Dynamic memory.
mallocreturns a pointer to heap memory (episode 20).
NULL
int *ptr = NULL; // ptr points to "no address"
if (ptr != NULL) {
*ptr = 5;
}
NULL is the special "points to nothing" value (defined in <stddef.h> and most headers). Always check before dereferencing — *NULL segfaults.
In modern C, nullptr (C23) is the typed null. Pre-C23, NULL is a macro that expands to ((void*)0) or 0 depending on the implementation.
Pointer types matter
int x = 42;
int *p = &x; // OK: types match
double *q = &x; // ERROR: type mismatch
double *q2 = (double*)&x; // compiles, but wrong (treats int bytes as double)
A pointer type tells the compiler how to interpret the bytes at the address. int * means "4 bytes here, treat as int." double * means "8 bytes here, treat as double."
Casting between pointer types compiles but reinterprets bytes — a footgun.
void*
void *vp = &x; // any pointer type
int *p = (int*)vp; // cast back to specific type
void * is "pointer of unknown type." Used for generic APIs (malloc returns void*; qsort takes void *). Can't be dereferenced directly — cast to a specific type first.
Pointer to pointer
int x = 42;
int *p = &x;
int **pp = &p; // pointer to pointer
**pp = 99; // double-dereference: x is now 99
int ** is "pointer to pointer to int." Episode 29 covers this.
Common mistakes
Uninitialized pointer. int *p; *p = 5; — p holds garbage; dereferencing crashes (or worse, silently corrupts memory). Always initialize: int *p = NULL; or int *p = &x;.
Dereferencing NULL. int *p = NULL; *p = 5; segfaults.
Returning pointer to local. int* foo() { int x = 5; return &x; } — x is destroyed when foo returns. The pointer dangles. Catastrophic.
Forgetting the &. scanf("%d", x) — passes x's value as the address. Crash.
Forgetting the * to dereference. int *p = &x; printf("%d", p); prints the address, not the value.
Pointer arithmetic with wrong type. int *p = arr; p + 1 advances by 4 bytes (one int). Don't manually compute byte offsets — let the type system handle it.
Pointers vs references (briefly)
C++ has int &ref = x; (references) which look like normal variables but are aliases. C doesn't — pointers are the only way.
Pros of pointers: explicit; you know when something is "by reference." Cons: more error-prone (NULL, dangling).
Why this matters
Pointers underpin:
- Function arguments that modify caller state (
scanf,swap). - Dynamic data structures (linked lists, trees).
- Heap memory (
malloc). - Generic data structures (qsort, hash tables).
- Strings (which are
char *).
If you don't get pointers, you can't read most non-trivial C code. If you do, the language's design suddenly makes sense.
What's next
Episode 18: pointer arithmetic. Walking an array with a pointer. *(ptr + i) is the same as ptr[i].
Recap
int *p = &x declares a pointer; &x is the address-of operator. *p dereferences (read/write through the pointer). NULL means "points to nothing"; never dereference NULL. Pointer types tell the compiler how to interpret bytes — mismatched types cast successfully but break semantically. void * for generic pointers; cast before use. Pointers underpin function arguments-by-reference, dynamic memory, and dynamic data structures.
Next episode: pointer arithmetic.