可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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.
回答2:
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.
回答3:
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.
回答4:
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.
回答5:
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
回答6:
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, ...
回答7:
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