Subtraction of Two Nullptr Values Guaranteed to Be Zero

Subtraction of two nullptr values guaranteed to be zero?

Yes, that is valid. It would be undefined in C, but C++ has added a special exception to the - operator to define the behaviour.

5.7 Additive operators [expr.add]

7 If the value 0 is added to or subtracted from a pointer value, the result compares equal to the original pointer value. If two pointers point to the same object or both point one past the end of the same array or both are null, and the two pointers are subtracted, the result compares equal to the value 0 converted to the type std::ptrdiff_t.

Can we subtract NULL pointers?

Subtracting two NULL pointers is not allowed. Section 6.5.6p9 of the C standard states:

When two pointers are subtracted, both shall point to elements of the
same array object, or one past the last element of the array
object; the result is the difference of the subscripts of the
two array elements.
The size of the result is
implementation-defined, and its type (a signed integer type) is
ptrdiff_t defined in the header. If the result is not
representable in an object of that type, the behavior is
undefined. In other words, if the expressions P and Q point to,
respectively, the i
-th and j
-th elements of an array object, the expression (P)-(Q) has the value i−j provided the value fits in an object of type ptrdiff_t . Moreover,
if the expression P points either to an element of an array object or
one past the last element of an array object, and the expression Q
points to the last element of the same array object, the expression
((Q)+1)-(P) has the same value as ((Q)-(P))+1 and as
-((P)-((Q)+1)) , and has the value zero if the expression P points one past the last element of the array object, even
though the expression (Q)+1 does not point to an element of the array
object.

Because neither pointer points to an array object, the behavior is undefined.

You also can't subtract two void * because void is an incomplete type, and pointer subtraction depends on knowing the size of the pointed-to object. You could cast each pointer to a intptr_t and subtract those, however that would give you the byte difference between the pointers, not the index difference.

Is the behavior of subtracting two NULL pointers defined?

In C99, it's technically undefined behavior. C99 §6.5.6 says:

7) For the purposes of these operators, a pointer to an object that is not an element of an
array behaves the same as a pointer to the first element of an array of length one with the
type of the object as its element type.

[...]

9) When two pointers are subtracted, both shall point to elements of the same array object,
or one past the last element of the array object; the result is the difference of the
subscripts of the two array elements. [...]

And §6.3.2.3/3 says:

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 since a null pointer is unequal to any object, it violates the preconditions of 6.5.6/9, so it's undefined behavior. But in practicality, I'd be willing to bet that pretty much every compiler will return a result of 0 without any ill side effects.

In C89, it's also undefined behavior, though the wording of the standard is slightly different.

C++03, on the other hand, does have defined behavior in this instance. The standard makes a special exception for subtracting two null pointers. C++03 §5.7/7 says:

If the value 0 is added to or subtracted from a pointer value, the result compares equal to the original pointer value. If two pointers point to the same object or both point one past the end of the same array or both are null, and the two pointers are subtracted, the result compares equal to the value 0 converted to the type ptrdiff_t.

C++11 (as well as the latest draft of C++14, n3690) have identical wording to C++03, with just the minor change of std::ptrdiff_t in place of ptrdiff_t.

Subtracting null pointer from a normal pointer?

It has undefined behaviour, since pointer arithmetic is only defined within an array.

In practice, on a machine that represents pointers by a numerical address, and with sufficiently large integers, and a null pointer represented by address zero, it will convert a byte address into a word address. That is, it gives the number of int-sized lumps of memory between the null pointer's address and the address p.

Also there was a comment that this is a pure and re-entrant function.

"Pure" means that it has no side-effects, and the result only depends on its inputs. "re-entrant" means that it's safe to call while it's already in progress (for example, from an interrupt handler) - it has no internal static data.

Is NULL guaranteed to be 0?

Is NULL guaranteed to be 0?

According to the standard, NULL is a null pointer constant (i.e. literal). Exactly which one, is implementation defined.

Prior to C++11, null pointer constants were integral constants whose integral value is equal to 0, so 0 or 0l etc.

Since C++11, there is a new null pointer literal nullptr and NULL may be defined as being nullptr. (And thus literal interpretation of Bjarne's quote has become obsolete).

Prior to standardisation: NULL may be defined as (void*)0 in C. Since C++ was based on C, it is likely that some C++ dialects pre-dating the standard might have used that definition, but such definition is not conformant with standard C++.


And for completeness: As explained in more detail in SO post linked in a comment below, null pointer constant being 0 does not necessarily mean that the value of the null pointer address is 0 (although the address being 0 is quite typical).

What can be concluded about this:

  • Don't use NULL to represent the number zero (use 0 with appropriate type suffix if appropriate), nor to represent a null-terminator character (use '\0').
  • Don't assume that NULL resolves to a pointer overload.
  • To represent a null pointer, don't use NULL but instead use nullptr if your standard is >= C++11. In older standard you can use (T*)NULL or (T*)0 if you need it for overload resolution... that said there are probably very few cases where overloading integers with pointers makes any sense.
  • Consider that the definition may differ when converting from C to C++ and vice versa.
  • Don't memset (or type pun) zero bits into a pointer. That's not guaranteed to be the null pointer.

Does both NULL and nullptr point to same address or NULL points to nowhere? (I have seen in many places that nullptr points to zero address)

It might be useful to clear up some terminology. A pointer points to an object. The value of a pointer is an address value. There is a special address value for pointers that do not point to an object. We call such pointers "null pointers". When you assign the NULL or nullptr values to a pointer, it becomes a null pointer.

Since all null pointers compare equal, static_cast<void*>(NULL) == static_cast<void>(nullptr).

Can I guarantee that the address of nullptr is always 0?

Yes.

A pointer initialised from nullptr is a null pointer.

Comparing a null pointer to the literal 0 (or to a std::nullptr_t, which nullptr is; together these are null pointer constants) yields true, always.

  • http://eel.is/c++draft/basic.fundamental#14
  • http://eel.is/c++draft/conv.ptr#1

You can't do this with any old expression, though; even if integer i is 1, i-i is not a valid null pointer constant, despite evaluating to 0 at runtime. Your
program will not compile if you try to do this. Only the literal 0 is a valid null pointer constant that can be compared to pointers.

Also, that does not necessarily mean that it has all bits zero, though! Much like how converting a bool to int gives you zero or one, but the actual underlying bits of the bool can be whatever the implementation likes.

Finally, note that your terminology is slightly off; per [support.types.nullptr/1], nullptr itself has no address that can be taken

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?

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.

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

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.



Related Topics



Leave a reply



Submit