A pointer always holds some value. When it has no object to point at yet - before it is assigned, after the thing it pointed at is freed, or when a function has nothing to return - it needs a value that unmistakably means "nothing here". That value is the null pointer, and NULL is the name you write.
The discipline around it is small and pays for itself constantly: set pointers to NULL when they have no target, check for NULL before dereferencing, and return NULL when you have no result.
What NULL Actually Is
NULL is a macro, defined in <stddef.h> and also pulled in by <stdio.h>, <stdlib.h>, <string.h>, and others. It expands to a null pointer constant - typically ((void*)0) or plain 0.
The C standard guarantees the property that matters: a null pointer compares unequal to a pointer to any actual object or function. Two null pointers of the same type always compare equal to each other.
Most systems print (nil) or 0x0 for the null pointer - address zero. That is a common implementation, not a rule; the standard never promises the bits are zero. What it promises is the comparison, so write p == NULL, never anything that depends on the representation.
Dereferencing NULL Crashes
The reason for every null check in existence:
int *p = NULL;
printf("%d\n", *p); // undefined behavior - almost certainly a crash
On Windows, macOS, and Linux the first page of the address space is deliberately left unmapped, so the CPU traps the access and the operating system kills the program. You will see:
Segmentation fault (core dumped) # Linux
zsh: segmentation fault ./program # macOS
That immediate crash is a feature. It converts a logic bug into a loud failure at the exact line that caused it, instead of a silent corruption you find three weeks later. See segmentation fault for how to read the crash and find the line.
This is why "null pointer" is not an exception you catch in C. There is no mechanism to recover; there is only checking beforehand.
Check What Might Fail
Functions that can fail signal it by returning NULL. Checking is not optional politeness - the crash is the alternative.
The standard library is full of these: malloc, calloc, realloc, fopen, strchr, strstr, getenv, bsearch. Every one of them answers "no result" with NULL. See dynamic memory for the full allocation story.
Two idioms for the check, both common:
if (p == NULL) { /* handle */ } // explicit - preferred for clarity
if (!p) { /* handle */ } // terse - relies on NULL being falsy
A null pointer tests as false in a condition and a valid pointer as true, so both work. The explicit form reads better when a stranger is scanning your code for bugs.
NULL vs 0 vs Uninitialized
Three distinct situations that beginners blur together:
int *a = NULL; "points at nothing" - testable, safe to check
int *b; uninitialized - holds garbage, NOT testable
int *c = &x; points at a real object
The dangerous one is b. Its bytes are whatever was left in that stack slot, which might be zero (and look fine) or might be a plausible-looking address from an earlier call. No check can distinguish it from a valid pointer, and the behavior changes between debug and release builds.
Initialize every pointer. If you have no address yet, NULL is the address. Compile with -Wall -Wextra and the compiler will flag many uninitialized uses for you.
On 0: in a pointer context the integer constant 0 is the null pointer constant, so p = 0; is valid C. Prefer NULL anyway. It signals intent, and it matters in variadic calls where the compiler cannot convert for you:
execl("/bin/ls", "ls", 0); // risky - may pass an int where a pointer is expected
execl("/bin/ls", "ls", (char *)NULL); // correct
C23 adds nullptr, a keyword with its own type that avoids this class of ambiguity entirely; NULL remains correct and portable everywhere.
Defensive Patterns
Guard at the top of a function that takes pointers.
Set to NULL after freeing. free does not change your pointer - it only releases the memory. The stale pointer left behind is a dangling pointer, and using it is undefined behavior that often does not crash right away.
Two facts inside that example are worth committing to memory. free(NULL) is defined to do nothing, so cleanup code never needs to guard it. And setting p = NULL after free turns a use-after-free - which may silently corrupt data - into an immediate, debuggable crash.
Return NULL to mean "no result", and say so.
// returns a pointer to the matching element, or NULL if there is none
int *find(int *arr, int n, int target);
Document it in the comment above the function. A caller who knows a NULL is possible writes the check; one who does not, will not.
A Quick Checklist
- Initialize every pointer, with a real address or with
NULL. - Check the return of
malloc,calloc,realloc, andfopenbefore using it. - Check pointer parameters at the top of any function that could be called with bad input.
- Set pointers to
NULLimmediately afterfree. - Write
NULL, not0, whenever a pointer is meant. - Build with
-Wall -Wextraand, when you can, run under a sanitizer:gcc -fsanitize=address,undefinedcatches null dereferences with a precise report.
Frequently Asked Questions
What is a NULL pointer in C?
A pointer holding the null pointer constant - a value guaranteed to compare unequal to the address of any real object. It is the standard way to say "this pointer points at nothing yet", and NULL is the macro for it, defined in <stddef.h> and several other headers.
What happens if you dereference a NULL pointer in C?
It is undefined behavior. In practice, on desktop and server systems, it crashes immediately with a segmentation fault because address zero is deliberately left unmapped. On embedded systems without memory protection it may silently read or corrupt something instead, which is far worse.
Is NULL the same as 0 in C?
In pointer contexts, yes: the integer constant 0 is the null pointer constant, so p = 0; and p == NULL both work. But use NULL for pointers and 0 for numbers - it tells the reader which of the two you meant, and it keeps variadic calls safe, where a bare 0 may be passed as an int rather than a pointer.
What is the difference between a NULL pointer and an uninitialized pointer?
A NULL pointer definitely points at nothing, and you can test for it. An uninitialized pointer holds whatever bytes happened to be in that memory - possibly a valid-looking address - so there is no test that can catch it. Always initialize pointers to NULL when you have nothing better.