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] include 0 and 0L, 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.