NULL != 0
Exploring why NULL and 0 are not equivalent in GNU C through pre-processor results and memory analysis.
March 28, 2026
NULL != 0
Reqruies
- Compiler: x86_64-linux-gnu 7.4.0, x86_64-linux-gnu 4.8.5
- Assembly: AT&T
- Code Base: glibc 2.23.90
All content in this post is based on GNU C.
Although they are often used interchangeably, in GNU C, NULL and 0 are strictly speaking not identical.
NULL != 0
So, what exactly is the NULL we use?
Let's check how it changes during the preprocessing stage with simple sample code.
Let's check how it changes during the preprocessing stage with simple sample code.
What is NULL?
Sample Source Code - null.cc
Preprocess only resultbash
As seen in the preprocessing result, in GNU C, NULL is replaced by ((void *)0).
NULL == ((void *)0)
Including stdio.h instead of stddef.h
Even if you include
This is because
stdio.h instead of stddef.h, the result is the same.This is because
stdio.h internally includes stddef.h to pull in the NULL definition.Content of stdio.hc
NULL Definition in stddef.h
Looking at how NULL is defined in GCC's
stddef.h, you can see differences depending on the environment.NULL definition in stddef.hc
In the GNU C++ compiler (when
__GNUG__ is defined), NULL is defined as the built-in keyword __null, while in standard C++ (__cplusplus environments), it is simply defined as 0.Difference in Size
The most decisive difference between
In C, 0 is an
In a 64-bit environment, the sizes of these two types are different.
0 and NULL lies in their Type.In C, 0 is an
int literal, whereas NULL is of type void *.In a 64-bit environment, the sizes of these two types are different.
sizeof NULL in 64-bitc
Size of resultbash
In common assignments (e.g.,
char *p = 0;), the compiler recognizes 0 as a null pointer constant and handles it appropriately. However, in Variadic Functions where types are not explicitly specified, this size difference can cause serious problems.Potential Issues with Variadic Functions
Let's look at an example using
va_arg, a variadic function.Problem with literal 0 in variadic functionsc
The
However, if you pass
According to the x86-64 calling convention, an 8-byte space is allocated on the stack, but only the lower 4 bytes are filled with 0.
expect() function expects char * (8 bytes) as variadic arguments.However, if you pass
0 as the last argument, the compiler treats it as an int (4 bytes).According to the x86-64 calling convention, an 8-byte space is allocated on the stack, but only the lower 4 bytes are filled with 0.
When
va_arg(ap, char *) later tries to read 8 bytes from the stack, the correctly filled 4-byte data combined with the garbage value in the upper 4 bytes is interpreted as an incorrect pointer address.This leads to an argument mismatch, and the program may terminate abnormally.
Result with gcc-4.8bash
To resolve this issue, you must explicitly pass NULL or cast it as
(char *)0.Fixed codec
As a side note, modern compilers like GCC 7 or later handle this more intelligently by using
pushq to ensure 8-byte alignment when passing arguments, thereby preventing such mistakes.Assembly result - gcc-7bash
man execl Recommendations
The manual pages for variadic system call functions like
execl() also strongly recommend using NULL as the terminator for the argument list and casting it as (char *)NULL.man execl excerptDocThe list of arguments must be terminated by a null pointer, and, since these are variadic functions, this pointer must be cast (char *) NULL.
This means that because the compiler cannot infer the types of variadic arguments, the programmer must explicitly pass a null value of pointer size (8 bytes).
Conclusion
In conclusion, while
NULL and 0 might behave similarly depending on the context in C, they are fundamentally different in type and size.They must be used distinctively, especially in environments where memory layout is critical, such as variadic functions or hardware control.
Adhering to the basic principle of using NULL for pointers and 0 for integers is the first step toward writing safe code.