Pointers in C and C++ explained simply

Pointers have a slightly intimidating reputation. Many people learning C or C++ get stuck on them, give up, then come back a few months later only to realise it wasn't so bad after all. The truth fits in one sentence: a pointer rests on a single, simple idea. Instead of working with a value, you work with the place where that value is stored. Everything else follows logically from that principle.

A variable, a box, an address

When you declare a variable, the computer sets aside a small area in memory to hold its value. You can picture memory as a long street of buildings, where every box carries a unique number: its address. The variable is just a label placed on one of those boxes, so you don't have to remember the number yourself.

A pointer is simply a variable that holds that number rather than an ordinary value. Where an integer stores the number 42, a pointer stores the address of the place where, say, that very 42 lives. If you have already read our guide on writing your first C++ program, you are halfway there: a pointer is still a variable, just with a slightly special job.

Declaring a pointer and reading what it points to

In C, two symbols do almost all the work. The star * declares a pointer and accesses the value it points to; the ampersand & retrieves the address of an existing variable.

int age = 42;
int *p = &age;        // p holds the address of age

printf("%d\n", *p);   // prints 42, the pointed-to value
*p = 43;              // we change age through the pointer
printf("%d\n", age);  // prints 43

Here *p reads as "the value stored at the address held in p". This is called dereferencing. The key point: changing *p changes age directly, because both refer to the same memory box. That is exactly what makes pointers so useful.

What pointers are really for

It might look like needless complexity. In practice, pointers solve very concrete problems. They let a function modify a variable passed to it instead of receiving a mere copy. They avoid copying large blocks of data: you pass an address, a few bytes, rather than a whole array. And they make possible the data structures that grow and shrink while the program runs, such as linked lists and trees. If you are just starting out, our walkthrough of a first program in C++ covers the basics first.

Dynamic memory allocation

So far, our variables had a size known in advance. But what do you do when you can't know, while writing the code, how many items you will need to store? The answer is dynamic allocation: you ask for memory while the program runs, then give it back once you are done. In C, you use malloc to reserve and free to release.

int *array = malloc(10 * sizeof(int));  // 10 integers
if (array != NULL) {
    array[0] = 1;
    // ... use it ...
    free(array);   // give the memory back
}

In C++, you have new and delete, which do the same thing while respecting types more closely.

int *value = new int(42);
delete value;

The golden rule fits in one line: every allocation must have a matching release. Forgetting the free or the delete means leaving memory tied up for nothing.

The pitfalls that scare people, and how to avoid them

The bad reputation of pointers comes mostly from three classic mistakes. The first is the memory leak: you allocate without ever releasing, and the program nibbles away at memory until it runs out. The second is the dangling pointer, which refers to an already-freed area; touching it gives unpredictable results. The third is the null pointer you dereference without checking, which triggers the infamous segmentation fault.

The good news is that these traps are defused with simple habits. Always initialise a pointer, even if only to NULL. Check that it isn't null before using it. And after a free, set the pointer back to NULL so you don't reuse it by accident. These reflexes quickly become second nature.

What modern C++ changes

Recent C++ has softened all of this considerably with smart pointers. A std::unique_ptr or a std::shared_ptr releases the memory automatically as soon as it is no longer needed, which wipes out a large share of leaks in one go.

#include <memory>

std::unique_ptr<int> value = std::make_unique<int>(42);
// automatic release: no delete to write

Even so, understanding raw pointers stays essential: smart pointers are built on top of them, and existing code is full of them. You always gain from knowing both.

Going further

Pointers aren't tamed in a single read, but through practice. Write small programs, draw memory on paper, follow each address by hand. In a few days, what felt obscure becomes a reflex. Take your time, experiment, and the concept settles in on its own.

Recent Posts

Recent Comments

"

Itamde is also an online programming school.

Itamde

Learn what you want, at your own pace

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

You may also be interested in...

The essential SEO plugins for WordPress

The essential SEO plugins for WordPress

The SEO plugins that actually matter for WordPress โ€” SEO suite, cache, images, redirects โ€” and how to combine them without slowing your site.

Stay up to date with the latest news and developments

Access restricted content

Discover behind-the-scenes details of our projects, exclusive resources, and the progress of our creations in real time.

Sign up for our newsletter

Receive our news, creative insights, and updates from the studio directly in your inbox.

Follow us

Join our community on social media to follow our daily projects and interact with us.