So far, we have read and changed data through variable names, such as changing data from 3 to 5. While a program runs, its data lives in memory. To understand pointers, start with a simpler question: where is a variable's value stored?
A variable's value and location
Think of memory as a row of numbered storage slots. A slot holds data, and its number tells us where to find it. Distinguish these three ideas:
- Name: the name used in code, such as
data. - Value: the contents of the variable, such as the integer
3. - Address: the variable's location in memory.
Changing a variable's value does not mean moving it elsewhere. Changing data from 3 to 5 changes the contents at the same location.
Finding a variable by its address
A name lets us find a variable; its address lets us find it too. In code, the address-of operator & obtains a variable's address. &data means “where data is stored,” not “what value data contains.”
Memory addresses are often written in hexadecimal, such as 0x1000. The addresses in this section's diagrams are illustrative labels. Actual addresses depend on the runtime environment; there is no need to memorize these numbers.
Storing an address in a variable
If we save an address, we can follow it later to reach the original variable. A variable that stores an address is called a pointer variable, or simply a pointer.
int *p = &data; creates a pointer named p and stores the address of data in it. int * means this pointer points to an integer variable. We call this relationship “p points to data.”
Importantly, p stores an address, not a copy of the integer 3. The pointer is itself a variable with its own location: &p is the pointer variable's address, which is different from the &data it contains.
Reading and writing through an address
Once p points to a variable, *p accesses that variable. This operation is called dereferencing:
- Reading
pgives the address it stores. - Reading
*pgives the value of the pointed-to variable. - Executing
*p = 5;changes the pointed-to value, not where the pointer points.
In a declaration, * indicates a pointer. In an expression, *p means “follow the address stored in p and access the variable.” The following complete program puts these operations together:
#include <iostream>
int main() {
int data = 3;
int *p = &data;
std::cout << "data = " << data << '\n';
std::cout << "&data = " << &data << '\n';
std::cout << "p = " << p << '\n';
std::cout << "*p = " << *p << '\n';
*p = 5;
std::cout << "data = " << data << '\n';
return 0;
}Address and dereference memory map
Unlock the complete interactive lab, deeper explanations, and curated reference answers.
KNOWLEDGE QUIZ
KNOWLEDGE QUIZ
Null pointers and safety checks
A pointer may temporarily point to no variable. Initialize it explicitly to a null pointer in that case. A null pointer lets us test whether a target exists, but it must not be dereferenced.
An uninitialized pointer is not automatically null; its value is indeterminate. Initialize a pointer to a valid object or to null, and check for null before reading or writing through it.
int *q = nullptr;
if (q != nullptr) {
*q = 5;
}KNOWLEDGE QUIZ
Passing addresses to functions
Ordinary parameters copy values, so swapping two integer copies inside a function does not change the caller's variables. To modify the originals, pass their addresses.
After calling swap(&x, &y), parameter a stores the address of x, and b stores the address of y. Accessing *a and *b reaches the original variables in main. The swap changes the two integer values, not where the pointers point.
#include <iostream>
using namespace std;
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 3;
int y = 5;
swap(&x, &y);
cout << x << ' ' << y << endl; // 5 3
return 0;
}Swap in three steps: follow a and b to x and y
Unlock the complete interactive lab, deeper explanations, and curated reference answers.
Set a value to zero through a pointer
Write set_to_zero(int *value) to change the caller's integer to 0. Test with score = 42 in main and print the result.
- Define clear_value with an integer pointer parameter.
- After checking the pointer, assign zero through *value.
- Pass a variable's address from main and print before and after.
Order a pair of integers
Write order_pair(int *a, int *b). Swap only when *a is greater than *b. Test with 9 and 4; the output must be 4 9.
- Accept the addresses of two integers.
- Swap the dereferenced values only when the first is larger.
- Test both already-sorted and unsorted inputs in main.
Safe increment
Write safe_increment: return false for a null pointer; otherwise increment the target and return true. Call it with both a valid address and nullptr.
- Accept an integer pointer and check it first.
- Execute (*value)++ only for a valid pointer.
- Return a success/failure status and test both cases.