Skip to content
C++ Better Explained
Go back
How to Use Pointers in C++: A Complete Beginner's Guide
Edit page

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:

When you write this in C++:

int x = 42;

The computer does something like this:

  1. Finds an empty “house” in memory
  2. Puts the number 42 inside that house
  3. 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:

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:

Use pointers when you need:

Use references when you want:

For beginners: Prefer references whenever possible, and use pointers only when you specifically need pointer behavior.

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.

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:

  1. 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;
  2. Data structures: Building linked lists, trees, graphs

    struct Node {
        int value;
        Node* next;
    };
  3. Callbacks and function pointers: Advanced topic, but sometimes necessary

  4. Smart pointers: Modern replacement for raw pointers

    std::unique_ptr<int> ptr(new int(42));  // Automatic cleanup

For most cases, prefer:

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:

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


📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Edit page
Share this post on:

Previous Post
Object-Oriented Programming in C++: Classes & Objects
Next Post
C++ Smart Pointers: unique_ptr & shared_ptr Guide