NULL & Dangling Pointers
A pointer variable always holds some address, but that address is not always a safe one to use. Two of the most common ways a pointer goes wrong are being NULL (deliberately "points to nothing") and being dangling (pointing to memory that is no longer valid). Both are behind a huge share of real-world C crashes and security bugs, so learning to recognize and guard against them is essential.
NULL: "points to nothing"
NULL is a special pointer value, defined in <stddef.h> (and several other standard headers), that is guaranteed not to point at any valid object. It is the conventional way to say "this pointer currently has no target" — used to initialize pointers before they are assigned a real address, and to signal failure from functions like malloc, which returns NULL when it cannot allocate memory.
#include <stdio.h>
int main(void) {
int *p = NULL;
printf("about to crash...\n");
printf("%d\n", *p); // undefined behavior: NULL pointer dereference
return 0;
}about to crash... Segmentation fault (core dumped)
The fix is always the same: check a pointer against NULL before dereferencing it, especially after any function that can return NULL on failure, such as malloc, fopen, or strchr.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
if (p == NULL) {
fprintf(stderr, "allocation failed\n");
return 1;
}
*p = 42; // safe: we know p is not NULL here
printf("%d\n", *p);
free(p);
return 0;
}Dangling pointers: valid-looking, but not valid
A dangling pointer is more dangerous than a NULL pointer precisely because it does not look obviously wrong — it holds an address that used to be valid, but the memory at that address has since been freed or gone out of scope. Using it is undefined behavior: it might appear to work, corrupt unrelated data, or crash, and the behavior can change from run to run.
The classic example is returning the address of a local (automatic) variable from a function. Once the function returns, its local variables no longer exist — the stack space they occupied is free to be reused by the next function call.
#include <stdio.h>
int *make_dangling(void) {
int local = 99;
return &local; // BUG: local's storage ends when the function returns
}
int main(void) {
int *p = make_dangling();
printf("%d\n", *p); // undefined behavior: reading freed stack memory
return 0;
}The fix is to give the value storage that outlives the function — either allocate it dynamically with malloc (and have the caller free it), or have the caller pass in a pointer to memory it already owns.
#include <stdio.h>
#include <stdlib.h>
int *make_valid(void) {
int *p = malloc(sizeof(int));
if (p != NULL) {
*p = 99;
}
return p; // heap memory outlives the function call
}
int main(void) {
int *p = make_valid();
if (p != NULL) {
printf("%d\n", *p); // well-defined
free(p);
p = NULL;
}
return 0;
}99
The other common cause: use-after-free
A pointer also dangles the moment you free the memory it points to — the pointer variable itself still holds the old address, but that address is no longer yours to use.
int *p = malloc(sizeof(int));
*p = 5;
free(p);
printf("%d\n", *p); // undefined behavior: use-after-free, p is danglingAlways initialize pointers, and check pointers returned from
malloc/fopen/etc. againstNULLbefore dereferencing.Never return the address of a local (automatic) variable from a function.
A pointer becomes dangling the instant the memory it points to is freed or goes out of scope, even though its value looks unchanged.
Set a pointer to
NULLimmediately afterfreeing it to convert future misuse into a safe no-op or an obvious crash.