Pointers: Addresses, Dereference, & and *
What This Concept Is
A pointer is a typed variable whose value is the address of another object. "Typed" is the important word: int * and char * both hold addresses, but the type tells the compiler how many bytes to read on a dereference and how to step through memory.
Two operators turn values into pointers and back:
&xproduces the address ofx(a pointer tox's type)*pyields the objectppoints to (the "pointee"), readingsizeof(*p)bytes
A pointer declaration follows the pattern "when you dereference this, you get the stated type":
int x = 42;
int *p = &x; /* *p is an int */
char *c = (char *)p; /* re-typed view; *c is a char */
Why It Matters Here
Almost everything in C that "passes something around" is either a value copy or a pointer. Function arguments in C are always passed by value; to let a function modify a caller's variable, you pass a pointer. To avoid copying a large object, you pass a pointer. Arrays decay to pointers in most contexts. Strings are pointers to characters. There is no getting around it.
Concrete Example
int x = 42;
int *p = &x;
Suppose x lives at address 0x1000. Then on a 64-bit machine with 4-byte int and 8-byte pointer:
Address Content Size
0x1000 0x0000002A (= 42) 4 bytes (this is x)
...
0x2000 0x0000000000001000 8 bytes (this is p, pointing to x)
Reading *p means "go to the address stored in p (which is 0x1000), read sizeof(int) = 4 bytes, interpret as int." That yields 42.
Writing *p = 100; means "write the int 100 into the 4 bytes at 0x1000." After this, x is 100.
Common Confusion / Misconception
"int *p, q; declares two pointers." No. It declares one pointer p and one plain int q. The * binds to the name, not to the type. Prefer int *p; int *q; on separate lines.
"&x is just some number I can do math on." It is a typed pointer. Casting it to int and back is implementation-defined at best. Use uintptr_t if you must store an address as an integer.
"NULL is zero." It represents a pointer that does not point at a valid object. Dereferencing it is undefined behavior, not zero-returning. On most OSes it reliably crashes, which is the best outcome.
How To Use It
For every pointer you write:
- State its type. "This is a pointer to one
int" or "pointer to the first of manyints." - State what it points to, and whether that object outlives the pointer.
- Never dereference a pointer you have not initialized.
- Check for
NULLafter allocation and before dereferencing pointers that can legitimately be null.
Check Yourself
- What is the difference between
int *pandint p? - If
phas typeint *and*p = 5;, what must be true for that write to be safe? - What does
&*pmean whenpis a valid pointer?
Mini Drill or Application
Predict each printed line, then run:
#include <stdio.h>
int main(void) {
int x = 10;
int *p = &x;
printf("x = %d, *p = %d\n", x, *p);
*p = 99;
printf("x = %d, *p = %d\n", x, *p);
int y = 7;
p = &y; /* retarget p */
printf("x = %d, *p = %d\n", x, *p);
return 0;
}
Build: gcc -Wall -Wextra -o basic_ptr basic_ptr.c. Draw a memory diagram for the state after each statement before running. The diagram is the point, not the output.