How to Use Pointers in C++: A Complete Beginner’s Guide
If you’re learning C++, you’ve probably heard that pointers are the most confusing topic for beginners. And honestly? There’s a reason. Pointers aren’t taught well in most tutorials because instructors jump straight into syntax without building the mental model first. This article changes that. By the time you finish reading, pointers won’t just make sense—they’ll feel obvious.
Video Walkthrough
Why Pointers Confuse Beginners (And Why You Need to Learn Them)
Here’s the thing: pointers aren’t actually complicated. They’re just a way to work with memory addresses. But most tutorials explain them backwards—they show you the syntax before explaining what’s actually happening under the hood.
Beginners struggle because they’re trying to memorize int* ptr = &x; without understanding what that code is really doing. They see the * and & symbols and think there’s some magic happening. There isn’t.
So why learn pointers? Because they’re essential in modern C++. You can’t work with dynamic memory, pass large objects efficiently, or build advanced data structures without understanding pointers. They’re also one of the most common interview questions. Master pointers now, and you’ll unlock the entire C++ language.
Understanding Computer Memory: The Mental Model
Imagine your computer’s memory as a long street with millions of houses in a row. Each house has:
- A unique address (like “House #1024”)
- A value inside (like a number, a letter, or some data)
When you write this in C++:
int x = 42;
The computer does something like this:
- Finds an empty “house” in memory
- Puts the number 42 inside that house
- Labels that house with the variable name
x
For example, maybe the house is at memory address 0x7ffc7b6d4a2c. The computer remembers this address so it can find that house whenever you use the variable name x.
This is all fine and good. But what if you need to know the actual address? Or what if you want to pass that address to another function instead of copying all the data inside the house? That’s where pointers come in.
A pointer is simply a variable that holds a memory address instead of regular data.
What Is a Pointer? The Real Definition
A pointer is a variable that stores a memory address. That’s it. It points to where data is stored in memory.
When you write:
int x = 42;
int* ptr = &x;
Here’s what happens:
int x = 42;— creates an integer variable with value 42 at some address (let’s say address 1000)int* ptr = &x;— creates a pointer variable that holds the address ofx(soptrcontains 1000)
The syntax int* means “pointer to an int.” The asterisk is part of the type, not an operation.
The & Operator: Getting an Address
The & symbol is called the address-of operator. It gives you the memory address of a variable.
int x = 42;
std::cout << &x; // Prints something like: 0x7ffc7b6d4a2c
This prints the memory address where x is stored. It’s useful, but not usually the address itself—what matters is that &x gives you a way to store that address in a pointer.
int x = 42;
int* ptr = &x; // ptr now "points to" x
Now ptr holds the memory address of x. Think of ptr as having a label that says “I know where x is.”
The * Operator: Dereferencing a Pointer
Now you have a pointer. How do you get the value back? You use the * operator, called the dereference operator. It means “give me the value at this address.”
int x = 42;
int* ptr = &x; // ptr points to x
std::cout << *ptr; // Prints: 42
When you write *ptr, you’re saying “go to the address that ptr points to, and give me the value there.” So *ptr has the same value as x because they’re the same piece of data.
Here’s a practical example that shows both operators together:
int x = 42;
int* ptr = &x;
std::cout << x << std::endl; // Prints: 42 (direct access)
std::cout << *ptr << std::endl; // Prints: 42 (indirect access through pointer)
std::cout << &x << std::endl; // Prints: memory address (e.g., 0x1000)
std::cout << ptr << std::endl; // Prints: memory address (e.g., 0x1000)
Notice that &x and ptr print the same address because ptr holds the address of x.
Declaring Pointers: The Syntax
Here’s the correct way to declare pointers:
int* ptr; // Pointer to int (recommended style)
int *ptr; // Also correct, but less clear
int* ptr1, ptr2; // CAREFUL: only ptr1 is a pointer!
int *ptr1, *ptr2; // Both are pointers (safer style)
The tricky part is that int* ptr1, ptr2; doesn’t declare two pointers—only ptr1 is a pointer, and ptr2 is a regular int. This is why many programmers prefer int* ptr1; on separate lines for clarity.
You can declare pointers to any type:
double* dptr; // Pointer to double
char* cptr; // Pointer to char
std::string* sptr; // Pointer to string
MyClass* objptr; // Pointer to custom class
Null Pointers: The Special Case
What if you declare a pointer but don’t initialize it?
int* ptr; // What does ptr point to?
This is undefined behavior. ptr contains garbage—whatever random value was in that memory location before. If you try to dereference it, your program will crash.
That’s why we use null pointers. A null pointer is a pointer that explicitly points to nothing. There are two ways to create one:
int* ptr = nullptr; // Modern C++ (preferred)
int* ptr = NULL; // C-style (still works, but avoid)
int* ptr = 0; // Also works, but confusing
Always initialize pointers to nullptr if you’re not immediately pointing them to valid data:
int x = 42;
int* ptr = nullptr; // Safe default
if (ptr == nullptr) {
std::cout << "Pointer is null" << std::endl;
}
ptr = &x; // Now ptr points to something valid
std::cout << *ptr << std::endl; // Safe to dereference now
This prevents accidentally dereferencing a null pointer and crashing your program.
Pointers vs References: When to Use Each
C++ also has references, which are similar to pointers but safer. Many beginners confuse them. Here’s the key difference:
int x = 42;
// Pointer
int* ptr = &x; // Can be null, can be reassigned, requires dereferencing
*ptr = 100; // Must use * to access the value
// Reference
int& ref = x; // Cannot be null, cannot be reassigned, automatic dereferencing
ref = 100; // No * needed, works like the original variable
Key differences:
- A pointer can be null; a reference cannot
- A pointer can be reassigned to point to different variables; a reference cannot
- You must use
*to access data through a pointer; references feel like the original variable - References are safer and should be your default choice
Use pointers when you need:
- Dynamic memory allocation (with
newanddelete) - Functions that may need to modify data in calling code
- Data structures like linked lists or trees
- Arrays of unknown size at compile time
Use references when you want:
- Safe, automatic dereferencing (no
*needed) - To ensure the reference always points to valid data
- Function parameters that modify the caller’s data
For beginners: Prefer references whenever possible, and use pointers only when you specifically need pointer behavior.
Pointer Arithmetic: Moving Through Memory
Pointers support arithmetic operations. You can add or subtract integers from pointers:
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr; // Points to first element (arr[0])
std::cout << *ptr << std::endl; // Prints: 10 (arr[0])
std::cout << *(ptr + 1) << std::endl; // Prints: 20 (arr[1])
std::cout << *(ptr + 2) << std::endl; // Prints: 30 (arr[2])
ptr = ptr + 2; // Move pointer to arr[2]
std::cout << *ptr << std::endl; // Prints: 30
This works because when you add 1 to a pointer, it moves by the size of the type it points to. For int*, adding 1 moves the pointer by 4 bytes (the size of an int).
This is incredibly useful for working with arrays, but be careful:
int arr[] = {10, 20, 30};
int* ptr = arr;
ptr += 10; // Points way past the array (undefined behavior!)
std::cout << *ptr << std::endl; // DANGER: accessing invalid memory
Always ensure you stay within bounds.
Common Beginner Pointer Mistakes (And How to Avoid Them)
Mistake 1: Dereferencing Null or Uninitialized Pointers
int* ptr; // Uninitialized (garbage value)
std::cout << *ptr << std::endl; // CRASH!
// Fix:
int* ptr = nullptr;
int x = 42;
ptr = &x;
std::cout << *ptr << std::endl; // Safe
Mistake 2: Forgetting the & When Taking an Address
int x = 42;
int* ptr = x; // Wrong! x is an int, not an address
ptr = &x; // Correct
Mistake 3: Not Dereferencing When You Need To
int x = 42;
int* ptr = &x;
std::cout << ptr << std::endl; // Prints memory address
std::cout << *ptr << std::endl; // Prints 42
Mistake 4: Mixing Up Pointers and Arrays
Arrays are not the same as pointers, but they decay to pointers in most contexts:
int arr[] = {10, 20, 30};
int* ptr = arr; // OK: arr decays to a pointer to its first element
int* ptr2 = &arr; // Wrong type (pointer to the entire array)
int* ptr3 = &arr[0]; // Correct: pointer to first element
Practical Example: Passing Large Objects Efficiently
One of the most practical uses of pointers is avoiding expensive copying. Imagine you have a large data structure:
struct Person {
std::string name;
int age;
std::string email;
std::string address;
// ... many more fields
};
If you pass this by value, the entire struct is copied:
void printPerson(Person p) { // COPY of Person is made here
std::cout << p.name << std::endl;
}
Person alice = {"Alice", 30, "alice@example.com", "123 Main St"};
printPerson(alice); // Expensive copy operation
With a pointer, you pass just the address (8 bytes on 64-bit systems):
void printPerson(Person* p) { // Only the address (8 bytes) is passed
std::cout << p->name << std::endl; // Note: p->name is shorthand for (*p).name
}
Person alice = {"Alice", 30, "alice@example.com", "123 Main St"};
printPerson(&alice); // Just passes the address, no copy
Modern C++ usually prefers references for this:
void printPerson(const Person& p) { // Preferred: reference, const for safety
std::cout << p.name << std::endl;
}
printPerson(alice); // Clean syntax
When to Use Pointers in Modern C++
With modern C++ (C++11 and later), pointers are used less than they used to be. Here’s when you actually need them:
-
Dynamic allocation: Creating objects whose size is unknown at compile time
int n = getUserInput(); int* arr = new int[n]; // Size determined at runtime delete[] arr; -
Data structures: Building linked lists, trees, graphs
struct Node { int value; Node* next; }; -
Callbacks and function pointers: Advanced topic, but sometimes necessary
-
Smart pointers: Modern replacement for raw pointers
std::unique_ptr<int> ptr(new int(42)); // Automatic cleanup
For most cases, prefer:
- References for passing data to functions
- Smart pointers (
std::unique_ptr,std::shared_ptr) for dynamic memory - STL containers (
std::vector,std::map) instead of manual pointer arrays
Conclusion: You Now Understand Pointers
Pointers aren’t magic—they’re just variables that hold memory addresses. Once you understand that mental model, the syntax makes sense. You can now:
- Declare pointers correctly
- Use
&to get an address - Use
*to access the value - Avoid null pointer crashes
- Choose between pointers and references
- Use pointers for efficient passing of large objects
The next step is practice. Write small programs that create pointers, pass them to functions, and access values through them. Once it becomes muscle memory, you’ll be ready for more advanced topics like dynamic memory allocation and data structures.
Ready to master all of C++? Check out our complete C++ course for in-depth explanations, practical projects, and guided learning from fundamentals to advanced topics.
Take Your C++ Further
If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.
👉 Get the C++ Better Explained Ebook — $19
Related Articles
- Smart Pointers in Modern C++: unique_ptr, shared_ptr, and weak_ptr Explained — once you understand raw pointers, upgrade to smart pointers to write safer, modern C++.
- Memory Management in C++: Heap vs Stack, new/delete, and How to Prevent Memory Leaks — pointers and memory management go hand in hand; learn how the heap and stack actually work.
- C++ Cheat Sheet: Quick Reference for Syntax, STL, and OOP — a quick-reference card covering pointer syntax alongside the rest of C++.
- Top 50 C++ Interview Questions and Answers — pointers appear in nearly every C++ technical interview; test yourself here.