Every variable in your program lives somewhere in the computer's memory, and every location in memory has an address, a number that says where it is. A pointer is a variable that stores one of those addresses. Instead of holding a value directly, it "points" to where the value lives.
Pointers have a reputation for being difficult, mostly because mistakes with them can crash a program. But the core idea is simple, and understanding it explains a lot about how C++ and computers work: how functions can change variables they don't own, how arrays are laid out, and how objects can be created while the program runs.
In modern C++ you'll use raw pointers less than in older code, because references, containers and smart pointers handle most jobs more safely. You still need to understand them, because they appear in libraries, older code and many interviews. This lesson takes them one small step at a time.
Addresses and the & operator
Put & in front of a variable to get its address:
int score = 42;
cout << &score; // something like 0x7ffd5c3a9b2cThe exact number changes every time you run the program and doesn't matter in itself. What matters is that it identifies where score is stored. Note that & means two different things in C++: "address of" in front of a variable, and "reference" in a declaration such as int& r.
Declaring and dereferencing a pointer
int score = 42;
int* p = &score; // p holds the address of score
cout << *p; // 42: the value at that address
*p = 50; // changes score through the pointer
cout << score; // 50int*means "pointer to int". The type must match what it points to.*pdereferences the pointer: it means "the value at the address stored in p". You can read it and assign to it.pitself is just an address; printing it shows the address, not 42.
So * also has two meanings: in a declaration it makes a pointer type, and in an expression it follows the pointer.
nullptr and pointers to structs
A pointer that doesn't point at anything should hold nullptr:
Product* found = nullptr;
if (found != nullptr) { ... }Dereferencing a null pointer, or a pointer to memory that no longer exists, is undefined behaviour: often an immediate crash. Always initialise pointers, and check them before use when they might be null.
To reach a member through a pointer to a struct or object, use the arrow operator: found->name is a shorter way of writing (*found).name.
Pointers, arrays and references
The name of a classic array acts like a pointer to its first element, and adding to a pointer moves it along the array:
int nums[3] = {10, 20, 30};
int* p = nums; // points at nums[0]
cout << *(p + 2); // 30, the same as nums[2]This is called pointer arithmetic. It's how arrays work under the hood, but in everyday code nums[2] or a vector is clearer and safer.
A reference (int& r = score;) is like a pointer that can never be null, never changes what it refers to and doesn't need * to use. Prefer references for function parameters, and use pointers when something can be "nothing" or needs to change what it points to.
const and pointers
A pointer involves two things that could be read-only: the value it points at, and the pointer itself. Where you put const decides which one:
int n = 5;
const int* a = &n; // pointer to const int: can't change n through a
int* const b = &n; // const pointer: can't point b somewhere else
const int* const c = &n; // both| Declaration | *p = 9 allowed? | p = &other allowed? |
|---|---|---|
const int* p | No | Yes |
int* const p | Yes | No |
const int* const p | No | No |
A trick for reading them: start at the name and read right to left. int* const b is "b is a const pointer to int", and const int* a is "a is a pointer to an int that is const".
The first form is the one you'll meet most often. A function parameter such as const Product* p promises that the function only looks at the product, just like const Product& does for references. The compiler enforces the promise: assigning through a gives the error "assignment of read-only location". Note that const int* a only stops changes through a; n itself can still be changed directly.
C++ Pointers Syntax
Pointer syntax
int x = 5;
int* p = &x; // pointer to x
*p = 10; // change x through p
int* q = nullptr; // points to nothing
if (q) { ... } // true only if q isn't null
Product* pp = &item;
pp->name; // same as (*pp).nameC++ Pointers Summary
- Every variable has an address;
&xgets it. int* p = &x;stores the address in a pointer;*preads or changes the value it points to.- Initialise pointers, use
nullptrfor "points to nothing" and check before dereferencing. ptr->memberreaches a member through a pointer.- Prefer references and containers; use pointers when "nothing" is a valid option.
C++ Pointers Examples
Each example shows the code, the output it produces and what each part does.
Changing a value through a pointer
#include <iostream>
using namespace std;
int main() {
int lives = 3;
int* p = &lives;
cout << "lives = " << lives << ", *p = " << *p << endl;
*p = *p - 1;
cout << "after: lives = " << lives << endl;
cout << "p points to lives? " << (p == &lives) << endl;
return 0;
}lives = 3, *p = 3
after: lives = 2
p points to lives? 1Line-by-line explanation
- Line 6p stores the address of lives.
- Line 8Dereferencing p gives the same value as lives.
- Line 9Changing *p changes lives itself.
- Line 11The pointer holds exactly lives's address, so the comparison is true (printed as 1).
Swapping with pointers and with references
#include <iostream>
using namespace std;
void swapPtr(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
void swapRef(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 1, y = 2;
swapPtr(&x, &y);
cout << x << " " << y << endl;
swapRef(x, y);
cout << x << " " << y << endl;
return 0;
}2 1
1 2Line-by-line explanation
- Lines 4 to 8The pointer version needs * every time and & when calling.
- Lines 10 to 14The reference version does the same job with cleaner syntax.
- Line 18The pointer version changes the caller's variables.
- Line 20So does the reference version, which is usually preferred.
Searching and returning a pointer
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Product {
string name;
double price;
};
Product* findProduct(vector<Product>& items, const string& name) {
for (Product& p : items) {
if (p.name == name) return &p;
}
return nullptr;
}
int main() {
vector<Product> shop = {{"Pen", 1.20}, {"Lamp", 24.50}};
Product* lamp = findProduct(shop, "Lamp");
if (lamp) {
lamp->price = 19.99;
}
Product* sofa = findProduct(shop, "Sofa");
cout << shop[1].name << " now " << shop[1].price << endl;
cout << "Sofa found? " << (sofa != nullptr) << endl;
return 0;
}Lamp now 19.99
Sofa found? 0Line-by-line explanation
- Lines 11 to 16Return a pointer to the matching product, or nullptr if there isn't one.
- Lines 21 to 23Check before using the pointer, then change the price through it with ->.
- Line 24The search for "Sofa" returns nullptr.
Common C++ Pointers Mistakes
Using an uninitialised pointer
int* p;
*p = 5; // p points somewhere random: undefined behaviourint value = 0;
int* p = &value;
*p = 5;Dereferencing nullptr
Product* found = findProduct(shop, "Sofa");
cout << found->name; // crash if nothing was foundProduct* found = findProduct(shop, "Sofa");
if (found) cout << found->name;Returning the address of a local variable
int* makeNumber() {
int n = 7;
return &n; // n is destroyed when the function ends
}int makeNumber() {
return 7; // return the value instead
}C++ Pointers in Real Projects
Pointers in practice
Operating systems, device drivers and game engines use pointers to work directly with memory and hardware. Data structures such as linked lists and trees connect their nodes with pointers. In application code, pointers often appear as "optional" results, like the findProduct example, or as links between objects managed by smart pointers.
C++ Pointers FAQ
Why use nullptr instead of NULL or 0?
NULL and 0 are really integers, so they can pick the wrong function. With overloads f(int) and f(int*), f(0) calls the int version, and g++ reports f(NULL) as ambiguous. nullptr has its own type that converts only to pointers, so f(nullptr) always calls the pointer version.
How big is a pointer in C++?
On a 64-bit system, a pointer is usually 8 bytes, whatever it points to: sizeof(int*), sizeof(double*) and sizeof(char*) are all 8. The pointer only stores an address, and every address is the same size. On 32-bit systems, pointers are usually 4 bytes.
Why does cout print text instead of an address for a char pointer?
cout treats a char* or const char* as a C-style string and prints the characters until it reaches the terminating '\0'. To see the address instead, cast it: cout << static_cast<const void*>(msg);. Pointers to other types, such as int*, print as addresses.
What is a pointer to a pointer in C++?
An int** stores the address of an int* variable. Dereferencing it once gives the inner pointer, and twice gives the int: with int** pp = &p;, **pp = 7 changes the number p points at. You'll mostly see them in C-style code, such as char** argv in main, or when a function has to change which address a pointer holds.
What does segmentation fault mean in C++?
The program tried to read or write memory it isn't allowed to touch, and the operating system stopped it. Common causes are dereferencing nullptr or an uninitialised pointer, using a pointer to a variable that no longer exists, and indexing far past the end of an array. Compile with -g -fsanitize=address and g++ will report the exact line where the bad access happened.
C++ Pointers Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the code so p stores the address of age.
int age = 20;
int* p = age;
What does this print?
int a = 8;
int* p = &a;
*p = *p * 2;
cout << a;
What does this print?
int arr[4] = {5, 10, 15, 20};
int* p = arr;
cout << *(p + 3) << " " << *p;
Which line correctly reads the name through a pointer to a struct?
When is a pointer a better choice than a reference?
C++ Pointers Quiz
5 questions to check what you learned.
What does a pointer store?
- A copy of a value
- A function
- A memory address
- A type name
What does
*pmean in an expression when p is a pointer?- Multiply p
- The address of p
- A new pointer
- The value at the address stored in p
What should a pointer hold when it points to nothing?
- nullptr
- 0x0000 as a string
- -1
- An empty string
What is wrong with returning
&nwhere n is a local variable?- Nothing
- You can only return references
- n is destroyed when the function ends, so the pointer is left dangling
- It returns the wrong type
What is
(*p).nameequivalent to?- p.name
- p->name
- &p->name
- *p->name