Pointers in C
A pointer is just an address. Here's the mental model that made them click, with a live memory diagram you can poke at.
Introduction
I am working through Harvard CS50 right now, and I noticed more people kept on dropping throught the course. Almost everyone was gone after we learned about pointers. David J. MalanCS50 instructor does a great job at teaching, and when he started drawing the memory out by hand. It helped solidify the concept.
Memory is a big array
Every variable in your program lives somewhere in memory, and every spot in memory has a number: its address. When you write int x = 42;, the compiler picks a spot, say address 0x1000, and puts 42 there. You use the name x, but the machine uses the address.
That's the whole secret. A pointera variable whose value is the memory address of another variable is just a variable that holds one of those addresses instead of a regular value.
int x = 42; // x lives at some address, holds 42
int *p = &x; // p holds the address of xTwo operators do all the work:
&xmeans "the address ofx". It gives you a pointer.*pmeans "the thing at the address inp". It follows the pointer, which is called dereferencingreading or writing the value stored at the address a pointer holds.
Try it
Below are three variables in memory: two ints and a pointer. Click the buttons to run one line of C at a time and watch which box changes. The important thing to notice: *p = 0; changes a different row depending on where p is pointing, and p = &y; changes p without touching x or y at all.
The highlighted row is whatever p currently points at. Writing through *p changes that row, not p itself.
Reading pointer code
The syntax trips people up because * means two different things depending on where it is.
int *p; // declaration: p is a pointer to an int
*p = 5; // expression: write 5 to wherever p points
int y = *p; // expression: read wherever p pointsI read int *p as "*p is an int", which is actually how the C designers intended it. The star binds to the name, not the type, which is why int *a, b; makes a a pointer and b a plain int.
Why pointers exist
Functions can change your variables
C passes everything by value, so a function gets a copy. If you want a function to modify the caller's variable, you pass its address.
void add_one(int *n) {
*n = *n + 1;
}
int main(void) {
int count = 10;
add_one(&count); // count is now 11
return 0;
}Without pointers, add_one would increment its own copy and count would stay 10.
Arrays are pointers in disguise
An array name is basically the address of its first element. arr[2] and *(arr + 2) are the same thing, which is why pointer arithmetic moves by the size of the type: adding 1 to an int * moves 4 bytes, not 1.
int arr[4] = {10, 20, 30, 40};
int *p = arr; // same as &arr[0]
printf("%d\n", *p); // 10
printf("%d\n", *(p + 2)); // 30Strings
A C string is a pointer to the first char, and the string ends wherever the first '\0' byte is. Every string function in the standard library is a loop that walks a pointer until it hits that zero.
The bugs you'll write
- Dereferencing
NULL.int *p = NULL; *p = 1;crashes with a segmentation fault. Always check pointers that might be empty. - Dangling pointers. Returning the address of a local variable. The variable dies when the function returns, but the pointer still holds its old address and now points at garbage.
- Uninitialized pointers.
int *p; *p = 5;writes to a random address. Sometimes it works, which is worse than crashing.
int *bad(void) {
int local = 7;
return &local; // local is gone after this line
}Conclusion
A pointer is an address. & gets an address, * follows one. Everything else, from pass-by-reference to arrays to strings to linked lists, is built on that. If pointers aren't clicking, stop reading and draw the boxes. It worked for me.