Skip to content
C++

Pointers and References Quick Reference

C++ pointers and references cheatsheet — raw pointers, references, pointer arithmetic, const correctness, nullptr, and smart pointer overview.

Raw Pointers

cpp
int  x  = 42;
int* p  = &x;      // pointer to int
*p = 99;           // dereference: x is now 99
int** pp = &p;     // pointer to pointer

// Null checks
int* q  = nullptr; // null pointer (C++11)
if (p != nullptr) { }
if (p)            { }  // same
if (!q)           { }  // null check

// Pointer arithmetic (only valid within arrays)
int arr[5] = {10, 20, 30, 40, 50};
int* it = arr;     // points to arr[0]
*(it + 2);         // arr[2] = 30
it++;              // it now points to arr[1]
ptrdiff_t diff = it - arr;  // 1

const and Pointers

cpp
int val = 42;

int*       p1 = &val;   // mutable pointer, mutable pointee
const int* p2 = &val;   // mutable pointer, const pointee (can't write *p2)
int* const p3 = &val;   // const pointer, mutable pointee (can't rebind p3)
const int* const p4 = &val;  // const pointer, const pointee

*p2 = 0;   // error: read-only through p2
p2  = nullptr;  // OK: pointer itself is mutable

*p3 = 0;   // OK: pointee is mutable
p3  = nullptr;  // error: pointer is const

// Mnemonic: read right-to-left
// const int*  → "pointer to const int"
// int* const  → "const pointer to int"

References

cpp
int x = 5;
int& r = x;      // reference: alias for x — must bind at init
r = 10;          // x is now 10
&r == &x;        // true — same address

// No null references, no rebinding
// int& r2;      // error: must initialize
// r = y;        // assigns to x, doesn't rebind r

// const reference — binds to rvalues and temporaries
const int& cr = 42;   // OK: lifetime extended
cr = 0;               // error

// rvalue reference
int&& rr = 42;        // binds rvalue
int&& rr2 = std::move(x);  // binds moved x

Pointer vs Reference

PointerReference
NullableYes (nullptr)No
RebindableYesNo
Syntax*p, p->mr, r.m
ArithmeticYesNo
ArraysYesNo (use span)
Optional paramYesNo (use optional)
Prefer whenNullable/rebind neededNon-nullable alias

Function Pointers

cpp
int add(int a, int b) { return a + b; }

// Declare
int (*fp)(int, int) = add;
fp(3, 4);           // 7

// Using alias (clearer)
using BinOp = int(*)(int, int);
BinOp op = add;

// Array of function pointers
BinOp ops[] = {add, [](int a, int b){ return a - b; }};
ops[0](3, 4);  // 7
ops[1](3, 4);  // -1

Pointer to Array and Array of Pointers

cpp
int arr[5];

int*  p  = arr;     // pointer to first element (decays from array)
int (*pa)[5] = &arr; // pointer to array of 5 ints
(*pa)[2] = 99;      // same as arr[2] = 99

// Array of pointers
int a = 1, b = 2, c = 3;
int* ptrs[3] = {&a, &b, &c};
*ptrs[1] = 99;      // b = 99

new / delete (avoid in modern C++)

cpp
// Prefer make_unique/make_shared
int* p = new int{42};
delete p;

int* arr = new int[10]{};
delete[] arr;        // MUST use delete[] for arrays

// Placement new
alignas(int) std::byte buf[sizeof(int)];
int* p2 = ::new(buf) int{7};
p2->~int();          // must destroy explicitly

Smart Pointers (Prefer These)

cpp
#include <memory>

// unique_ptr — exclusive ownership, zero overhead
auto up = std::make_unique<Widget>(args...);
up->do_work();
// freed when up goes out of scope

// shared_ptr — shared ownership, ref-counted
auto sp1 = std::make_shared<Widget>();
auto sp2 = sp1;     // ref count = 2

// weak_ptr — non-owning observer
std::weak_ptr<Widget> wp = sp1;
if (auto locked = wp.lock()) {
    locked->do_work();
}

// unique_ptr for arrays
auto arr = std::make_unique<int[]>(10);
arr[3] = 42;

void* and Casts

cpp
void* vp = &x;            // holds any pointer
int* ip = static_cast<int*>(vp);   // must cast back to use

// reinterpret_cast — low-level type pun (use std::bit_cast instead in C++20)
float f = 3.14f;
int bits = std::bit_cast<int>(f);  // preferred (C++20)

// C-style cast — avoid
int* p3 = (int*)vp;   // avoid: doesn't check safety

Common Mistakes

cpp
// 1. Dangling pointer
int* bad_ptr;
{
    int local = 5;
    bad_ptr = &local;
}
*bad_ptr;   // UB: local destroyed

// 2. Uninitialized pointer
int* p;     // indeterminate value — don't read
*p = 0;     // UB

// 3. Off-by-one in pointer arithmetic
int arr[5];
int* end = arr + 5;  // OK: one past end is valid (don't dereference)
*end = 0;            // UB: one past end

// 4. Signed/unsigned mismatch in pointer arithmetic
size_t n = 5;
ptrdiff_t diff = end - arr;  // ptrdiff_t, not size_t

// 5. delete non-heap pointer
int stack_var = 5;
int* p2 = &stack_var;
delete p2;   // UB: not heap allocated
Edit on GitHub