Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Understanding Pointers and Heap Memory | Introduction to Smart Pointers
C++ Smart Pointers
course content

Contenido del Curso

C++ Smart Pointers

C++ Smart Pointers

1. Introduction to Smart Pointers
2. Unique Pointers
3. Shared Pointers
4. Weak Pointers
5. References
6. Advanced topics

bookUnderstanding Pointers and Heap Memory

What is a pointer

Pointers are an essential concept in the world of C++. Instead of storing actual data, pointers store memory addresses, allowing developers to efficiently access and manipulate various parts of the memory.

Note

You can think of pointers as GPS coordinates. They point us to specific locations in the memory, and allow us to access data stored in those locations.

When you declare a pointer, you create a variable that holds the memory address of another variable. For example, you can create a pointer to an integer like this:

  • ptr holds the memory address of the integer myValue.
  • The & sign is used to get the memory address of the variable it’s used with.
  • &myValue retrieves the memory address of the myValue variable, which is then stored in the ptr pointer.

Null pointers

In C++, null or nullptr are special keywords used to represent a null pointer. A null pointer *doesn't point to any valid memory address. It's essentially a pointer with no target.

Note

We can also initialize the integer pointer to null (or nullptr). We do this when we don't want to associate the pointer with a specific value right away.

Reassigning pointers

Pointers can be reassigned:

Pointer arithmetic

Pointer arithmetic is a fascinating aspect of pointers. It allows you to traverse memory by incrementing or decrementing the address held by a pointer.

For example, consider the following code where we create an array of integers and then define a pointer to hold the address of the array. Since an array contains multiple elements, a pointer, by default, stores the address of the first element, which in this case is the integer 1.

To access the second element, we can simply increment the pointer by 1. Then, we finally dereference it using the (*) operator. Derefencing returns the value present at the memory address held by the pointer (the integer 2 in our case). Follow the comments in the above code to know what’s happening!

Dynamic memory allocation

There are two kinds of memory available for use in a C++ program: stack and heap. Stack memory is limited in size. It’s well-suited for small, short-lived variables that aren’t needed beyond their scope. When a variable on the stack goes out of scope, it’s automatically destroyed.

However, when you need memory that persists longer than its scope, or requires more space, you turn to the heap. The heap is a much larger pool of memory where your program can request memory at runtime. This is typically done using the new keyword:

Here, we've allocated memory for an integer on the heap and stored its address in a pointer. The tricky thing about heap memory is that it’s not automatically released. It’s the developer’s responsibility to deallocate it when they are done. For every new, there must be a corresponding delete.

Memory leaks and dangling pointers

When you introduce pointers into your C++ code, you enter the realm of manual memory management. Manual memory management requires you to release the memory when it's no longer needed. It can lead to common pitfalls: memory leaks and dangling pointers.

Memory leaks

Memory leaks typically occur when you forget to deallocate memory. Such memory remains reserved, causing your program to lose memory over time.

Example

You create dynamic objects in a loop, but never delete them, they will lead to a memory leak.

Dangling pointers

Dangling pointers occur when a pointer continues to point to a memory that’s been deallocated. It's like having a treasure map that leads you to a place (memory address) where the treasure (data) has already been taken (destroyed).

cpp

main

copy
12345678910111213
#include <iostream> int main() { int* ptr = new int; // dynamically allocate an integer *ptr = 42; // assign a value to the allocated memory std::cout << *ptr << std::endl; // This will output 42 delete ptr; // deallocate the memory std::cout << *ptr << std::endl; // ptr is a dangling pointer! }

In the above code, we try to access a pointer after calling delete on it, which deallocates the memory it was pointing to. After deletion, the pointer becomes a dangling pointer, as it still holds the address to a memory that no longer exists.

Accessing a dangling pointer can lead to unpredictable behavior or even crashes, as the memory it points to is no longer valid. To avoid dangling pointers, it’s important to set a pointer to “null” after calling delete on it. Then, before accessing the value of that pointer, it’s imperative to check whether it’s null or not. Consider the following code:

cpp

main

copy
1234567891011121314151617181920212223
#include <iostream> int main() { // Dynamically allocate an integer int* ptr = new int; // Assign a value to the allocated memory *ptr = 42; // Output the value, which will be 42 std::cout << *ptr << std::endl; // Free the allocated memory to avoid memory leaks delete ptr; // Set the pointer to null ptr = nullptr; // Check whether the pointer is not null if (ptr != nullptr) // Access and output the value, which won't execute std::cout << *ptr << std::endl; }
Does the following code have a memory leak?

Does the following code have a memory leak?

Selecciona la respuesta correcta

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 1. Capítulo 1
some-alt