81

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.

ScottishTapWater
  • 3,656
  • 4
  • 38
  • 81
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/204757/discussion-on-question-by-roberts-reinstate-monica-can-i-use-null-as-substitut). – Samuel Liew Dec 24 '19 at 01:11
  • 1
    It would really be better to ask two separate questions, one for C and one for C++. – Konrad Rudolph Dec 24 '19 at 18:12

7 Answers7

85

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 4
    You should point out that "which more typically has type `void *`" is only true of C. `void *` is not a legal type for C++ (because you can't assign `void*` to any other pointer type). In C++89 and C++03, in fact `NULL` *must* be of type `int`, but in later versions it can be (and usually is) `nullptr_t`. – Martin Bonner supports Monica Dec 23 '19 at 18:32
  • 1
    You are also wrong the converting `void*` to `int` is undefined behaviour. It is not; it is implementation specified behaviour. – Martin Bonner supports Monica Dec 23 '19 at 18:36
  • @MartinBonnersupportsMonica, In those contexts where C specifies that a pointer is converted to an integer, the result of the conversion is indeed implementation-specified, but that's not what I'm talking about. It is assignment of a pointer to an lvalue of integer type (without explicitly converting via a cast) that has undefined behavior. The language does not define an automatic conversion there. – John Bollinger Dec 23 '19 at 18:58
  • @MartinBonnersupportsMonica, I have edited to be more inclusive of C++ considerations. In any event, the central theme applies equally to both languages: if you want an integer 0 then write it explicitly as an integer constant of appropriate type. – John Bollinger Dec 23 '19 at 19:08
32

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • For completeness, 0L is also a null pointer constant and could be used as `NULL` in both languages. – eerorika Dec 21 '19 at 16:29
  • @eerorika - I think we can do one better. Copied the full definition into the answer. – StoryTeller - Unslander Monica Dec 21 '19 at 16:30
  • @eerorika: Is 0L ALWAYS a NULL pointer? I'm recalling segmented architectures like the 80286, where a far pointer was two separate registers, and IIRC you could validly set the offset to zero. (Quite apart from the fact that IMHO it's always better to say exactly what you mean in code. If you're assigning to a numeric variable, use 0 or 0.0; if to a pointer, then NULL.) – jamesqf Dec 22 '19 at 02:58
  • 1
    @jamesqf The standard says that integer constant with value 0 is a null pointer constant. Therefore 0L is a null pointer constant. – eerorika Dec 22 '19 at 03:02
  • 1
    @eerorika: Just what the world needs, standards that ignore reality :-) 'Cause if I'm remembering my 80286 assembly correctly, you can't even assign a far pointer as a single operation, so the compiler writers would have to special-case it. – jamesqf Dec 23 '19 at 03:45
  • 3
    @jamesqf Per [the C FAQ](http://c-faq.com/null/machexamp.html), re: making `0` a null pointer constant: "evidently as a sop to all the extant poorly-written C code which made incorrect assumptions" – Andrew Henle Dec 23 '19 at 11:50
  • 3
    @jamesqf, that any and every integer constant with value 0 is a null-pointer constant (in C) has nothing to do with hardware pointer implementations. Note also that standard C does not recognize a distinction between near and far pointers in any case, but does support pointer-to-pointer assignments. It also supports (some) pointer comparisons, which present interesting issues for segmented addressing formats such as the 286's. – John Bollinger Dec 23 '19 at 14:57
  • @jamesqf: it ignores reality in a way that doesn't matter. Note that it's only ICEs with value 0 that are guaranteed to work, not just any old integer expression that happens to have value 0. So yes, the compiler can special-case it if needed, and substitute in whatever the actual null-pointer-pattern is, because it's known at compile time whether or not an integer expression is an ICE or not. – Steve Jessop Dec 24 '19 at 13:24
  • Then, `void *foo = 0L;` will not necessarily have the same result as `long get_null() { return 0;}` ... `void *foo = get_null();`. I don't believe this poses any problem for compiler-writers, just for the misguided author of `get_null`. The standard in effect says that assigning an ICE with value 0 to a pointer has a special meaning, which is to set the pointer to null. Once you know the null pointer representation this is not hard to implement on any architecture :-) – Steve Jessop Dec 24 '19 at 13:27
  • Or, to put in another way, treating 0-valued ICEs as a special case was, prior to C++11, considered better than having a null pointer type. I suspect (but I'm not sure) that this is because it was actually considered *easier* for compiler-writers to implement the special case than the special type! – Steve Jessop Dec 24 '19 at 13:33
22

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.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
10

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.

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
Damon
  • 67,688
  • 20
  • 135
  • 185
6

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

Community
  • 1
  • 1
phuclv
  • 37,963
  • 15
  • 156
  • 475
5

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, ...

pmg
  • 106,608
  • 13
  • 126
  • 198
  • I've seen `#define NULL (void *)0` before. Are you sure NULL and plain 0 are 100% equivalent? – machine_1 Dec 21 '19 at 15:15
  • 2
    In the context of pointer, yes ... condition emphasized in my answer, thank you – pmg Dec 21 '19 at 15:16
  • @phuclv: I have absolutely no idea about C++. My answer (except the bit between parenthesis) is about C – pmg Dec 21 '19 at 15:25
  • @phuclv `ptr + NULL` is not using `NULL` in the context of pointers – pmg Dec 21 '19 at 15:31
  • "0 and NULL are 100% equivalent" - No they are not. NULL is often (but not always) defined as `((void*)0)` - which is *not* the same as `0`. And certainly not the same as `nullptr`. – Jesper Juhl Dec 21 '19 at 16:07
  • 3
    @JesperJuhl: **in the context of pointers** they are 100% equivalent. I have no idea what `nullptr` is, but `((void*)0)` and `0` (or `'\0'`) are equivalent in the context of pointers ... `if (ptr == '\0' /* or equivalent 0, NULL */)` – pmg Dec 21 '19 at 16:11
5

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
Build Succeeded
  • 1,153
  • 1
  • 10
  • 24
  • The question is about assigning NULL to 0 for integers. In this sense, nothing changes with nullptr instead of NULL. – ivan.ukr Dec 25 '19 at 09:17
  • By the way, C++ do not have the NULL concept after C++11. Author might be confused about using the constexpr or define old way initialization. https://en.cppreference.com/w/cpp/language/default_initialization – Build Succeeded Dec 25 '19 at 10:51