UNIT 04Prerequisite: Units 00–0375 min read
Pointers are the single most important concept in C — and the single most misunderstood. Most tutorials throw symbols at you and hope something sticks. We are going to do the opposite: build the idea from the ground up, one layer at a time, until it feels completely obvious.
After this unit, linked lists, trees, graphs, and dynamic memory will all make sense. Every advanced data structure in DSA uses pointers internally. This unit is the key that unlocks the rest.
// Section 1
Every Variable Has a Home Address
Go back to what we learned in Unit 00. RAM is like a long street of tiny houses. Each house has a unique address. When you create a variable, the computer picks an empty house, stores your value there, and gives that house number a name — the variable name.
🏠 The house and address analogy
Real world
A house at 12 MG Road contains a family.
The address tells you where to find them.
The contents are who lives there.
In C
A variable age at address 0x1004 contains 25.
The address tells you where to find it.
The value is what is stored there.
The variable name age is just a convenient label for humans. Internally, the computer only works with the address 0x1004. The name exists only in your source code — it disappears when the program compiles.
Here is how you see a variable's address in C using the & operator:
#include <stdio.h>
int main() {
int age = 25;
float salary = 50000.0;
char grade = 'A';
printf("Value of age: %d\n", age);
printf("Address of age: %p\n", &age); /* %p prints an address */
printf("Value of salary: %f\n", salary);
printf("Address of salary: %p\n", &salary);
printf("Value of grade: %c\n", grade);
printf("Address of grade: %p\n", &grade);
return 0;
}
/* Sample output (addresses will differ on your machine):
Value of age: 25
Address of age: 0x7fff5fbff5ac
Value of salary: 50000.000000
Address of salary: 0x7fff5fbff5a8
Value of grade: A
Address of grade: 0x7fff5fbff5a7
*/
💡 Note
The & operator means "give me the address of". You already used this in Unit 00 with scanf — scanf("%d", &age) means "read a number and store it at the address of age." Now you know exactly why.
// Section 2
What is a Pointer?
A pointer is a variable that stores a memory address instead of a regular value.
A normal variable stores something like 25, 3.14, or 'A'. A pointer variable stores something like 0x1004 — the address of where some other variable lives. Think of a pointer as a piece of paper with someone's home address written on it. The paper is not the house. It just tells you where the house is.
// int age = 25 and int* p = &age — what lives in memory
normal variable
25
0x1004
age
pointer variable
0x1004
0x2008
p (pointer to age)
p stores the address of age (0x1004). p itself also lives somewhere in memory (0x2008). Following p leads you to age.
🎯 Pro Tip
One sentence definition: A pointer is a variable whose value is the address of another variable. That is all it is. Everything else — linked lists, trees, dynamic memory — is just a consequence of this one simple idea.
// Section 3
The Two Operators — & and *
Pointers use two operators. These two symbols are what confuse most people — because they look the same as other uses of & and * in C but mean completely different things depending on context. Let us nail this once and for all.
&
Address-of operator
Put it before a variable name to get its memory address. Think: "where does this variable live?"
&age → 0x1004
*
Dereference operator
Put it before a pointer to get the value stored at that address. Think: "go to that address and bring back what is there."
*p → 25 (the value at the address p holds)
#include <stdio.h>
int main() {
int age = 25;
int *p; /* declare a pointer to int — the * here means "pointer to" */
p = &age; /* store the address of age inside p */
printf("age = %d\n", age); /* 25 — direct access */
printf("&age = %p\n", &age); /* address of age */
printf("p = %p\n", p); /* p holds the same address */
printf("*p = %d\n", *p); /* dereference p — go to that address, get the value */
/* you can also CHANGE age through the pointer */
*p = 30; /* go to address p, write 30 there */
printf("age is now: %d\n", age); /* age is now: 30 */
return 0;
}
Breaking down every line
int age = 25Create a variable called age, store 25 in it. It gets some address, say 0x1004.
int *pDeclare a pointer variable p that will hold the address of an int. The * here means "this is a pointer type".
p = &age& gives us the address of age (0x1004). We store that address inside p.
*p* here means "dereference" — go to the address stored in p (0x1004) and read the value there (25).
*p = 30Go to the address stored in p and write 30 there. Since p points to age, age becomes 30.
⚠️ Important
The * symbol has two different meanings in C — do not confuse them:
In a declaration like int *p — the * means "p is a pointer to int".
In an expression like *p = 30 — the * means "go to the address p holds".
Same symbol. Different context. Different meaning. This is the #1 source of pointer confusion.
// Section 4
Pointer Types — Why They Matter
Every pointer has a type — int*, float*, char*. The type tells the compiler how many bytes to read when you dereference. An int* reads 4 bytes. A char* reads 1 byte. If you use the wrong pointer type, you read the wrong amount of memory and get garbage data.
#include <stdio.h>
int main() {
int num = 100;
float price = 9.99;
char letter = 'Z';
int *ip = # /* int pointer — points to an int */
float *fp = &price; /* float pointer — points to a float */
char *cp = &letter; /* char pointer — points to a char */
printf("num via pointer: %d\n", *ip); /* 100 */
printf("price via pointer: %f\n", *fp); /* 9.990000 */
printf("letter via pointer: %c\n", *cp); /* Z */
return 0;
}
// Section 5
Pointer Arithmetic — Moving Through Memory
You can add and subtract integers from pointers — but the result is not what you might expect. When you do p + 1, the pointer does not move by 1 byte. It moves by 1 × sizeof(type) bytes. An int pointer moves 4 bytes forward. A char pointer moves 1 byte forward. This is deliberate — it lets you step through arrays cleanly.
// int arr[4] — p starts at arr[0], p+1 jumps 4 bytes to arr[1]
p = &arr[0] → 0x1000p+1 → 0x1004 (jumped 4 bytes)p+2 → 0x1008
#include <stdio.h>
int main() {
int arr[4] = {10, 20, 30, 40};
int *p = arr; /* p points to arr[0] — array name IS the address of first element */
int i;
printf("Using pointer arithmetic to walk the array:\n");
for (i = 0; i < 4; i++) {
printf("p+%d = address %p, value = %d\n", i, p+i, *(p+i));
}
/* arr[i] and *(p+i) are identical — two ways to say the same thing */
printf("\narr[2] = %d\n", arr[2]); /* 30 */
printf("*(p + 2) = %d\n", *(p + 2)); /* 30 — same thing */
return 0;
}
💡 Note
Array indexing and pointer arithmetic are the same thing.When you write arr[2], C internally translates it to *(arr + 2). The array name is a pointer to the first element. Square bracket notation is just cleaner syntax for pointer arithmetic. This is why arrays and pointers are so deeply connected in C.
// Section 6
Pointers and Functions — Pass by Reference
This is where pointers become genuinely powerful and practically useful. In C, when you pass a variable to a function normally, the function gets a copy — it cannot change the original. This is called pass by value. When you pass the address of a variable, the function can reach back and change the original directly. This is called pass by reference.
❌ Pass by value — original unchanged
The function gets a photocopy. It scribbles all over the photocopy. Your original document stays untouched.
✅ Pass by reference — original changed
The function gets your home address. It walks to your house and changes things directly. The original is modified.
#include <stdio.h>
/* WRONG: pass by value — cannot change the original */
void tryDouble_wrong(int n) {
n = n * 2; /* only changes the local copy — original untouched */
}
/* CORRECT: pass by reference — can change the original */
void tryDouble_correct(int *n) {
*n = *n * 2; /* dereference n to reach the original variable */
}
int main() {
int x = 10;
tryDouble_wrong(x);
printf("After wrong: x = %d\n", x); /* x = 10 — unchanged */
tryDouble_correct(&x); /* pass the ADDRESS of x */
printf("After correct: x = %d\n", x); /* x = 20 — changed! */
return 0;
}
Classic example — swap two numbers
The swap function is the most famous pass-by-reference example. It cannot work without pointers in C. This exact function is used inside sorting algorithms.
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a; /* save value at address a */
*a = *b; /* write value at b into address a */
*b = temp; /* write saved value into address b */
}
int main() {
int x = 100, y = 200;
printf("Before: x = %d, y = %d\n", x, y); /* x=100, y=200 */
swap(&x, &y);
printf("After: x = %d, y = %d\n", x, y); /* x=200, y=100 */
return 0;
}
// Section 7
Pointers and Arrays — The Deep Connection
When you pass an array to a function in C, you are actually passing a pointer to its first element. The entire array is not copied — only the address is passed. This means functions can modify arrays directly, and it is also why arrays are fast to pass around even when they are huge.
#include <stdio.h>
/* when you write int arr[], C treats it as int *arr internally */
void doubleAll(int *arr, int n) {
int i;
for (i = 0; i < n; i++) {
arr[i] = arr[i] * 2; /* modifies the ORIGINAL array */
}
}
int main() {
int numbers[5] = {1, 2, 3, 4, 5};
int i;
doubleAll(numbers, 5); /* pass array name = pass address of first element */
for (i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
/* Output: 2 4 6 8 10 — original array was modified */
return 0;
}
// Section 8
The NULL Pointer — A Pointer That Points to Nothing
NULL is a special value you can assign to a pointer to say "this pointer currently points to nothing." It is the number 0. In linked lists and trees, NULL is how you mark the end of a chain — "there is no next node."
#include <stdio.h>
int main() {
int *p = NULL; /* p does not point to anything yet */
/* ALWAYS check before dereferencing */
if (p != NULL) {
printf("Value: %d\n", *p);
} else {
printf("Pointer is NULL — nothing to read\n");
}
/* Assigning a real address later */
int age = 25;
p = &age;
if (p != NULL) {
printf("Value: %d\n", *p); /* Value: 25 */
}
return 0;
}
⚠️ Important
Never dereference a NULL pointer. If you do *p when p is NULL, your program crashes immediately with a Segmentation Fault. Always check if (p != NULL) before reading from a pointer if there is any chance it could be NULL. This rule saves you hours of debugging.
// Section 9
Common Pointer Mistakes — And How to Avoid Them
⚠ Uninitialized pointer — reading garbage
Symptom: Program crashes or reads random values
Fix: Always initialise a pointer — either to NULL or to the address of a real variable before using it.
❌ Wrong
int *p; /* p contains garbage — random address */
*p = 10; /* writing to random memory — crash */
✅ Correct
int *p = NULL; /* safe — clearly says "not set yet" */
int x = 5;
p = &x; /* now p is valid */
*p = 10; /* safe */
⚠ Dangling pointer — pointing to freed memory
Symptom: Program works sometimes but crashes unpredictably
Fix: After freeing memory, always set the pointer to NULL immediately so it cannot be accidentally used.
❌ Wrong
int *p = (int*)malloc(sizeof(int));
*p = 42;
free(p);
printf("%d\n", *p); /* p still points there — undefined */
✅ Correct
int *p = (int*)malloc(sizeof(int));
*p = 42;
free(p);
p = NULL; /* safe — pointer is now clearly dead */
⚠ Wrong pointer type
Symptom: Reading the wrong number of bytes — getting garbage values
Fix: The pointer type must match the variable type it points to.
❌ Wrong
float price = 9.99;
int *p = &price; /* WRONG — int* on a float variable */
printf("%d", *p); /* reads only 4 bytes wrong way */
✅ Correct
float price = 9.99;
float *p = &price; /* CORRECT — types match */
printf("%f", *p); /* 9.990000 */
// Section 10
Why Pointers Unlock All of DSA
Now that you understand pointers, let us connect this to what is coming. Every data structure from Unit 05 onwards uses pointers to link pieces of data together. Here is a quick preview:
Unit 05Linked Lists
Each node has a pointer to the next node. The last node points to NULL. Without pointers, you cannot build a linked list at all.
Unit 11Trees
Each node has a left pointer and a right pointer. The tree grows by creating new nodes and linking them with pointers.
Unit 15Graphs
Each node has a list of pointers to its neighbours. Traversal means following pointers from node to node.
Unit 16Dynamic Programming
DP tables are arrays — which are pointers to contiguous memory. Memoization uses pointer-based structures internally.
🎯 Pro Tip
Quick mental model to carry forward: A pointer is just an arrow. It says "go here." Linked lists are arrows chaining nodes. Trees are arrows branching down. Graphs are arrows going in all directions. Every time you see a pointer from now on, just think: arrow pointing to something.
// What's Next
You Are Ready for Unit 05
You now understand the most important and most feared concept in C programming. Pointers are not magic — they are just variables that hold addresses, and two operators: & to get an address, * to follow one.
In Unit 05 we build our first truly dynamic data structure — Linked Lists. Everything you just learned about pointers will be used immediately. A linked list is nothing more than nodes connected by pointers.
UP NEXT → UNIT 05
Linked Lists — Nodes Connected by Pointers
Singly, doubly, circular — insert, delete, reverse, detect loops.
Coming Soon →🎯 Key Takeaways
- ✓Every variable lives at a specific address in RAM. The & operator gives you that address
- ✓A pointer is a variable that stores a memory address — nothing more, nothing less
- ✓int *p declares p as a pointer to int. p = &age stores the address of age in p
- ✓The * symbol has two meanings: in declarations it means "pointer type", in expressions it means "go to that address"
- ✓*p reads the value at the address p holds. *p = 10 writes 10 to that address
- ✓Pointer arithmetic: p+1 moves by sizeof(type) bytes — not by 1 byte
- ✓Pass by reference: passing &variable lets a function modify the original. Pass by value only passes a copy
- ✓NULL means the pointer points to nothing. Always check for NULL before dereferencing
- ✓Array name is a pointer to the first element. arr[i] and *(arr+i) are identical
- ✓Every data structure from here — linked lists, trees, graphs — is built by connecting nodes with pointers