Pointer Arithmetic and Const Pointers in C++
Have you ever played a treasure hunt game?
You start with a tiny clue, and each clue leads you closer to the final treasure.
You don’t just jump directly to the treasure — you move step-by-step, following the trail of hints carefully.
In the world of C++, pointers work just like those clues.
A pointer doesn't store the actual treasure (the value) itself; instead, it holds the location — the address — where that treasure is hidden in memory.
Using this address, you can find and access the real value whenever you want.
Now, just like in a treasure hunt where you sometimes need to move from one spot to another, in C++ we can perform pointer arithmetic.
Pointer arithmetic allows you to move through memory, step-by-step, especially when dealing with things like arrays.
It’s like saying, "Take one step forward to find the next clue!"
And then, there are const pointers — they are the rules of the game.
A const pointer might tell you:
- "Hey, you are allowed to hold this clue, but you can't change it."
- Or, "You can read the treasure here, but you must not move to another location!"
These rules keep everything safe and predictable, just like the rules in a real treasure hunt!
So today, let’s dive deep into this magical journey of pointers, learn how to move between clues, and understand when and why the rules (const pointers) matter!
Adventure awaits — let's go!
Pointer Arithmetic in C++ (Addition, Subtraction, and Comparisons)
Imagine you are playing a board game, like Snakes and Ladders.
You roll a dice, and your token moves forward (Addition).
If you step on a snake, you slide back (Subtraction).
And sometimes, you want to compare who is ahead — "Am I closer to the finish line than my friend?" (Comparison).
Similarly, in C++, you can move pointers forward, backward, and even compare them!
Let's jump into it, step-by-step.
Pointer Addition (+)
Imagine you are walking along a street with houses lined up in a row.
Each house has a number: 100, 102, 104, 106, and so on.
You are currently standing in front of house 100.
Now your friend says,
"Walk 2 houses ahead!"

So you move:
- From 100 ➔ 102 ➔ 104
You add steps and reach a new house.
Similarly, in C++, adding a number to a pointer means moving forward in memory to the next item.
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // points to first element (arr[0])
cout << "Value at ptr: " << *ptr << endl; // 10
ptr = ptr + 2; // move forward by 2 elements
cout << "Value at new ptr: " << *ptr << endl; // 30
return 0;
}
Important:
Pointer automatically jumps the right distance depending on data type size.
For example, adding 1 to an int* moves 4 bytes forward, not just 1 byte!
Imagine an array like houses:
Address | Value |
---|---|
1000 | 10 |
1004 | 20 |
1008 | 30 |
1012 | 40 |
1016 | 50 |
- Start at 1000 (10)
- ptr + 1 ➔ jump to 1004 (20)
- ptr + 2 ➔ jump to 1008 (30)
Every step moves 4 bytes forward (for int).
Pointer Subtraction (-) in C++
Imagine you are at a shopping mall, walking down a long row of shops.
You start at Shop 5 and your friend says:
"Hey, you missed the candy store at Shop 3! Go back two shops!"

So you walk backward:
- From Shop 5 ➔ Shop 4 ➔ Shop 3
In the same way, pointer subtraction in C++ is like walking backward in memory!
You can subtract a number from a pointer to move it to a previous element.
In C++, if you subtract an integer from a pointer,
it moves backward by that many elements, not bytes.
Just like addition moved forward,
subtraction moves you back to earlier positions in memory.
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {5, 10, 15, 20, 25};
int* ptr = arr + 4; // points to arr[4], value 25
cout << "Value at ptr: " << *ptr << endl; // 25
ptr = ptr - 2; // move backward by 2 elements
cout << "Value after moving back 2 steps: " << *ptr << endl; // 15
return 0;
}
- ptr starts at arr[4], which has value 25.
- ptr - 2 means move 2 elements back.
- Now it points to arr[2], which has value 15.
Easy, right? Just like stepping backward in a mall to find your missed candy shop!
Imagine an array like mall shops:
Address | Value |
---|---|
1000 | 5 |
1004 | 10 |
1008 | 15 |
1012 | 20 |
1016 | 25 |
- ptr at 1016 (25).
- ptr - 2 ➔ jumps back to 1008 (15).
Each backward step goes 4 bytes back for int.
Pointer Subtraction Between Two Pointers
Not only can you subtract a number from a pointer,
you can also subtract two pointers to find out how far apart they are!
#include<iostream>
using namespace std;
int main() {
int arr[5] = {2, 4, 6, 8, 10};
int* ptr1 = arr + 4; // points to arr[4] (10)
int* ptr2 = arr + 1; // points to arr[1] (4)
int distance = ptr1 - ptr2; // how many elements apart?
cout << "Distance between ptr1 and ptr2: " << distance << endl; // 3
return 0;
}
Explanation
- ptr1 is at index 4, ptr2 is at index 1.
- So, ptr1 - ptr2 = 3.
It tells you there are 3 elements between them!
(Like saying "Shop 5 is 3 shops ahead of Shop 2.")

Important:
When subtracting two pointers, they must point into the same array.
Otherwise, C++ gets confused and it can crash your program!
Things to Remember:
Subtracting a number from a pointer moves it backward by that many elements.
Subtracting one pointer from another gives you the number of elements between them.
Subtracting pointers from different arrays = undefined behavior (not allowed!).
Pointer Comparisons (==, !=, >, <) in C++
Imagine you and your friend are running a race on a straight track.
Each runner starts at different positions.
While running, you might wonder:
- "Are we at the same spot?" (==)
- "Am I ahead of him?" (>)
- "Is he ahead of me?" (<)
- "Are we at different spots?" (!=)
This is exactly how Pointer Comparisons work in C++!
Pointers can check:
- Are they pointing to the same place?
- Who is ahead in memory?
- Who is behind?
- Are they pointing to different places?
What is Pointer Comparison?
Pointer Comparison means comparing two pointers to see where they are in memory relative to each other.
You can use comparison operators:
- == (equal to)
- != (not equal to)
- > (greater than)
- < (less than)
But:
You should only compare pointers that belong to the same array or the same block of memory.
Otherwise, C++ gets confused (undefined behavior)!
Let's See Each Operator One by One:
1. Equality (==)
Checks if two pointers are pointing to the same memory location.
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr1 = arr; // points to arr[0]
int* ptr2 = arr; // also points to arr[0]
if (ptr1 == ptr2) {
cout << "Both pointers are at the same position!" << endl;
}
return 0;
}
Both you and your friend are standing at the same starting line.
Of course, you are equal!
2. Inequality (!=)
Checks if two pointers are pointing to different memory locations.
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {5, 15, 25, 35, 45};
int* ptr1 = arr; // arr[0]
int* ptr2 = arr + 2; // arr[2]
if (ptr1 != ptr2) {
cout << "Pointers are at different positions!" << endl;
}
return 0;
}
You are at Shop 0, your friend is at Shop 2 —
Different places! So ptr1 != ptr2 is true.
3. Greater Than (>)
Checks if one pointer is ahead of another in memory.
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {100, 200, 300, 400, 500};
int* ptr1 = arr + 3; // arr[3]
int* ptr2 = arr + 1; // arr[1]
if (ptr1 > ptr2) {
cout << "ptr1 is ahead of ptr2!" << endl;
}
return 0;
}
- ptr1 is at arr[3] (400).
- ptr2 is at arr[1] (200).
- 400 comes after 200 ➔ ptr1 > ptr2 is true.
It's like checking:
"Am I ahead of you on the running track?"
4. Less Than (<)
Checks if one pointer is behind another in memory.
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {2, 4, 6, 8, 10};
int* ptr1 = arr; // arr[0]
int* ptr2 = arr + 4; // arr[4]
if (ptr1 < ptr2) {
cout << "ptr1 is behind ptr2!" << endl;
}
return 0;
}
- ptr1 at arr[0] (2).
- ptr2 at arr[4] (10).
- 2 comes before 10 ➔ ptr1 < ptr2 is true.
Just like being behind someone in a race!
Summary:
Pointer Comparisons are just like checking positions in a race:
- == ➔ Are we at the same spot?
- != ➔ Are we at different spots?
- > ➔ Am I ahead?
- < ➔ Am I behind?
As long as you're racing on the same track (same array),
Pointer Comparisons are safe and super useful!
Pointer Increment (++) and Decrement (--) in C++
Imagine you're reading a comic book.
Each page has a funny cartoon.
- When you turn to the next page, you are moving forward ➔ like incrementing.
- When you flip back to a previous page, you are moving backward ➔ like decrementing.
In C++, pointers can do the same thing!
- Incrementing a pointer ➔ moves it to the next element.
- Decrementing a pointer ➔ moves it to the previous element.
Pointer Increment (++)
Pointer Increment means moving to the next element.
- For an int*, moving to next element moves 4 bytes forward (because int is 4 bytes).
- For a char*, it moves 1 byte forward (because char is 1 byte).
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {1, 3, 5, 7, 9};
int* ptr = arr; // points to arr[0]
cout << "Value at ptr: " << *ptr << endl; // 1
ptr++; // move to next element
cout << "Value at ptr after increment: " << *ptr << endl; // 3
return 0;
}
- ptr points to the first element (1).
- After ptr++, it now points to the second element (3).
- Just like turning to the next page in your comic book!
Important:
Pointer moves one full element, not just one byte!
Pointer Decrement (--)
Pointer Decrement means moving to the previous element.
Example:
#include<iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr + 3; // points to arr[3] (40)
cout << "Value at ptr: " << *ptr << endl; // 40
ptr--; // move to previous element
cout << "Value at ptr after decrement: " << *ptr << endl; // 30
return 0;
}
- ptr initially points to 40.
- After ptr--, it points back to 30.
Just like flipping back one page to re-read a funny joke in your comic book!
Pre-Increment vs Post-Increment
You might see two styles:
Style | What Happens |
---|---|
++ptr (pre-increment) | First move the pointer, then use it |
ptr++ (post-increment) | First use the pointer, then move it |
Simple Example:
#include<iostream>
using namespace std;
int main() {
int arr[3] = {100, 200, 300};
int* ptr = arr;
cout << *ptr++ << endl; // prints 100, then moves to next (post-increment)
cout << *ptr << endl; // now points to 200
ptr = arr; // reset pointer to start
cout << *++ptr << endl; // first moves to next, then prints 200 (pre-increment)
return 0;
}
Summary:
Pointer Increment and Decrement are like moving through pages:
- ptr++ ➔ Go to next page (next element).
- ptr-- ➔ Go to previous page (previous element).
C++ makes it super easy by moving the pointer based on element size,
so you just enjoy flipping pages (or memory locations)!
Const with Pointers in C++
Imagine you have a pet cat.
Now there are three situations:
- You can walk your cat anywhere, but you can't change the cat itself. (const cat*)
- You can change the cat (maybe get a new pet!), but you must stay at the same spot. (cat* const)
- You can't change the cat, and you can't move from your spot either. (const cat* const)
Pointers in C++ work exactly like that!
When you add const with pointers,
you're either:
- stopping changes to the data,
- stopping changes to the pointer itself,
- or stopping both!
What is Const with Pointers?
In C++, const with pointers controls what you are allowed to change:
Situation | What Changes | What Cannot Change |
---|---|---|
const int* | Pointer can move | Value can't change |
int* const | Value can change | Pointer can't move |
const int* const | Neither value nor pointer can change |
1. const int* ptr (Pointer to Constant Data)
- The pointer can move.
- The data it points to cannot be changed.
Example:
#include<iostream>
using namespace std;
int main() {
int x = 10;
int y = 20;
const int* ptr = &x; // pointer to constant int
cout << *ptr << endl; // prints 10
ptr = &y; // OK: pointer can move
cout << *ptr << endl; // prints 20
// *ptr = 30; // Error: cannot change value through ptr
return 0;
}
2. int* const ptr (Constant Pointer to Non-Constant Data)
- The pointer cannot move (it's fixed).
- The data can be changed.
Example:
#include<iostream>
using namespace std;
int main() {
int x = 50;
int* const ptr = &x; // constant pointer to int
cout << *ptr << endl; // prints 50
*ptr = 100; // OK: can change value through ptr
cout << *ptr << endl; // prints 100
// int y = 200;
// ptr = &y; // Error: cannot change pointer
return 0;
}
3. const int* const ptr (Constant Pointer to Constant Data)
- The pointer cannot move.
- The data cannot change.
#include<iostream>
using namespace std;
int main() {
int x = 500;
const int* const ptr = &x; // constant pointer to constant int
cout << *ptr << endl; // prints 500
// *ptr = 600; // Error: cannot change value
// int y = 700;
// ptr = &y; // Error: cannot change pointer
return 0;
}
Summary:
When you combine const with pointers:
- const int* ➔ data is constant, pointer can move.
- int* const ➔ pointer is constant, data can change.
- const int* const ➔ both are constant, nothing can change!
Void Pointers (void*) in C++
Imagine you have a magic box.
This magic box can hold anything —
a toy car , a doll , a comic book , or even candies .
The only rule?
When you open the box to use the item, you must know what's inside.
Otherwise, you'll get confused!
In C++, a void* pointer is just like this magic box.
- It can point to any type of data — int, float, char, anything!
- But when you want to actually use the data,
you have to tell C++ what type it is.
What is a Void Pointer?
A void pointer is a special pointer that doesn't have a type.
It can point to any data type — but you cannot directly access the value through it without telling it the type first.
Declaring a Void Pointer
Syntax:
void* ptr;
- ptr can point to anything — an int, a float, a char, etc.
- It's a general-purpose pointer.
Important: You Cannot Dereference Directly
- Dereferencing means getting the value stored at the pointer.
- Since void* has no type, C++ has no idea how many bytes to read.
- That's why you must cast it first; we'll learn what it is shortly.
When Are Void Pointers Useful?
When you want to write general-purpose functions
When you don't know the data type ahead of time
When you want flexibility
Casting From/To Void Pointers in C++
Imagine you get a mystery gift box at a party.
You know it contains a toy, but you don't know if it's:
- A car
- A robot
- A doll
Before you play with it, you have to unwrap the box carefully
and say exactly what kind of toy it is.
In C++, when you have a void* pointer, it's the same thing!
- You have a mystery pointer (void pointer).
- Before you can use it, you must unwrap it —
cast it into the correct type!
Why Do We Need Casting with Void Pointers?
Because a void pointer has no type.
- It only knows an address.
- It doesn't know how much memory to read.
- It doesn't know how to interpret the memory (int? float? char?).
Casting tells C++:
"Hey, unwrap this as an int (or float, or char)!"
How to Cast a Void Pointer to Another Type?
Use static_cast<Type>(void_pointer)*
This converts the void* into the right type,
So you can safely use it.
Example:
int x = 5;
void* ptr = &x;
int* intPtr = static_cast<int*>(ptr); // cast void* to int*
cout << *intPtr << endl; // prints 5
Void Pointer Can Point to Any Type:
#include<iostream>
using namespace std;
int main() {
int a = 10;
float b = 5.5;
char c = 'Z';
void* ptr;
ptr = &a;
// cout << *ptr; // Error: cannot dereference void pointer directly
cout << *(static_cast<int*>(ptr)) << endl; // 10
ptr = &b;
cout << *(static_cast<float*>(ptr)) << endl; // 5.5
ptr = &c;
cout << *(static_cast<char*>(ptr)) << endl; // Z
return 0;
}
- ptr is a void pointer, pointing to x.
- We cast it to int* using static_cast<int*>.
In Short:
From ➔ To | Cast Needed? | Example |
---|---|---|
int* ➔ void* | No | void* ptr = intPtr; |
float* ➔ void* | No | void* ptr = floatPtr; |
void* ➔ int* | Yes | int* intPtr = static_cast<int*>(ptr); |
void* ➔ float* | Yes | * floatPtr = static_cast<float*>(ptr); |
Important Things to Remember:
Assigning any pointer to void* ➔ NO cast needed
Using a void pointer ➔ YES cast needed
Never use a wrong cast! (like treating float as int — it causes bugs!)
Pointer arithmetic is not allowed on void* without casting.