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
Related
Static analysis flagged this code as nullPointerArithmetic:
static_cast<BYTE*>(NULL) + p_row_fields->offsets.back(), // field offset
where NULL is defined as #define NULL 0
and offsets resolves through typedef to std::vector<int>
The line in question is passed as a BYTE* argument to a method call.
My question is - What could be the purpose of this idiom?
Is there any difference between what's shown and the less eclectic direct cast:
static_cast<BYTE*>(p_row_fields->offsets.back())
Null pointer constant converts to any pointer type, resulting in null pointer value of that pointer type. Thus, static_cast<BYTE*>(NULL) yields a null pointer of type BYTE*. This conversion works implicitly as well.
Strictly speaking, the behaviour of the pointer arithmetic on null is undefined by the standard because (or assuming that) there is no array at the null address.
As far as what the behaviour might be in practice assuming the compiler allows this, one might expect it to behave same as :
reinterpret_cast<Byte*>(
static_cast<std::intptr_t>(
p_row_fields->offsets.back()
)
)
While this is not UB, there is still technically no standard guarantee that the resulting address is what was intended.
Is there any difference between what's shown and the less eclectic direct cast:
static_cast<BYTE*>(p_row_fields->offsets.back())
Yes, there is a difference. Of integer expressions, only compile time constant prvalues with value 0 are convertible to pointer types. Values other than 0, and lvalues such as p_row_fields->offsets.back() can not be static-casted to pointers.
As such, the quoted cast is ill-formed.
The C++ standard defines the null pointer to be 0. In a lot of code I've seen the NULL macro used however.
Why is it so? If the standard defines NULL to be 0 anyway, why use a special constant for it?
Is it just an aesthetic choice, or is there a more practical reason for it? Or was there a reason in the past that is no longer valid, i.e. holdovers from older drafts of the C++ standard?
How and why did NULL begin to be used instead of 0?
This is a leftover from C, where NULL is often defined as (void*)0. In C, because of conversion rules, it makes NULL implicitly convertible to any other pointer type while remaining non convertible to integer types:
int* a = NULL; /* OK */
int b = NULL; /* ERROR */
C++ however does not have such loose type conversions, and therefore NULL is defined as 0 in C++. While this means that int b = NULL; is legal, compilers are generally smart enough to emit a warning if you do so as they recognized that NULL is a special macro thus preserving some of the type safety... when the warning works.
Of course, in C++11, one should use nullptr instead. In C++03 though even Stroustrup already recommended using 0 rather than a macro.
NULL is more descriptive than 0, and they have different fundamental meanings. 0 is an integer number, while NULL indicates something like "no value" or "points to nothing". Similarly PI is more descriptive in your code than 3.1415926535897.
This was a compromise until C++11 where you should be using the nullptr keyword. Now, in addition to the added clarity, you get actual compiler support for your intention. For example int x = NULL; doesn't really make much sense because even though NULL boils down to an integer, its meaning is not that of an integer. int x = nullptr; will properly error.
I would say it is just an influence of C programmers, where NULL was used as a standard value for initializing pointers.
A pointer is not an integer - their types are different. So int* p = 0; somehow looks wrong, especially given the case the int* p = 5; is a compiler error.
People are using NULL (reference) to have a special word for a null pointer constant, i.e. indicating a pointer which points to nothing. But NULL is just a workaround and often realized as a macro which in C++ (often) resolves to the integer 0.
C++11 has introduced nullptr (reference) to solve this problem once and for all.
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.
This question already has answers here:
What is the difference between NULL, '\0' and 0?
(11 answers)
Closed 9 years ago.
As mentioned in the question, I have been using NULL and false(in C++) interchangeably with 0 or 0x0 and so on. I was curious to know if they held any special meaning other than being synonyms of 0.
for some platforms NULL is not 0x0
From Is NULL in C required/defined to be zero? :
NULL is guaranteed to be zero, perhaps casted to (void *)1.
C99, §6.3.2.3, ¶3
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.
And note 55 says:
55) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant.
Notice that, because of how the rules for null pointers are formulated, the value you use to assign/compare null pointers is guaranteed to be zero, but the bit pattern actually stored inside the pointer can be any other thing (but AFAIK only few very esoteric platforms exploited this fact, and this should not be a problem anyway since to "see" the underlying bit pattern you should go into UB-land anyway).
So, as far as the standard is concerned, the two forms are equivalent (!ptr is equivalent to ptr==0 due to §6.5.3.3 ¶5, and ptr==0 is equivalent to ptr==NULL); if(!ptr) is also quite idiomatic.
That being said, I usually write explicitly if(ptr==NULL) instead of if(!ptr) to make it extra clear that I'm checking a pointer for nullity instead of some boolean value.
Notice that in C++ the void * cast cannot be present due to the stricter implicit casting rules that would make the usage of such NULL cumbersome (you would have to explicitly convert it to the compared pointer's type every time).
Well, NULL may not be zero, it just usually is. It's dependant on your platform - here are some nasty examples of non-zero NULL machines.
NULL has got wide meaning in pointers. Most probably a pointer declared as NULL is something that cannot be referenced but just can be assigned something. whereas 0 or 0x0 can be the value of the pointer which can be both referenced and assigned.
Generally in C++ you shouldn't use NULL. In C, NULL is a macro for (void*)0, whereas in C++ you should use 0 instead of NULL. But you can't use false instead of 0 for pointers! Apart from that, they are actually the same, sometimes causing confusion.
This is why in C++11 a nullptr was defined as use for pointers.
In C and historic C++, NULL has to be a zero-valued integer constant, typically 0. I think that C can include an explicit cast to void*, but C++ can't since that language doesn't allow implicit conversion from void* to other pointer types.
In modern C++, it could be either nullptr instead.
In either case, any zero-valued integer constant (including oddities such as '\0' and false) is convertible to a null pointer value.
from linux kernel stddef.h
#define NULL ((void *)0)
enum {
false = 0,
true = 1
};
So it is not exactly false
even though this post claims it is NULL , 0x0 and false are almost the same.
I found a bug in my code where I compared the pointer with '\0'.
Wondering why the compiler didn't warn me about this bug I tried the following.
#include <cassert>
struct Foo
{
char bar[5];
};
int main()
{
Foo f;
Foo* p = &f;
p->bar[0] = '\0';
assert(p->bar == '\0'); // #1. I forgot [] Now, comparing pointer with NULL and fails.
assert(p->bar == 'A'); // #2. error: ISO C++ forbids comparison between pointer and integer
assert(p->bar[0] == '\0'); // #3. What I intended, PASSES
return 0;
}
What is special about '\0' which makes #1 legal and #2 illegal?
Please add a reference or quotation to your answer.
What makes it legal and well defined is the fact that '\0' is a null pointer constant so it can be converted to any pointer type to make a null pointer value.
ISO/IEC 14882:2011 4.10 [conv.ptr] / 1:
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' meets the requirements of "integral constant expression prvalue of integer type that evaluates to zero" because char is an integer type and \0 has the value zero.
Other integers can only be explicitly converted to a pointer type via a reinterpret_cast and the result is only meaningful if the integer was the result of converting a valid pointer to an integer type of sufficient size.
'\0' is simply a different way of writing 0. I would guess that this is legal comparing pointers to 0 makes sense, no matter how you wrote the 0, while there is almost never any valid meaning to comparing a pointer to any other non-pointer type.
This is a design error of C++. The rule says that any integer constant expression with value zero can be considered as the null pointer constant.
This idiotic highly questionable decision allows to use as null pointer '\0' (as you found) but also things like (1==2) or even !!!!!!!!!!!1 (an example similar to one that is present on "The C++ programming language", no idea if Stroustrup thinks this is indeed a "cool" feature).
This ambiguity IMO even creates a loophole in the syntax definition when mixed with ternary operator semantic and implicit conversions rules: I remember finding a case in which out of three compilers one was not compiling and the other two were compiling with different semantic ... and after wasting a day on reading the standard and asking experts on c.c.l.c++.m I was not able to decide which of the three compilers was right.