root@coding-prodigies:~# โ–Š
// lesson 2 of 12 ยท 18 min

Pointers and references

โš‘ Report an issue with this lesson
int value = 42;
int* ptr = &value;      // ptr holds the address of value
int& ref = value;         // ref is an alias for value

*ptr = 10;    // changes value through the pointer
ref = 20;       // changes value through the reference
std::cout << value; // 20

A pointer can be reassigned to point elsewhere, or set to nullptr. A reference is bound permanently to one variable at creation and can never be null -- prefer references when you don't need that flexibility, since they're harder to misuse.

Pointer arithmetic is one of the things that separates C++ from most higher-level languages: incrementing a pointer moves it forward by sizeof(T) bytes, not one byte, which is what makes it work correctly with arrays of any element type:

int arr[] = {10, 20, 30};
int* p = arr;        // arrays decay to a pointer to their first element
std::cout << *p;       // 10
p++;                    // now points at arr[1], not arr[0] + 1 byte
std::cout << *p;       // 20
std::cout << p[1];     // 30 -- p[i] is shorthand for *(p + i)

const interacts with pointers in two independent ways, and it's worth being able to read the declaration precisely -- read it right to left from the variable name:

const int* a;       // pointer to const int: can't modify *a, but a can point elsewhere
int* const b = &x;   // const pointer to int: can modify *b, but b can never repoint
const int* const c = &x; // both: neither *c nor c itself can change

A dangling pointer -- one that still holds an address whose object has already been destroyed -- is one of the most common sources of undefined behavior in C++. Returning the address of a local variable is the classic mistake:

int* danger() {
    int local = 42;
    return &local; // local's storage is gone the instant the function returns
} // calling code that dereferences the result reads garbage, or worse

The compiler will often warn about this specific case, but the same bug shows up less obviously with pointers into containers that get reallocated (a vector growing past its capacity invalidates pointers/iterators into it) or with a unique_ptr being deleted while a raw pointer obtained from .get() is still in use elsewhere. When in doubt about whether a raw pointer might be dangling, prefer a smart pointer or a reference with a clearly bounded lifetime instead -- the next lesson covers exactly that.

A pointer can also point to a pointer (int**), which shows up when a function needs to modify what a caller's pointer points to, or in old-style C APIs (like argv-style string arrays, char**). It's rare in idiomatic modern C++, but you'll still encounter it reading lower-level or interop code.

Try it yourself

Exercise: Declare int value = 42, a pointer ptr pointing to value, and a reference ref bound to value. Use ptr to change value to 10, then use ref to change value to 20, then print value.
Expected output:
20
c++
Output

      
    

Run your code and get it working before marking this lesson complete.

// free preview

9 more lessons โ€” including Project: build a thread-safe task queue โ€” and a final exam plus a certificate are waiting.

Unlock the full course โ€” $99.99