Can '\0' and NULL be used interchangeably?

C++Null

C++ Problem Overview


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 (https://stackoverflow.com/questions/1296843/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)?

C++ Solutions


Solution 1 - C++

> 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 - C++

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.

Solution 3 - C++

Please don't do this. It is an anti-pattern, and it is actually wrong. NULL is for NULL pointers, '\0' is the null-character. They are logically different things.

I don't think I have ever seen this:

int* pVal='\0';

But this is fairly common:

char a=NULL;

But it is not good form. It makes the code less portable, and in my opinion less readable. It is also likely to cause issues in mixed C/C++ environments.

It relies on assumptions regarding how any particular implementation defines NULL. For instance, some implementations use a simple

#define NULL 0

Others might use:

#define NULL ((void*) 0)

And I have seen others defining as an integer, and all sorts of odd treatment.

NULL should, in my opinion be used solely to indicate an invalid address. If you want a null-character, use '\0'. Or define this as NULLCHR. But that isn't as clean.

This will make your code more portable- you won't start getting warnings regarding types etc if you change compiler / environment /compiler settings. This might be more important in a C or mixed C/C++ environment.

An example of warnings that might arise: Consider this code:

#define NULL 0
char str[8];
str[0]=NULL;

This is equivalent to:

#define NULL 0
char str[8];
str[0]=0;

And we are assigning an integer value to a char. This may cause a compiler warning, and if there are enough occurrences of this, pretty soon you can't see any important warnings. And for me, this is the real problem. Having warnings in the code has two side effects:

  1. Given enough warnings, you don't spot new ones.
  2. It gives the signal that warnings are acceptable.

In both cases, actual bugs can then slip through, that would be caught by the compiler had we bothered to read the warnings (or turn on -Werror )

Solution 4 - C++

Yes, they may exhibit different behaviour while doing resolution of overloaded functions.

func('\0') invokes func(char),

while

func(NULL) invokes func(integer_type).


You can remove the confusion by using nullptr, which is always a pointer type, displays no ambiguity while assigning/comparing value or function overloading resolution.

char a = nullptr; //error : cannot convert 'std::nullptr_t' to 'char' in initialization
int x = nullptr;  //error : nullptr is a pointer not an integer

Note that, it is still compatible with NULL:

int *p=nullptr;
if (p==NULL) //evaluates to true

Excerpt from C++ Programming Stroustrup 4th Edition book:

> In older code, 0 or NULL is typically used instead of nullptr > (§7.2.2). However, using nullptr eliminates potential confusion > between integers (such as 0 or NULL) and pointers (such as nullptr).


Solution 5 - C++

Computer programs have two types of readers.

The first type are computer programs, like the compiler.

The second type are humans, like yourself and your coworkers.

Programs are generally fine with getting one type of zero in place of another. There are exceptions, as the other answers have pointed out, but that is not really important.

What is important is that you are messing with the human readers.

Human readers are very context sensitive. By using the wrong zero, you are lying to you human readers. They are going to curse you.

A human that is lied to can overlook bugs more easily.
A human that is lied to can see "bugs" that aren't there. When "fixing" these phanthom bugs, they introduce real bugs.

Don't lie to your humans. One of the humans you are lying to is your future self. You are going to curse you too.

Solution 6 - C++

Excerpts from C++14 draft N3936:

> ###18.2 Types [support.types] 3 The macro NULL is an implementation-defined C++ null pointer constant in this International Standard (4.10).

> ###4.10 Pointer conversions [conv.ptr] 1 A null pointer constant is an integer literal (2.14.2) with value zero or a prvalue of type std::nullptr_t.
A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type.

Thus, NULL can be any integer literal with value zero, or a value of type std::nullptr_t like nullptr, while '\0' is always the zero narrow-character literal.

So, not in general interchangeable, even though in a pointer-context you cannot see any but stylistic difference.

An example would be:

#include <iostream>
#include <typeinfo>

int main() {
    std::cout << typeid('\0').name << '\n'
        << typeid(NULL).name << '\n'
        << typeid(nullptr).name << '\n';
}

Solution 7 - C++

Let's define what's NULL in C/C++.

According to the C/C++ reference NULL is defined as a macro that expands to a null pointer constant. Next we can read that a null pointer constant can be converted to any pointer type (or pointer-to-member type), which acquires a null pointer value. This is a special value that indicates that the pointer is not pointing to any object.

Definition referring to C:

> A null-pointer constant is an integral constant expression that > evaluates to zero (like 0 or 0L), or the cast of such value to type > void* (like (void*)0).

Definition referring to C++98:

> A null-pointer constant is an integral constant expression that evaluates to zero (such as 0 or 0L).

Definition referring to C++11:

> A null-pointer constant is either an integral constant expression that evaluates to zero (such as 0 or 0L), or a value of type nullptr_t (such as nullptr).

Overloading methods example.

Let's assume that we have following methods:

class Test {
public:
    method1(char arg0);
    method1(int arg0);
    method1(void* arg0);
    method1(bool arg0);
}

Calling method1 with argument NULL or nullptr should call method1(void* arg0);. However if we call method1 with argument '\0' or 0 should execute method1(char arg0); and method1(int arg0);.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncwestView Question on Stackoverflow
Solution 1 - C++LeonView Answer on Stackoverflow
Solution 2 - C++VLLView Answer on Stackoverflow
Solution 3 - C++mjsView Answer on Stackoverflow
Solution 4 - C++Saurav SahuView Answer on Stackoverflow
Solution 5 - C++Stig HemmerView Answer on Stackoverflow
Solution 6 - C++DeduplicatorView Answer on Stackoverflow
Solution 7 - C++user6927987View Answer on Stackoverflow