- 1. Pointer Size and Architecture
- 2. Dereferencing
- 3. Null Pointers
- 4. Wild Pointers
- 5. Dangling Pointers
- 6. Memory Safety
- 7. See Also
This doc covers raw pointers and the bugs they enable. For ownership semantics and modern memory management see smart_pointers.md. For aliases that share an address with another variable see references.md.
A pointer's size depends on the CPU's address width, not on what it points to:
- 32-bit machine: every pointer is 4 bytes.
- 64-bit machine: every pointer is 8 bytes.
std::cout << sizeof(char*) << '\n'; // 8 on x86-64
std::cout << sizeof(int*) << '\n'; // 8
std::cout << sizeof(double*) << '\n'; // 8
std::cout << sizeof(void*) << '\n'; // 8Dereferencing means "follow the address and access the value there." Three equivalent ways to read the second character of a string:
const char* p = "abc";
char c1 = *(p + 1); // pointer arithmetic, then dereference
char c2 = p[1]; // sugar for *(p + 1)
char c3 = *++p; // advance p first, then dereference ('b')p + 1 advances the address by sizeof(*p) bytes — 1 for char, 4 for int*, etc.
A null pointer is a sentinel value meaning "doesn't point at anything." Dereferencing it is undefined behavior (typically a segfault).
int* p = nullptr;
if (p) { /* skipped */ }NULL is a macro that expands to 0 (or (void*)0 in C). Pre-C++11, NULL was the idiomatic null pointer constant. It's a problem in overload resolution:
void f(int);
void f(char*);
f(NULL); // ⚠️ ambiguous on some compilers — NULL is just 0, which is an int
f(nullptr); // ✅ unambiguous — nullptr has type std::nullptr_tnullptr (C++11) has its own type std::nullptr_t, implicitly convertible to any pointer type but not to integer types. Always prefer nullptr in C++.
A wild pointer is one that was never initialized. Reading or writing through it accesses whatever happened to be on the stack at that address — usually garbage, sometimes a valid object you didn't mean to touch.
int* p; // ⚠️ wild — points wherever
*p = 10; // undefined behavior: crash, silent corruption, or "works"Always initialize:
int* p = nullptr;Then any accidental dereference fails loudly (segfault) instead of silently corrupting memory.
A dangling pointer is a non-null pointer to memory that has already been freed.
Heap case:
Object* a = new Object();
Object* b = a; // b also points at the heap object
delete a;
a = nullptr; // good: a is now safe to test
// bad: b is dangling and we have no way to knowStack case — returning the address of a local:
Object* make() {
Object o;
return &o; // ⚠️ o is destroyed when make() returns
}
Object* p = make();
p->foo(); // undefined behaviorModern compilers warn on this with -Wreturn-stack-address.
The structural fix is to use ownership-aware types — see smart_pointers.md. std::weak_ptr in particular gives a way to detect a dangling reference at runtime instead of guessing.
A segmentation fault means the program touched a page the kernel didn't grant it. Typical causes:
- Dereferencing a null, wild, or dangling pointer.
- Writing to read-only memory (string literals, the code segment).
- Reading or writing past the end of an allocation (heap or stack overflow).
- Calling through a freed function pointer or vtable.
Note that C/C++ don't bounds-check array indices. An out-of-range write that happens to land on a still-mapped page won't crash — it'll silently corrupt data and surface much later.
-fsanitize=address (ASan) instruments every load/store and reports use-after-free, buffer overflow, leaks, and stack overflow with full stack traces. Turn it on for debug builds:
target_compile_options(my_target PRIVATE -fsanitize=address -fno-omit-frame-pointer)
target_link_options (my_target PRIVATE -fsanitize=address)Or globally:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")Slowdown is ~2× — perfectly fine for tests. Pair with -fsanitize=undefined (UBSan) to catch undefined-behavior bugs that don't trigger a segfault.
- smart_pointers.md —
unique_ptr,shared_ptr,weak_ptr, custom deleters, breaking cycles. - shared_ptr_use_cases.md — twelve concrete patterns where shared ownership is the right tool.
- passing_returning_smart_pointers_to_from_functions.md — function-signature rules.
- smart_pointers_class_member.md — owning resources from class members.
- references.md — references, lvalue/rvalue,
reference_wrapper,std::ref/std::cref.