A pointer in C++ is a variable that stores the address of another object. Two operators do all the work: [[&]] takes the address of something, and [[*]] dereferences a pointer to reach the thing it points at. Once those two ideas are clear, pointers stop being mysterious and become the tool for building linked structures such as a playlist.
Addresses and the address-of operator
Every variable lives somewhere in memory, and the compiler gives it an address. The & operator retrieves that address.
#include
int main() {
int track_count = 12;
int* pointer_to_count = &track_count;
std::cout << "value: " << track_count << '\n';
std::cout << "address: " << &track_count << '\n';
std::cout << "through the pointer: " << *pointer_to_count << '\n';
return 0;
}Reading the three lines: the value of track_count is 12, the address of track_count is some number the operating system chose, and dereferencing the pointer gives 12 back because the pointer points at that variable.
The dereference operator
* is the inverse of &. Where &x gives you the address of x, *p gives you the object at the address stored in p. The two cancel out.
#include
int main() {
double duration = 214.5;
double* duration_pointer = &duration;
*duration_pointer = 180.0; // write through the pointer
std::cout << "duration: " << duration << '\n';
std::cout << "through pointer: " << *duration_pointer << '\n';
*duration_pointer += 20.0; // read, modify, write back
std::cout << "after adding: " << duration << '\n';
return 0;
}The output confirms that writing through the pointer changed the original variable:
duration: 180
through pointer: 180
after adding: 200Declaring pointers
The * goes next to the type name, which tells you the pointer points at something of that type.
#include
int main() {
int number = 42;
int* int_pointer = &number; // pointer to an int
double* double_pointer = nullptr; // pointer to a double, currently empty
std::cout << "number: " << *int_pointer << '\n';
*int_pointer = 7;
std::cout << "after write: " << number << '\n';
if (double_pointer == nullptr) {
std::cout << "double pointer is empty\n";
}
return 0;
}A pointer of type int* can only point at an int. If you try to point an int* at a double, the compiler refuses. That type checking is one of the main reasons pointers are safe enough to use at all.
Pointers and arrays are closely related
An array name in most expressions behaves like a pointer to its first element. That is why indexing an array and dereferencing a pointer look the same.
#include
int main() {
int ratings[3] = {4, 5, 3};
int* first = ratings;
std::cout << "ratings[0]: " << ratings[0] << '\n';
std::cout << "*first: " << *first << '\n';
std::cout << "same?: " << (first == &ratings[0]) << '\n';
first = &ratings[2];
std::cout << "after move: " << *first << '\n';
return 0;
}The output shows the two forms are interchangeable:
ratings[0]: 4
*first: 4
same?: 1
after move: 3Pointers to pointers
A pointer is an object, so it can be pointed at. A pointer to a pointer is how you pass a pointer to a function so that the function can change which object the original pointer refers to.
#include
void swap_pointers(int*& first, int*& second) {
int* temporary = first;
first = second;
second = temporary;
}
int main() {
int a = 4;
int b = 5;
int* first = &a;
int* second = &b;
std::cout << "before: " << *first << " and " << *second << '\n';
swap_pointers(first, second);
std::cout << "after: " << *first << " and " << *second << '\n';
return 0;
}Note the int*& in the parameter: a reference to a pointer. The output shows the swap worked:
before: 4 and 5
after: 5 and 4Without the &, the function would only be able to change its own copy of the pointer.
What pointers are for
Pointers earn their keep in three situations.
- Dynamic memory, where the size of an object is not known until runtime. That is the subject of the memory module later in this tutorial.
- Linked structures, where each element points at the next one. A playlist is the classic example.
- Optional parameters, where
nullptrmeans "no value supplied".
Here is the beginning of a linked playlist. Each track holds its duration and a pointer to the next track.
#include
#include
struct Track {
std::string title;
int seconds;
Track* next;
};
int main() {
Track second = {"Blue Skies", 187, nullptr};
Track first = {"Slow Tide", 243, &second};
for (Track* current = &first; current != nullptr; current = current->next) {
std::cout << current->title << " (" << current->seconds << "s)\n";
}
return 0;
}The arrow operator -> is shorthand for dereferencing then selecting a member, so current->next means (*current).next.
Pointers to const and const pointers
The const qualifier combines with pointers in two different ways, and the position matters.
#include
int main() {
int value = 42;
const int* read_only = &value; // pointer to a const int
int* const fixed = &value; // a const pointer to an int
std::cout << "value: " << *read_only << '\n';
std::cout << "value: " << *fixed << '\n';
*fixed = 99; // allowed: the pointer is fixed, the int is not
std::cout << "after write: " << value << '\n';
return 0;
}Reading the declarations out loud settles them: a const pointer to int is a pointer that cannot be reseated but whose target can be modified, while a pointer to const int is a pointer that can be reseated but whose target cannot be modified. The output shows the write through the fixed pointer:
value: 42
value: 42
after write: 99Quick reference
&xgives the address ofx;*pgives the object the pointerppoints at.- A pointer is declared with a
*next to the type:int* p. - Writing through a pointer changes the original object.
- An array name behaves like a pointer to its first element in most expressions.
- A pointer to a pointer lets a function change which object a pointer refers to.
- Use
->to reach a member through a pointer.
Try it yourself: write a program that stores three track durations in an array, then writes a small function that takes an [[int*]] and doubles every value in place. Call it with the array and print the results. Next, extend the linked playlist above to three tracks and confirm the loop prints all three in order.