Can '\0' and NULL be used interchangeably?
NULL is often used in the context of pointers, and is defined via macros in multiple standard libraries (such as <iostream>
) to be the integer 0
. '\0'
is the null character, and is 8 bits of zeros. Incidentally, 8 bits of zeros is equivalent to the integer 0
.
In some cases, although it is considered to be horrible style, these two can be interchanged:
int *p='\0';
if (p==NULL) //evaluates to true
cout << "equal\n";
Or
char a=NULL;
char b='\0';
if (a==b) //evaluates to true
cout << "equal again\n";
There are already many similar questions on SO alone; for example, the top answer for this question (What is the difference between NULL, '\0' and 0) says "they are not really the same thing."
Could anyone provide an example that NULL
and \0
cannot be interchanged (preferably an actual application and not a pathological case)?
Solution 1:
Could anyone provide an example that NULL and \0 cannot be interchanged?
The difference between NULL
and '\0'
may affect overload resolution.
Example (check it on Coliru):
#include <iostream>
// The overloaded function under question can be a constructor or
// an overloaded operator, which would make this example less silly
void foo(char) { std::cout << "foo(char)" << std::endl; }
void foo(int) { std::cout << "foo(int)" << std::endl; }
void foo(long) { std::cout << "foo(long)" << std::endl; }
void foo(void*) { std::cout << "foo(void*)" << std::endl; }
int main()
{
foo('\0'); // this will definitely call foo(char)
foo(NULL); // this, most probably, will not call foo(char)
}
Note that the gcc compiler used at Coliru defines NULL
as 0L
, which for this example means that foo(NULL)
resolves to foo(long)
rather than to foo(void*)
. This answer discusses that aspect in detail.
Solution 2:
Definition of macro NULL in C++
Leon is correct that when there are several overloads for the same function, \0
would prefer the one that takes parameter of type char
. However, it is important to notice that on a typical compiler, NULL
would prefer the overload that takes parameter of type int
, not of type void*
!
What probably causes this confusion is that C language allows defining NULL
as (void*)0
. C++ standard explicitly states (draft N3936, page 444):
Possible definitions [of macro
NULL
] include0
and0L
, but not(void*)0
.
This restriction is necessary, because e.g. char *p = (void*)0
is valid C but invalid C++, whereas char *p = 0
is valid in both.
In C++11 and later, you should use nullptr
, if you need a null constant that behaves as pointer.
How Leon's suggestion works in practice
This code defines several overloads of a single function. Each overload outputs the type of the parameter:
#include <iostream>
void f(int) {
std::cout << "int" << std::endl;
}
void f(long) {
std::cout << "long" << std::endl;
}
void f(char) {
std::cout << "char" << std::endl;
}
void f(void*) {
std::cout << "void*" << std::endl;
}
int main() {
f(0);
f(NULL);
f('\0');
f(nullptr);
}
On Ideone this outputs
int
int
char
void*
Therefore I would claim that the problem with overloads is not an actual application but a pathological case. The NULL
constant will behave wrong anyway, and should be replaced with nullptr
in C++11.
What if NULL is not zero?
Another pathological case is suggested by Andrew Keeton at another question:
Note that what is a null pointer in the C language. It does not matter on the underlying architecture. If the underlying architecture has a null pointer value defined as address 0xDEADBEEF, then it is up to the compiler to sort this mess out.
As such, even on this funny architecture, the following ways are still valid ways to check for a null pointer:
if (!pointer) if (pointer == NULL) if (pointer == 0)
The following are INVALID ways to check for a null pointer:
#define MYNULL (void *) 0xDEADBEEF if (pointer == MYNULL) if (pointer == 0xDEADBEEF)
as these are seen by a compiler as normal comparisons.
Summary
All in all, I would say that the differences are mostly stylistic. If you have a function that takes int
and overload that takes char
, and they function differently, you will notice difference when you call them with \0
and NULL
constants. But as soon as you place those constants in variables, the difference disappears, because the function that is called is deducted from the type of the variable.
Using correct constants makes the code more maintainable, and conveys meaning better. You should use 0
when you mean a number, \0
when you mean a character, and nullptr
when you mean a pointer. Matthieu M. points out in comments, that GCC had a bug, in which a char*
was compared to \0
, whereas the intention was to dereference the pointer and compare a char
to \0
. Such errors are easier to detect, if proper style is used thorough the codebase.
To answer your question, there is not really an actual use case that would prevent you from using \0
and NULL
interchangeably. Just stylistic reasons and some edge cases.