Access Violation 0xC0000005: What It Means and How to Find the Cause

The most common Windows exception code. Here's how to read the target address, register state, and stack to determine what actually went wrong.

If you've worked with C++ on Windows for any length of time, you've seen 0xC0000005. It's STATUS_ACCESS_VIOLATION the NTSTATUS code Windows raises when a program attempts to access a memory address it doesn't have permission to read or write. It is by far the most common exception code in crash dumps.

What the code doesn't tell you is why the access was invalid, and that's where the real diagnostic work begins. An access violation can be a null pointer dereference, a use-after-free, a buffer overflow, a race condition, or a stack corruption. Each has different causes and different fixes. Telling them apart requires reading the crash data carefully rather than just noting the exception code.

Read vs. write: the first distinction

Every access violation has a type: read or write. This is shown in the Exception tab in CrashCatch and in WinDbg's output.

  • A read violation means the program tried to read from an address it couldn't access. The most common cause is a null or dangling pointer being dereferenced.
  • A write violation means the program tried to write to an address it couldn't access. This often indicates a buffer overflow, writing to freed memory, or writing to a read-only region.

Write violations to addresses in your own heap particularly to addresses that look suspiciously like valid heap addresses are worth treating as potential heap corruption. Read violations at 0x0 or small non-zero offsets are almost always null pointer dereferences.

Reading the target address

The target address (the address that was being accessed when the fault occurred) is the single most useful piece of information for classifying an access violation. Here's how to read it:

Target address: 0x0000000000000000

Classic null pointer dereference. The code attempted to dereference a pointer that was null. Look at the faulting instruction and the frame that owns it you're looking for a pointer that wasn't initialized or wasn't checked before use.

Target address: small non-zero offset (0x8, 0x10, 0x18, 0x20...)

Almost certainly a null pointer dereference through a struct or class. The code is accessing a member of an object, but the object pointer is null. The offset corresponds to the byte offset of that member within the struct. For example, a target address of 0x18 on a class with a pointer at offset 0x18 means the this-pointer was null when accessing that member.

Exception detail
Exception code:    0xC0000005  ACCESS_VIOLATION
Type:              read
Fault address:     0x00007FF6A3C28E1
Target address:    0x0000000000000018

→ Accessing member at offset 0x18 on a null pointer.
→ Check the owning object before accessing this member.

Target address: looks like a valid heap address but isn't

This pattern a non-null address in the range of your heap that causes an access violation is the signature of a use-after-free or dangling pointer. The object was valid at some point, but the memory has since been freed and possibly reallocated. Accesses to it may succeed silently (if the memory was reallocated with compatible data) or crash with an access violation (if the page was decommitted).

Target address: debug heap sentinel values

In debug builds compiled with MSVC, the debug heap fills freed memory with known patterns. If your target address contains these values, the pointer came directly from freed memory:

  • 0xDDDDDDDDDDDDDDDD — freed heap memory (debug CRT)
  • 0xFEEEFEEEFEEEFEEE — freed heap memory (HeapFree)
  • 0xCDCDCDCDCDCDCDCD — uninitialized heap memory (debug CRT)

Seeing these in a target address in a debug build is definitive: you have a dangling pointer. The fix is finding where the object is being freed while still being referenced.

Target address: large, random-looking value

A target address that looks like an arbitrary large number nothing recognizable — usually means a corrupted pointer. The pointer variable itself has been overwritten with garbage, either by a buffer overflow in a nearby variable, a race condition writing to the pointer location, or a stack corruption propagating from a previous function.

Using the register state

The CPU registers captured at crash time can help identify which variable or argument was the bad pointer. On x64 Windows, function arguments are passed in specific registers: RCX (first argument), RDX (second), R8 (third), R9 (fourth). If the crash is in a function entry or early in a function body, the argument registers may still hold their call-site values.

Look at RAX specifically on x64, RAX is the most commonly used accumulator and is often the register that holds the pointer being dereferenced. If RAX is 0x0 and the faulting instruction is mov rcx, [rax+18h], that's a null pointer at rax. If RAX is 0xDDDDDDDD, it's a freed pointer.

Stack trace patterns for each cause

The stack frame that owns the fault address is the most important frame it's where execution was at the moment of the crash. But the frames above it tell you how you got there, which is often equally important:

  • Null pointer dereference the fault frame is typically deep inside a method on a class that should have been checked before use. Look one or two frames up from the fault for the call site that passed the bad pointer in.
  • Use-after-free the fault frame may be in a destructor or in code that runs during cleanup. Look for a pattern where the same object is being accessed from multiple code paths, one of which frees it.
  • Buffer overflow the fault frame is often in unrelated code that happens to be adjacent in memory. The actual corruption happened earlier. Look for array writes without bounds checks near the crash timeline in the log.
  • Stack corruption the stack itself is damaged. Stack overflow (0xC00000FD) is a separate exception code, but a corrupted stack can manifest as an access violation if the return address or a stack variable is overwritten. The stack trace may be truncated or nonsensical above a certain frame.

Intel Engine classification

CrashCatch's Intel Engine classifies 0xC0000005 automatically using the target address, access type, register values, and stack shape. A null pointer at 0x0 with a read violation and a matching register state gets classified as Null Pointer Dereference with high confidence. A randomized target address combined with a write violation and suspicious register patterns gets classified as Memory Corruption. You still need to do the debugging but having the classification and the evidence list saves you the first ten minutes of every investigation.

Classify your access violations automatically.

Intel Engine runs on every crash dump no API key, no internet required.

Download Beta
← All posts
More from the blog
Why C++ Crash Dumps Are Still Unreadable How to Read a Windows Minidump File (Without WinDbg) Unreal Engine Crash Analysis: What the Crash Folder Actually Contains