Difference Between NULL and Zero in Comparing - c++

I know a little bit about NULL, but when it comes to comparing I get confused.
For Example:
int* p;
if(p == NULL){
//do stuff
}
if(p == 0){
//do stuff
}
In the first comparison "p" compares with what address?
Is it looking for the reference point of "p", and seeing if it is valid or not?

In every modern implementation of C, NULL is zero, usually as a pointer value:
#define NULL (void *) 0
So comparing, say, a character to NULL is likely to be invalid:
char ch = 'a';
if (ch == NULL) // probably "invalid comparison of pointer with scalar"
As a pointer value, NULL chosen to point to invalid memory so that dereferencing it (on a suitable architecture) will cause a memory fault. Most virtual machines reserve lower memory just for this purpose and implemented by leaving low memory unmapped to physical memory. How much memory is unreserved? Could be a few kilobytes, but many implementations reserve a few megabytes.

NULL represents a value that is not used by any valid pointer. So, if a pointer is NULL, it points to no variable at all. In pointer contexts, the value zero means the same thing as NULL, however it is clearer to use NULL instead of zero. That's all you need to know about NULL.

From the C99 Standard:
7.17 Common definitions <stddef.h>
3 The macros are
NULL
which expands to an implementation-defined null pointer constant; ...
From the C++11 Standard:
18.2 Types
3 The macro NULL is an implementation-defined C++ null pointer constant in this International Standard (4.10).194
Footnote 194:
194) Possible definitions include 0 and 0L, but not (void*)0.
Microsoft VS 2008 defines it as:
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
gcc/g++ 4.9.3 defines it as:
#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL /* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else /* C++ */
#define NULL 0
#endif /* C++ */
#endif /* G++ */
#endif /* NULL not defined and <stddef.h> or need NULL. */
I suspect other modern compilers define it similarly.
Given the above definitions of NULL, the line
if(p == NULL){
expands to:
if(p == ((void*)0) ){
in C.
It expands to
if(p == 0){
in C++.
In other C++ compilers, that line could expand to
if(p == 0L){
When using C++11, it is better to avoid NULL and start using nullptr instead.
if ( p == nullptr ) {

Conceptually, NULL is a singular pointer-value pointing nowhere, and implicitly convertible to any pointer-type.
Practically, you unfortunately cannot depend on it behaving as a pointer value unless you use it in a pointer-only context.
Thus, use nullptr in C++11 and later respectively 0 cast to the right pointer-type, especially in contexts where an integral zero would not be converted to a pointer.
BTW: While on most modern systems a null pointer is really all-bits-zero, that's not required by the standard (there might be many bit-patterns representing null pointers, and none all-zero), and doesn't make any difference to how a null pointer constant is represented in source code.
In C++:
The macro NULL is an implementation-defined C++ null pointer constant in this International Standard (4.10).
Which is defined as:
A null pointer constant is an integer literal (2.14.2) with value zero or a prvalue of type std::nullptr_t.
Pre-C++11 the last option was obviously not available...
And post-C++11, because you do not know what it is, you should use nullptr directly.
In C:
The macros are
NULL
which expands to an implementation-defined null pointer constant; ...
Which is defined as:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.

NULL in C++ compilers is just a macro defining a "null pointer constant" (generally of value 0). So there is no difference, apart from NULL being used to indicate an intent (which is important). For example you can create an int variable with the value NULL, which is not given any special treatment over the standard declaration (int a = 0;).
NULL is NOT "an empty space in memory". It is just a symbol that (generally) represents an absence of something (value, pointer etc...).
However using of NULL is now discouraged in C++ and the far superior nullptr should be used instead.

know that NULL is a empty space in memory
Not quite - NULL is a well-defined "nowhere" that doesn't correspond to any valid memory address.
In both C and C++, the NULL macro is defined to be a null pointer constant, which is a zero-valued integer expression. C++ also provides the nullptr literal, which also evaluates to the null pointer constant. When a null pointer constant appears in a pointer context, it will be converted to an implementation-defined null pointer value. A null pointer value may or may not be 0-valued, but it is guaranteed to compare unequal to any valid pointer value.
C 2011 online standard:
6.3.2.3 Pointers
...
3 An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant.
66) 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.
66)) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19
C++ 2014 online draft
2.13.7 Pointer literals [lex.nullptr]
pointer-literal:
nullptr
1 The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [Note: std::nullptr_t
is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is
a null pointer constant and can be converted to a null pointer value or null member pointer value. See 4.10
and 4.11. — end note ]
4.10 Pointer conversions [conv.ptr]
1 A null pointer constant is an integer literal (2.13.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. Such a conversion
is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The
conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the
sequence of a pointer conversion followed by a qualification conversion (4.4). A null pointer constant of
integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a
null pointer value. — end note ]
...
18.2 Types [support.types]
...
3 The macro NULL is an implementation-defined C++ null pointer constant in this International Standard
(4.10).194
194) Possible definitions include 0 and 0L, but not (void*)0
To hammer some points home:
The NULL macro, nullptr literal (C++ only), and null pointer constant are always 0-valued;
The null pointer value does not have to be 0-valued;
The null pointer value will never equal a valid memory address;
It's the compiler's job to map the null pointer constant in your source code to the equivalent null pointer value in the generated machine code; as the programmer, you don't (generally) need to worry about the actual null pointer value on your implementation;
Since a null pointer constant is always 0-valued, comparing a pointer value against 0 should be equivalent to comparing it against NULL or nullptr.

Related

Can I use NULL as substitution for the value of 0?

Am I allowed to use the NULL pointer as replacement for the value of 0?
Or is there anything wrong about that doing?
Like, for example:
int i = NULL;
as replacement for:
int i = 0;
As experiment I compiled the following code:
#include <stdio.h>
int main(void)
{
int i = NULL;
printf("%d",i);
return 0;
}
Output:
0
Indeed it gives me this warning, which is completely correct on its own:
warning: initialization makes integer from pointer without a cast [-Wint-conversion]
but the result is still equivalent.
Am I crossing into "Undefined Behavior" with this?
Is it permissible to utilize NULL in this way?
Is there anything wrong with using NULL as a numerical value in arithmetical expressions?
And what is the result and behavior in C++ for this case?
I have read the answers of What is the difference between NULL, '\0' and 0 about what the difference between NULL, \0 and 0 is, but I did not get the concise information from there, if it is quite permissible and also right to use NULL as value to operate with in assignments and other arithmetical operations.
Am I allowed to use the NULL pointer as replacement for the value of 0?
No, it is not safe to do so. NULL is a null-pointer constant, which could have type int, but which more typically has type void * (in C), or otherwise is not directly assignable to an int (in C++ >= 11). Both languages allow pointers to be converted to integers, but they do not provide for such conversions to be performed implicitly (though some compilers provide that as an extension). Moreover, although it is common for converting a null pointer to an integer to yield the value 0, the standard does not guarantee that. If you want a constant with type int and value 0 then spell it 0.
Am I might crossing into Undefined Behavior with this?
Yes, on any implementation where NULL expands to a value with type void * or any other not directly assignable to int. The standard does not define the behavior of your assignment on such an implementation, ergo its behavior is undefined.
is it permissible to operate with the NULL in that way?
It is poor style, and it will break on some systems and under some circumstances. Inasmuch as you appear to be using GCC, it would break in your own example if you compiled with the -Werror option.
Is there anything wrong about to use NULL as numerical value in arithmetical expressions?
Yes. It is not guaranteed to have a numerical value at all. If you mean 0 then write 0, which is not only well defined, but shorter and clearer.
And how is the result in C++ to that case?
The C++ language is stricter about conversions than is C and has different rules for NULL, but there, too, implementations may provide extensions. Again, if you mean 0 then that's what you should write.
NULL is some null pointer constant. In C it could be an integer constant expression with value 0 or such an expression cast to void*, with the latter more likely. Which means you can't assume to use NULL interchangeably with zero. For instance, in this code sample
char const* foo = "bar";
foo + 0;
Replacing 0 with NULL is not guaranteed to be a valid C program, because addition between two pointers (let alone of different pointer types) is not defined. It will cause a diagnostic to be issued due to a constraint violation. The operands for addition will not be valid.
As for C++, things are somewhat different. Lack of an implicit conversion from void* to other object types meant that NULL was historically defined as 0 in C++ code. In C++03, you could probably get away with it. But since C++11 it can legally be defined as the nullptr keyword. Now again producing an error, since std::nullptr_t may not be added to pointer types.
If NULL is defined as nullptr then even your experiment becomes invalid. There is no conversion from std::nullptr_t to an integer. That is why it is considered a safer null pointer constant.
Am I allowed to use the NULL pointer as a replacement for the value of 0?
int i = NULL;
The rules vary between languages and their versions. In some cases you can and in others, you can't. Regardless, you shouldn't. If you're lucky, your compiler will warn when you attempt it or even better, fail to compile.
In C++, prior to C++11 (quote from C++03):
[lib.support.types]
NULL is an implementation-defined C++ null pointer constant in this International Standard.
It makes little sense to use a null pointer constant as an integer. However...
[conv.ptr]
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.
So, it would technically work even if it's nonsensical. Due to this technicality, you may encounter poorly written programs that abuse NULL.
Since C++11 (quote from latest draft):
[conv.ptr]
A null pointer constant is an integer literal ([lex.icon]) with value zero or a prvalue of type std​::​nullptr_­t.
A std​::​nullptr_­t is not convertible to an integer, so using NULL as integer would work only conditionally, depending on choices made by the language implementation.
P.S. nullptr is a prvalue of type std​::​nullptr_­t. Unless you need your program to compile in pre-C++11, you should always use nullptr instead of NULL.
C is a bit different (quotes from C11 draft N1548):
6.3.2.3 Language / Conversions / Other operands / Pointers
3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. ...
So, the case is similar to post C++11 i.e. the abuse of NULL works conditionally depending on choices made by the language implementation.
Yes, though depending on the implementation you may need a cast. But yes, it is 100% legitimate, otherwise.
Although it is really, really, really bad style (needless to say?).
NULL is, or was, actually not C++, it is C. The standard does however, like for many C legacies, have two clauses ([diff.null] and [support.types.nullptr]) which technically make NULL C++. It is an implementation-defined null-pointer constant. Therefore, even if it's bad style, it's technically as C++ as it can be.
As pointed out in the footnote, possible implementations could be 0 or 0L, but not (void*)0.
NULL could, of course (the standard doesn't explicitly say so, but it's pretty much the only choice remaining after 0 or 0L) be nullptr. That's almost never the case, but it is a legal possibility.
The warning that the compiler showed to you demonstrates that the compiler is in fact not compliant (unless you compiled in C mode). Because, well, according to the warning, it did convert a null pointer (not nullptr which would be of nullptr_t, which would be distinct), so apparently the definition of NULL is indeed (void*)0, which it may not be.
Either way, you have two possible legitimate (i.e. compiler not broken) cases. Either (the realistic case), NULL is something like 0 or 0L, then you have "zero or one" conversions to integer, and you are good to go.
Or NULL is indeed nullptr. In that case you have a distinct value that has guarantees about comparison as well as clearly-defined conversions from integers, but unluckily not to integers. It does, however, have a clearly-defined conversion to bool (resulting in false), and bool has a clearly-defined conversion to integer (resulting in 0).
Unluckily, that's two conversions, so it's not within "zero or one" as pointed out in [conv]. Thus, if your implementation defines NULL as nullptr, then you will have to add an explicit cast for your code to be correct.
From the C faq:
Q: If NULL and 0 are equivalent as null pointer constants, which should I use?
A: It is only in pointer contexts that NULL and 0 are equivalent. NULL should not be used when another kind of 0 is required, even though it might work, because doing so sends the wrong stylistic message. (Furthermore, ANSI allows the definition of NULL to be ((void *)0), which will not work at all in non-pointer contexts.) In particular, do not use NULL when the ASCII null character (NUL) is desired. Provide your own definition
http://c-faq.com/null/nullor0.html
Disclaimer: I don't know C++. My answer is not meant to be applied in the context of C++
'\0' is an int with value zero, just 100% exactly like 0.
for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;
In the context of pointers, 0 and NULL are 100% equivalent:
if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;
are all 100% equivalent.
Note about ptr + NULL
The context of ptr + NULL is not that of pointers. There is no definition for the addition of pointers in the C language; pointers and integers can be added (or subtracted). In ptr + NULL if either ptr or NULL is a pointer, the other must be an integer, so ptr + NULL is effectively (int)ptr + NULL or ptr + (int)NULL and depending on the definitions of ptr and NULL several behaviours can be expected: it all working, warning for conversion between pointer and integer, failure to compile, ...
No, not anymore preferred to use NULL(old way of pointer initilization).
Since C++11:
The keyword nullptr denotes the pointer literal. It is a prvalue of type std::nullptr_t. There exist implicit conversions from nullptr to null pointer value of any pointer type and any pointer to member type. Similar conversions exist for any null pointer constant, which includes values of type std::nullptr_t as well as the macro NULL.
https://en.cppreference.com/w/cpp/language/nullptr
Actually, std::nullptr_t is the type of the null pointer literal, nullptr. It is a distinct type that is not itself a pointer type or a pointer to member type.
#include <cstddef>
#include <iostream>
void f(int* pi)
{
std::cout << "Pointer to integer overload\n";
}
void f(double* pd)
{
std::cout << "Pointer to double overload\n";
}
void f(std::nullptr_t nullp)
{
std::cout << "null pointer overload\n";
}
int main()
{
int* pi; double* pd;
f(pi);
f(pd);
f(nullptr); // would be ambiguous without void f(nullptr_t)
// f(0); // ambiguous call: all three functions are candidates
// f(NULL); // ambiguous if NULL is an integral null pointer constant
// (as is the case in most implementations)
}
Output:
Pointer to integer overload
Pointer to double overload
null pointer overload

Why can pointers be NULL

In C or C++, isn't NULL just the constant integer 0? Why are we allowed to assign some pointer to the value NULL if it a constant integer? Wouldn't a compiler say that the two types don't match?
The interpretation of the constant 0 depends on the context. If it's being used in a context where a number is required, it's the number zero. If it's being used in a context where a pointer is required, it's treated as a null pointer. So if you have the code:
int n = 0; // assigns zero
int *p = 0; // assigns null pointer
somefunc((char *)0); // passes null pointer to the function
In C, the macro NULL is permitted to expand into either an integer constant 0 or such a constant cast to void *. In C++ pre-11, it can expand into either an integer constant that evaluates to 0. In C++-11 it can expand into an integer literal with value 0 or a prvalue of type std::nullptr_t (e.g. nullptr).
See http://en.cppreference.com/w/cpp/types/NULL for the C++ description, http://en.cppreference.com/w/c/types/NULL for the C description.
There are two issues:
A type can have a "special", out-of-band, exceptional value. For example, floating-point numbers have the value NaN. And pointers have a special, not-a-valid-pointer value, the null pointer.
What's the name of the null pointer? In C, for better or worse, its name is 0. (It would avoid all kinds of confusion if its name were a separate keyword like null or nil, but that's not the way the language definition came out.)
But yes, you're right, the ability to assign the constant 0 to a pointer variable is a special case, enshrined in the language definition.
6.3.2.3 Pointers
...
3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.66) 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.
66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.
C 2011 online draft
A literal 0 in a pointer context is treated specially by the compiler, and is understood to be the null pointer constant. In the context of your source code, it behaves like any other zero-valued expression.
However, once your code is translated to machine code, all occurrences of the null pointer constant will be replaced with whatever value the underlying platform uses to represent a well-defined invalid pointer value, whether that’s 0 or 0xFFFFFFFF or 0xDEADBEEF.

How to write C/C++ code correctly when null pointer is not all bits zero

As the comp.lang.c FAQ says, there are architectures where the null pointer is not all bits zero. So the question is what actually checks the following construction:
void* p = get_some_pointer();
if (!p)
return;
Am I comparing p with machine dependent null pointer or I'm comparing p with arithmetic zero?
Should I write
void* p = get_some_pointer();
if (NULL == p)
return;
instead to be ready for such architectures or is it just my paranoia?
According to the C spec:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant. 55) 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.
So 0 is a null pointer constant. And if we convert it to a pointer type we will get a null pointer that might be non-all-bits-zero for some architectures. Next let's see what the spec says about comparing pointers and a null pointer constant:
If one operand is a
pointer and the other is a null pointer constant, the null pointer
constant is converted to the type of the pointer.
Let's consider (p == 0): first 0 is converted to a null pointer, and then p is compared with a null pointer constant whose actual bit values are architecture-dependent.
Next, see what the spec says about the negation operator:
The result of the logical negation operator ! is 0 if the value of its
operand compares unequal to 0, 1 if the value of its operand compares
equal to 0. The result has type int. The expression !E is equivalent
to (0==E).
This means that (!p) is equivalent to (p == 0) which is, according to the spec, testing p against the machine-defined null pointer constant.
Thus, you may safely write if (!p) even on architectures where the null pointer constant is not all-bits-zero.
As for C++, a null pointer constant is defined as:
A null pointer constant is an integral constant expression (5.19)
prvalue of integer type that evaluates to 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.
Which is close to what we have for C, plus the nullptr syntax sugar. The behavior of operator == is defined by:
In addition, pointers to members can be compared, or a pointer to
member and a null pointer constant. Pointer to member conversions
(4.11) and qualification conversions (4.4) are performed to bring them
to a common type. If one operand is a null pointer constant, the
common type is the type of the other operand. Otherwise, the common
type is a pointer to member type similar (4.4) to the type of one of
the operands, with a cv-qualification signature (4.4) that is the
union of the cv-qualification signatures of the operand types. [ Note:
this implies that any pointer to member can be compared to a null
pointer constant. — end note ]
That leads to conversion of 0 to a pointer type (as for C). For the negation operator:
The operand of the logical negation operator ! is contextually
converted to bool (Clause 4); its value is true if the converted
operand is true and false otherwise. The type of the result is bool.
That means that result of !p depends on how conversion from pointer to bool is performed. The standard says:
A zero value, null pointer value, or null member pointer value is
converted to false;
So if (p==NULL) and if (!p) does the same things in C++ too.
It doesn't matter if null pointer is all-bits zero or not in the actual machine. Assuming p is a pointer:
if (!p)
is always a legal way to test if p is a null pointer, and it's always equivalent to:
if (p == NULL)
You may be interested in another C-FAQ article: This is strange. NULL is guaranteed to be 0, but the null pointer is not?
Above is true for both C and C++. Note that in C++(11), it's preferred to use nullptr for null pointer literal.
This answer applies to C.
Don't mix up NULL with null pointers. NULL is just a macro guaranteed to be a null pointer constant. A null pointer constant is guaranteed to be either 0 or (void*)0.
From C11 6.3.2.3:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant 66). 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.
66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.
7.19:
The macros are
NULL
which expands to an implementation-defined null pointer constant;
Implementation-defined in the case of NULL, is either 0 or (void*)0. NULL cannot be anything else.
However, when a null pointer constant is assigned to a pointer, you get a null pointer, which may not have the value zero, even though it compares equal to a null pointer constant. The code if (!p) has nothing to do with the NULL macro, you are comparing a null pointer against the arithmetic value zero.
So in theory, code like int* p = NULL may result in a null pointer p which is different from zero.
Back in the day, STRATUS computers had null pointers as 1 in all languages.
This caused issues for C, so their C compiler would allow pointer comparison of 0 and 1 to return true
This would allow:
void * ptr=some_func();
if (!ptr)
{
return;
}
To return on a null ptr even though you could see that ptr had a value of 1 in the debugger
if ((void *)0 == (void *)1)
{
printf("Welcome to STRATUS\n");
}
Would in fact print "Welcome to STRATUS"
If your compiler is any good there are two things (and only two things) to watch out for.
1: Static default initialized (that is, not assigned) pointers won't have NULL in them.
2: memset() on a struct or array or by extension calloc() won't set pointers to NULL.

Can the expression "(ptr == 0) != (ptr == (void*)0)" really be true?

I read this claim in a forum thread linked to in a comment by #jsantander:
Keep in mind that when you assign or compare a pointer to zero, there is some special magic that occurs behind the scenes to use the correct pattern for the given pointer (which may not actually be zero). This is one of the reasons why things like #define NULL (void*)0 are evil – if you compare a char* to NULL that magic has been explicitly (and probably unknowingly) turned off, and an invalid result may happen. Just to be extra clear:
(my_char_ptr == 0) != (my_char_ptr == (void*)0)
So the way I understand it, for an architecture where the NULL pointer is, say, 0xffff, the code if (ptr), would compare ptr to 0xffff instead of to 0.
Is this really true? Is it described by the C++ standard?
If true, it would mean that 0 can be safely used even for architectures that have a non-zero NULL pointer value.
Edit
As an extra clarification, consider this code:
char *ptr;
memset(&ptr, 0, sizeof(ptr));
if ((ptr == (void*)0) && (ptr != 0)) {
printf("It can happen.\n");
}
This is how I understand the claim of this forum post.
There's two parts to your question. I'll start with:
If true, it would mean that 0 can be safely used even for architectures that have a non-zero NULL pointer value.
You are mixing up "value" and "representation". The value of a null pointer is called the null pointer value. The representation is the bits in memory that are used to store this value. The representation of a null pointer could be anything, there is no requirement that it is all-bits-zero.
In the code:
char *p = 0;
p is guaranteed to be a null pointer. It might not have all-bits-zero.
This is no more "magic" than the code:
float f = 5;
f does not have the same representation (bit-pattern in memory) as the int 5 does, yet there is no problem.
The C++ standard defines this. The text changed somewhat in C++11 with the addition of nullptr; however in all versions of C and C++, the integer literal 0 when converted to a pointer type generates a null pointer.
From C++11:
A null pointer constant is an integral constant expression prvalue of integer type that evaluates to 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. Such a conversion is called a null pointer conversion.
0 is a null pointer constant, and (char *)0 for example is a null pointer value of type char *.
It's immaterial whether a null pointer has all-bits-zero or not. What matters is that a null pointer is guaranteed to be generated when you convert an integral constexpr of value 0 to a pointer type.
Moving onto the other part of your question. The text you quoted is complete garbage through and through. There's no "magic" in the idea that a conversion between types results in a different representation, as I discuss above.
The code my_char_ptr == NULL is guaranteed to test whether or not my_char_ptr is a null pointer.
It would be evil if you write in your own source code, #define NULL (void*)0. This is because it is undefined behaviour to define any macro that might be defined by a standard header.
However, the standard headers can write whatever they like so as the Standard requirements for null pointers are fulfilled. Compilers can "do magic" in the standard header code; for example there doesn't have to be a file called iostream on the filesystem; the compiler can see #include <iostream> and then have hardcoded all of the information that the Standard requires iostream to publish. But for obvious practical reasons, compilers generally don't do this; they allow the possibility for independent teams to develop the standard library.
Anyway, if a C++ compiler includes #define NULL (void *)0 in its own header, and as a result something non-conforming happens, then the compiler would be non-conforming obviously. And if nothing non-conforming happens then there is no problem.
I don't know who the text you quote would direct its "is evil" comment at. If it is directed at compiler vendors telling them not to be "evil" and put out non-conforming compilers, I guess we can't argue with that.
I think the forum post you link to is incorrect (or we have misinterpreted what it means by !=). The two sub-expressions have different semantics but the same result. Assuming that my_char_ptr really has type char* or similar, and a valid value:
my_char_ptr == 0 converts 0 to the type of my_char_ptr. That yields a null pointer because 0 is an example of a so-called "null pointer constant", which is defined in the standard. It then compares the two. The comparison is true if and only if my_char_ptr is a null pointer, because only null pointers compare equal to other null pointers.
my_char_ptr == (void*)0 converts my_char_ptr to void*, and then compares that to the result of converting 0 to void* (which is a null pointer). The comparison is true if and only if my_char_ptr is a null pointer because when you convert a pointer to void* the result is a null pointer if and only if the source is a null pointer.
The issue of whether null pointers are represented with 0 bits or not is interesting but irrelevant to the analysis of the code.
The practical danger of thinking that NULL is a null pointer (rather than merely a null pointer constant) is that you might think that printf("%p", NULL) has defined behaviour, or that foo(NULL) will call the void* overload of foo rather than the int overload, and so on.
No, because they incidentially used the only case where it is guaranteed to work as example.
Otherwise, yes.
Although practially you probably won't ever see a difference, strictly speaking, the concern is correct.
The C++ standard requires (4.10) that:
A null pointer constant (which is either an integral constant expression that evaluates to 0, or a prvalue of type std::nullptr_t) converts to the null pointer of any type.
Two null pointers of the same type compare equal.
A prvalue of type pointer-to-cv-T can be converted to pointer-to-cv-void, and the null pointer value will be adjusted accordingly.
Pointers of derived classes can be converted to pointers of base classes, and the null pointer value will be adjusted accordingly.
This means, if you are pedantic about the wording, that the null pointers of void and char and foo_bar are not only not necessarily zero bit patterns, but also are not necessarily the same. Only null pointers of the same type are necessarily the same (and actually, not even that is true, it only says that they must compare equal, which isn't the same thing).
The fact that it explicitly says "The null pointer value is converted to the null pointer value of the
destination type" signifies that this is not only an absurd, theoretical contortion of the wording, but indeed intended as a legitimate feature of an implementation.
That is regardless of the fact that the same literal 0 will convert to the null pointer of each type.
Incidentially, in their example, they compared to void*, which will work due to the above conversion rule. Also, in practice, the null pointer for every type is a zero bit pattern on every architecture that you are likely to encounter in your life (though of course, that's not guaranteed).
First, I'm not sure that (charPtr == 0) != (charPtr == (void*)0) is allowed, even in C++. In both cases, you're
converting a null pointer constant (0) to a pointer, which
results in a null pointer. And all null pointers should compare
equal.
Second, while I don't know the context of the passage you cite,
you really don't have to worry about NULL being (void*)0:
user code cannot legally define NULL (at least not if it
includes any standard headers), and the C++ standard requires
NULL to be defined as a null pointer constant; i.e. an
constant integral expression evaluating to 0. (Note that
despite its name, a null pointer constant cannot have a pointer
type.) So it might be 0 (the more or less standard
definition, since the very beginnings of C), or possibly 0L,
or even (1-1), but not ((void*)0). (Of course, it might
also be something like __nullptr, a compiler built-in constant
which evaluates to integer 0, but triggers a warning if not
converted immediately into a null pointer.
Finally: there's no requirement that a null pointer have all
0 bits, and there certainly have been cases where this wasn't
the case. On the other hand, there is a requirement that
comparing a null pointer to a null pointer constant will
evaluate to true; it's up to the compiler to make it work. And
since NULL is required to be defined as a null pointer
constant, whether you use NULL or 0 is purely a question of
personal preference and convention.
EDIT:
Just to clarify a little: the critical point involves conversion
of a "null pointer constant", an integral constant expression
evaluating to 0. What can surprise people is:
int zero = 0; // NOT a constant expression.
void* p1 = reinterpret_cast<void*>( zero );
void* p2 = 0;
if ( p1 == p2 ) // NOT guaranteed!
The results of converting a non-constant expression which
evaluates to zero to a pointer is not guaranteed to be a null
pointer.

Null pointer in C++

When in C++ I declare a null pointer to be int* p=0, does that mean the zero is some special constant of integer pointer type, or does it mean that p is pointing to address 0x0?
Of course for that 0x0 would have to be an special address to which C++ never touches during allocation of variables/arrays.
The C++ standard defines that the integer constant 0 converts to a null pointer. This does not mean that null pointers point to address 0x0. It just means that the text '0' turns into a null pointer when converted to a pointer.
Of course, making null pointers have a representation other than 0x0 is rather complicated, so most compilers just let 0x0 be the null pointer address and make sure nothing is ever allocated at zero.
Note that using this zero-conversion is considered bad style. Use NULL (which is a preprocessor macro defined as 0, 0L, or some other zero integral constant), or, if your compiler is new enough to support it, nullptr.
It means that an integral constant expression with value zero has a special meaning in C++; it is called a null pointer constant. when you use such an expression to initialize a pointer with, or to assign to a pointer, the implementation ensures that the pointer contains the appropriately typed null pointer value. This is guaranteed to be a different value to any pointer pointing at a genuine object. It may or may not have a representation that is "zero".
ISO/IEC 14882:2011 4.10 [conv.ptr] / 1:
A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to 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.
It's a special value, which by the standard is guaranteed to never be equal to a pointer that is pointing to an object or a function. The address-of operator & will never yield the null pointer, nor will any successful dynamic memory allocations. You should not think of it as address 0, but rather as special value that indicates that the pointer is pointing nowhere. There is a macro NULL for this purpose, and the new idiom is nullptr.
It means that it's not pointing to anything.
The value of the pointer is just 0. It doesn't necessarily mean it points to address 0x0. The NULL macro, is just a 0 constant.
the pointer points to address 0. On most platforms that is very special, but you should use NULL, because it is not always 0 (but very often).
Yes. Zero is a special constant. In fact, it's the only integral constant which can be used, without using explicit cast, in such statements:
int *pi = 0; //ok
char *pc = 0; //ok
void *pv = 0; //ok
A *pa = 0; //ok
All would compile fine.
However, if you use this instead:
int *pi = 1; //error
char *pc = 2; //error
void *pv = 3; //error
A *pa = 4; //error
All would give compilation error.
In C++11, you should use nullptr, instead of 0, when you mean null pointer.
In your example 'p' is the address of an int. By setting p to 0 you're saying there is an int at address 0. The convention is that 0 is the "not a valid address address", but its just a convention.
In pratice address 0 is generally "unmapped" (that is there is no memory backing that address), so you will get a fault at that address. That's not true in some embedded systems, though.
You could just as well pick any random address (e.g. 0xffff7777 or any other value) as the "null" address, but you would be bucking convention and confusing a lot of folks that read your code. Zero is generally used because most languages have support for testing is-zero is-not-zero efficiently.
See this related question: Why is address zero used for the null pointer?