Lots of confusion and confused answers here. First of all, there is strictly speaking nothing called a "NULL pointer". There are null pointers, null pointer constants and the NULL
macro.
Start by studying my answer from Codidact: What's the difference between null pointers and NULL? Quoting some parts of it here:
There are three different, related concepts that are easy to mix up:
- null pointers
- null pointer constants
- the NULL macro
Formal definitions
The first two of these terms are formally defined in C17 6.3.2.3/3:
An integer constant expression with the value 0
, or such an expression cast to type void *
, is called a null pointer
constant.67) If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is
guaranteed to compare unequal to a pointer to any object or function.
In other words, a null pointer is a pointer of any type pointing at a
well-defined "nowhere". Any pointer can turn into a null pointer when
it is assigned a null pointer constant.
The standard mentions 0
and (void*)0
as two valid null pointer
constants, but note that it says "an integer constant expression with
the value 0
". This means that things like 0u
, 0x00
and other
variations are also null pointer constants. These are particular
special cases that can be assigned to any pointer type, regardless of
the various type compatibility rules that would normally apply.
Notably, both object pointers and function pointers can be null
pointers. Meaning that we must be able to assign null pointer
constants to them, no matter the actual pointer type.
NULL
The note 67) from above adds (not normative):
67) The macro NULL
is defined in <stddef.h>
(and other headers) as a null pointer constant; see 7.19.
where 7.19 simply defines NULL
as (normative):
NULL
which expands to an implementation-defined null pointer constant;
In theory this could perhaps be something other than 0
and
(void*)0
, but the implementation-defined part is more likely saying
that NULL
can either be
#define NULL 0
or #define NULL (void*)0
or some other integer
constant expression with the value zero, depending on the C library
used. But all we need to know and care about is that NULL
is a null
pointer constant.
NULL
is also the preferred null pointer constant to use in C code,
because it is self-documenting and unambiguous (unlike 0
). It should
only be used together with pointers and not for any other purpose.
Additionally, do not mix this up with "null termination of strings", which is an entirely separate topic. Null termination of strings is just a value zero, often referred to either as nul
(one L) or '\0'
(an octal escape sequence), just to separate it from null pointers and NULL
.
Dereferencing
Having cleared that out, we cannot access what a null pointer points at, because it is as mentioned a well-defined "nowhere". The process of accessing what a pointer points at is known as dereferencing, and is done in C (and C++) through the unary *
indirection operator. The C standard specifying how this operator works simply states (C17 6.5.3.3):
If an invalid value has been assigned to the pointer, the behavior of the unary *
operator is undefined
Where an informative note adds:
Among the invalid values for dereferencing a pointer by the unary *
operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.
And this would be where "segmentation faults" or "null pointer/reference exceptions" might be thrown. The reason for such is almost always an application bug such as these examples:
int* a = NULL; // create a null pointer by initializing with a null pointer constant
*a = 1; // null pointer is dereferenced, undefined behavior
int* b = 0; // create a null pointer by initializing with a null pointer constant
// not to be confused with similar looking dereferencing and assignment:
*b = 0; // null pointer is dereferenced, undefined behavior