Step 58. Pointers 1 — The Day You Hold Addresses in Your Hand
Level 1 — Programming and the Computer’s Inner Workings | Difficulty ★★★★☆ | Estimated time: 4 hours
Prerequisites: Steps 56–57 complete. You can write variables, control flow, and functions in C.
- What you need: a Linux terminal, gcc, paper and a pen (there will be drawing to do).
- Caution: today’s exercises are experiments inside your own computer, so they’re safe. That said, you will see a program die spitting out "Segmentation fault" — that’s not a malfunction, it’s the curriculum.
In studying C, there is a gateway known as "once you cross this, C becomes visible." It’s the pointer. Rumors say it’s hard, but a pointer is not a difficult concept — it’s an unfamiliar one. It’s confusing at first. That’s normal. Everyone who learns this part passes through the same awkwardness.
Here’s the conclusion up front. Every variable lives at some address in memory. A pointer is a variable like "a piece of paper with that address written on it." Follow the address written on the paper, and you can read — or change — the value living there. That’s all of it. And this "all of it" is the heart of C; the memory attack techniques you’ll learn later are, in the end, "techniques for manipulating addresses." Today is the day you learn the alphabet of those techniques.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Confirm with
%poutput that every variable lives at an address in memory - Use
&(get the address) and*(follow the address) distinctly - Read and change another variable’s value through a pointer variable
- Explain with pointers why scanf demanded
& - Explain pointers by drawing boxes and arrows
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language/environment | C language, Linux terminal, gcc -Wall (verified: Ubuntu 13.3.0, 64-bit) |
| Today’s syntax | &variable (address), int *p (pointer declaration), *p (dereference), %p (print address), sizeof |
| Concepts needed | Memory addresses, pointer variables, dereferencing, addresses change on every run |
2-1. Memory Is a Land of Street Numbers
Your computer’s memory (RAM) is a long row of lockers. Each slot has a number (an address), and when you create a variable, you’re assigned one of those slots. int x = 10; means "put the value 10 into some slot and stick the name tag x on it." So far we’ve moved around using only the name tag (x), but every slot has a number.
2-2. & — Tell Me the Address
Put & in front of a variable and you get that variable’s address. &x is "the slot where x lives." Now you can see why Step 56’s scanf took an & — for scanf to put a value in, it needs to know the box’s slot number.
2-3. * — Two Faces
The asterisk is used with two faces in pointers.
- At declaration:
int *p;is a mark saying "p is a variable that holds the address where an int lives." - At use:
*pis "follow the address written in p to the value sitting there."
Same asterisk, different meaning depending on position. Most beginner confusion comes from here, so today we put extra effort into this distinction.
2-4. Dereferencing — Going There
The act of "following an address to see the value," like *p, is called dereferencing. It means going back along the reference (the address) to reach the value. The term is hard; the job is simple — going to the slot number written on the paper.
3. Follow Along
Every address that appears in today’s exercises changes on every run. If the long numbers on your screen differ from the book’s, that’s normal. Don’t look at the numbers themselves — look at the relationships: "same/different/jumps by how much."
3-1. Seeing Addresses — Printing a Variable’s Slot
Input (addr.c)
#include <stdio.h>
int main(void) {
int x = 10;
printf("x value: %dn", x);
printf("x address: %pn", &x);
return 0;
}
Compile and run (run it three times in a row)
gcc -Wall addr.c -o addr
./addr
(Run 1) x value: 10
x address: 0x7ffe31d17e14
(Run 2) x value: 10
x address: 0x7ffdf2bcfd94
(Run 3) x value: 10
x address: 0x7ffde9b6e814
(Verified 2026-09-09. Addresses differ on every run.)
How to read it: %p is the format specifier that prints an address. The long hexadecimal number starting with 0x is x’s slot (Step 50’s hexadecimal notation). Three runs produced three different slots. Addresses changing every time is due to an OS security feature (ASLR, address space layout randomization) — the full story arrives in Step 60.
Why: hearing "variables live at slots" is different from seeing the slot with your own eyes. This output is the baseline for every experiment today.
3-2. Declaring a Pointer and Storing an Address
Input (ptr1.c)
#include <stdio.h>
int main(void) {
int x = 10;
int *p = &x;
printf("x address: %pn", &x);
printf("Value stored in p: %pn", p);
return 0;
}
Compile and run
gcc -Wall ptr1.c -o ptr1
./ptr1
x address: 0x7ffdf6e7500c
Value stored in p: 0x7ffdf6e7500c
(Verified 2026-09-09. Addresses differ on every run.)
How to read it: int *p = &x; means "make a box p that holds the address of an integer, and put x’s slot into it." The two outputs match — p contains x’s slot. You’ve written the slot on a piece of paper. The asterisk in the declaration (int *p) is only a mark saying "this is an address box," not an instruction to go anywhere.
3-3. Dereferencing — Following the Address
Input (ptr2.c)
#include <stdio.h>
int main(void) {
int x = 10;
int *p = &x;
printf("Value where p points: %dn", *p);
return 0;
}
Compile and run
gcc -Wall ptr2.c -o ptr2
./ptr2
Value where p points: 10
(Verified 2026-09-09.)
How to read it: *p in a use position is "go to the slot written in p and get the value there." Without calling x directly, we reached 10 by way of its slot. That one line is dereferencing.
Predict: what happens with
printf("%dn", *&x);? & and * are stuck together in a row — you get the address and then follow that address right back. Predict, then run it. (Verified: it printed10. You went to the slot and came back, so you’re right where you started.)
3-4. Changing a Value Through a Pointer
Input (ptr3.c)
#include <stdio.h>
int main(void) {
int x = 10;
int *p = &x;
printf("x before: %dn", x);
*p = 99;
printf("x after: %dn", x);
return 0;
}
Compile and run
gcc -Wall ptr3.c -o ptr3
./ptr3
x before: 10
x after: 99
(Verified 2026-09-09.)
How to read it: *p = 99; means "go to the slot written in p and put 99 there." We never touched x directly, yet x changed. Whoever knows the slot can act like the owner of that slot. You’ll meet this sentence again when you learn memory attacks.
Why: the power of pointers is "reading and writing someone else’s slot remotely." Reading (3-3) and writing (3-4) are today’s two weapons.
3-5. Solving the scanf Mystery
Now Step 56’s & resolves itself.
Input (scanfwhy.c)
#include <stdio.h>
int main(void) {
int age;
printf("Age: ");
scanf("%d", &age);
printf("The address you gave: %pn", &age);
printf("The value stored at that address: %dn", age);
return 0;
}
Compile and run
gcc -Wall scanfwhy.c -o scanfwhy
./scanfwhy
Age: 25
The address you gave: 0x7ffc882102c4
The value stored at that address: 25
(Verified 2026-09-09. The 25 is keyboard input, and addresses differ on every run.)
How to read it: why we give scanf &age instead of age — because scanf needs to know the slot "where to put the input value." Give it the value (age) and it can’t find the slot. Give it the address (&age) and it can go there and store. What scanf does internally is what we did with *p in 3-4.
3-6. Pointer Size and the Meaning of Types
Input (ptrsize.c)
#include <stdio.h>
int main(void) {
int a = 1;
char b = 'A';
int *pa = &a;
char *pb = &b;
printf("int pointer size: %zun", sizeof(pa));
printf("char pointer size: %zun", sizeof(pb));
return 0;
}
Compile and run
gcc -Wall ptrsize.c -o ptrsize
./ptrsize
int pointer size: 8
char pointer size: 8
(Verified 2026-09-09. Based on a 64-bit environment.)
How to read it: sizeof is the operator "measure the box’s size," and %zu is the blank for printing that size. Pointers are the same size no matter what they point to (8 bytes in a 64-bit environment). They’re paper with a slot written on it, so the paper is all the same size. Then why do we write kinds like int * and char *? Because they’re signboards telling you "how many bytes to read when you get there."
3-7. Drawing a Picture — Pointers Your Hands Remember
Let’s leave the code and draw on paper.
[ x ] address: 0x100 value: 10
[ p ] address: 0x200 value: 0x100 ──→ arrow to [ x ]
How to read it: draw two boxes, write x’s slot in p’s value field, and draw an arrow. *p is "the place you reach by following the arrow." This one picture is the blueprint of every piece of code you learned today. Whenever you’re confused, don’t look at the code — redraw this picture. Pointers are a concept you learn through pictures.
3-8. Pointers and Functions — A Component That Changes the Original
Here’s the first real use of pointers. In C, when you hand a value to a function, a copy is passed — change it inside the function and the original doesn’t change. But hand over an address, and you can change the original.
Input (swap.c)
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(void) {
int x = 10, y = 20;
printf("Before: x=%d, y=%dn", x, y);
swap(&x, &y);
printf("After: x=%d, y=%dn", x, y);
return 0;
}
Compile and run
gcc -Wall swap.c -o swap
./swap
Before: x=10, y=20
After: x=20, y=10
(Verified 2026-09-09.)
How to read it: we gave swap not values but the slots of two boxes (&x, &y). The function went to those slots (*a, *b) and swapped the contents of the two originals. "Give a value and you change a copy; give an address and you change the original" — this sentence is half of all pointer usage. The same principle as why scanf demanded &. Every "function that changes the original" takes addresses.
4. Missions & Exercises
Mission — Writing a Pointer Program Without the Book
- Close the book and write the following program alone, start to finish.
- Create two int variables (x=10, y=20).
- Swap the values of x and y through pointers (use a temporary variable).
- Print before and after.
Before: x=10, y=20
After: x=20, y=10
- When done, open the book to compare, and fix what’s wrong.
- Draw your own program on paper using the 3-7 picture method — boxes, addresses, arrows.
- On purpose, run code that only does
int *p = NULL;and then stores a value at*p. Meeting Segmentation fault (an access violation error) is also part of today’s lesson. - In 3-5’s scanfwhy.c, remove the
&and compile with-Wall, and observe what warning appears.
Exercises
Problem 1. In int *p = &x;, explain how the asterisk’s meaning differs from the asterisk in the line below, *p = 99;.
Problem 2. For some pointer p, printing p, *p, and &p produces three different values. Explain what each of the three is, using the box-picture vocabulary from 3-7.
Problem 3. In testing, both the int pointer and the char pointer were 8 bytes. If the size is the same, why do we write different kinds like int * and char *?
Problem 4. Using the sentence "give a value and you change a copy; give an address and you change the original," explain why scanf demanded &age and why swap took &x, &y as one principle.
5. Model Answers & Completion Criteria
Mission Model Answer
The mission’s skeleton is the same as 3-8’s swap. Without a function, doing it all inside main looks like this:
#include <stdio.h>
int main(void) {
int x = 10, y = 20;
int *px = &x, *py = &y;
int temp;
printf("Before: x=%d, y=%dn", x, y);
temp = *px;
*px = *py;
*py = temp;
printf("After: x=%d, y=%dn", x, y);
return 0;
}
Run result (verified 2026-09-09)
Before: x=10, y=20
After: x=20, y=10
How to verify: ① The point is that you wrote it without looking at the book — if you got things wrong, the correction marks are proof of studying. ② In the picture there should be four boxes (x, y, px, py) and two arrows (px→x, py→y). ③ Did you see experiment 4’s Segmentation fault and experiment 5’s warning message with your own eyes — the verified messages are in Wall 2 and Wall 3.
Exercise Answers
Problem 1 answer. The asterisk at the declaration (int *p) is a mark saying "this variable is an address box." The asterisk at use (*p = 99) is the action "go to the address written in p." Same symbol, different meaning — look at the position and they separate.
Problem 2 answer. p is the slot written on the paper (x’s address), *p is the value seen by going to that slot (x’s content), and &p is the slot of the box where that paper itself sits (p’s own address). Find which of the three things in the picture — the x box, the p box, the arrow — is being talked about, and you won’t get confused.
Problem 3 answer. A pointer’s size is the size of "paper with a slot written on it," so it’s the same no matter what it points to (8 bytes on 64-bit). We still write the kind because the kind is a signboard for "how many bytes to read, and in what form, when you get there." If the signboard and the actual content disagree, you read something wrong.
Problem 4 answer. Because both are "functions that must change the original." scanf must put the input value into the original variable, and swap must swap the contents of the two originals. Hand over a value and the function receives only a copy and can’t reach the original; you must hand over an address so it can go to the slot and change the original.
Completion Criteria Checklist
- [ ] I can print a variable’s address with %p
- [ ] I know it’s normal for addresses to change on every run
- [ ] I can distinguish and explain the declaration * from the use *
- [ ] I can read and change another variable’s value through a pointer
- [ ] I can explain why scanf takes & in terms of "the box’s slot"
- [ ] I have met a Segmentation fault myself and can explain its cause
- [ ] I can write a pointer program without the book and explain it with a picture
6. Common Pitfalls & Fixes
**Wall 1. Confusing the Declaration * with the Use ***
Symptom: after int *p = &x;, you stick another asterisk on the next line like *p = &y;.
Cause: you assumed the same asterisk means the same thing. The declaration * is an "address box mark"; the use * is "going there."
Fix: build the habit of reading statements aloud — "int star p" is "integer address box p"; "star p is" is "the place p points to is." Look at the position and they separate.
Wall 2. Segmentation Fault (Segfault)
Symptom: run code like int *p = NULL; *p = 5; and it dies like this (verified 2026-09-09):
Segmentation fault (core dumped)
Cause: the pointer had no valid destination (NULL, a garbage address), or you touched a region you weren’t permitted to access. The operating system is evicting the program, saying "that slot isn’t yours."
Fix: ask "have I ever put a valid address into this pointer?" A segfault is a rite of passage in your beginner days — deliberately meeting it in mission step 4 is your vaccination.
Wall 3. Uninitialized Pointers — Scarier When It Doesn’t Die
Symptom: you declare only int *p; and then write *p = 5;, along with a compile warning (verified 2026-09-09):
segv.c:5:8: warning: 'p' is used uninitialized [-Wuninitialized]
Cause: a pointer that was never given an address is holding a garbage address.
Fix: a point of caution — in testing, this program happened not to die and ran anyway, because the garbage address happened to be a usable slot. "It runs" and "it’s correct" are different things. Build the habit of putting a valid address (or NULL) into a pointer the moment you declare it.
*Wall 4. Mixing Up p, p, and &p in Output
Symptom: you printed all three and panicked because they’re all different.
Cause: that’s normal. p is "the written slot," *p is "the value at that slot," and &p is "p’s own slot" — three different things.
Fix: draw the 3-7 picture and mark which part of the picture each one is. Find its place in the picture and you won’t get confused in the code.
Wall 5. Ignoring Pointer Types
Symptom: you put a char variable’s address into an int * and read a strange value.
Cause: the type is a signboard for "how many bytes to read, and how, when you get there," and the signboard disagreed with the actual content.
Fix: read the compiler’s warnings. Pointer type mismatches are warned about by -Wall.
7. Summary
Today’s Concepts
| Concept | One-line explanation |
|---|---|
| Address | The slot number of the memory slot where a variable lives — print with %p |
| Pointer | A variable that holds an address as its value — paper with a slot written on it |
| & | "Give me this variable’s address" |
| * (use position) | "Go to the written address" — dereference |
| Dereference | The act of following an address to reach a value |
| Segmentation fault | The error where the OS evicts you for touching someone else’s slot |
Today’s Syntax
| Syntax | What it does |
|---|---|
&x |
x’s address |
int *p = &x; |
Declare a pointer to int + store the address |
*p |
The value in the slot p points to (read) |
*p = 99; |
Write into the slot p points to |
%p |
The blank for printing an address |
sizeof(pointer) |
A pointer’s size — 8 bytes on 64-bit |
A Sense More Important Than Syntax
"Whoever knows the slot can act like the owner of that slot." In a normal program, a pointer is the proper use of this power; a memory attack is its abuse. If an attacker swaps "the slot being pointed to" for one of their choosing, the program goes where the attacker wrote and does what they want — and at the root of those techniques there are always today’s two symbols, & and *.
And pointers are not understood in a day. Even people who have written C for years all wandered for days at first. If it didn’t sink in today, that’s normal; tomorrow, just rerun 3-1 through 3-4. After about five repetitions, & and * start sticking to your hands. Understanding by feel rather than by concept is the order of conquering pointers.
Once every box is checked, Step 58 is complete.