the null pointer and NULL
/ NULL: nuhl /
Sometimes a pointer needs to mean 'nothing here yet' — the end of a linked list, a lookup that found no match, an allocation that failed. The null pointer is the agreed-upon 'points to nothing' value, and NULL is the name C gives you to write it clearly. Assigning p = NULL says 'this pointer deliberately points at no object'.
A null pointer is a pointer guaranteed to compare unequal to any real object's address. In source code 0 and NULL both produce a null pointer in a pointer context, and in practice the stored address is the number 0. The whole point is that you can test it: if (p) is true when p points somewhere and false when p is null, so if (p == NULL) or simply if (!p) checks before you trust it. Crucially, you must NOT dereference a null pointer — *p when p is NULL is undefined behaviour, and on most systems it crashes with a segmentation fault, because the operating system deliberately leaves the page at address 0 unmapped to catch exactly this mistake.
Null is a tool, not a danger in itself: it is the well-defined, checkable 'absent' value, and it is far safer than an uninitialised wild pointer that holds some random garbage address. The discipline is simple — set a pointer to NULL when it owns nothing, and check for NULL before every dereference, especially on the return of any function that can fail to give you a real pointer.
char *s = NULL; if (s != NULL) puts(s); — the guard skips the puts because s is null, avoiding a crash. Compare: writing puts(s) unguarded when s is NULL would dereference the null pointer.
if (p) / if (p != NULL) is the standard guard before dereferencing.
NULL is a 'points to nothing' marker, not the number zero you should do arithmetic on; and a null pointer is different from an uninitialised pointer (which holds garbage) and from a pointer to a zero value. After free(), set the pointer to NULL so a later accidental use is a clean crash rather than a use-after-free.