-1

Is ((char *)NULL - (char *)NULL) an UB?

IMO the answer is not trivial here. Any thoughts?

Godbolt link for experiments https://godbolt.org/z/zgVGk9

PS

I am not asking about adding something the the null pointer (as in the proposed dupe), but only about one particular case.

Community
  • 1
  • 1
0___________
  • 60,014
  • 4
  • 34
  • 74
  • Your compiler won't tell you if the behaviour is defined or not, only the specification can. – tadman Jan 24 '20 at 18:21
  • When experimenting with UB - yes, the specification is not helpful, because it is not defining the behavior – Eugene Sh. Jan 24 '20 at 18:26
  • 1
    It may not be very helpful, but that's where behaviour is defined, so if you can't find a definition, maybe it is undefined. Compilers do their best to implement those behaviours, but are not always correct. Perhaps this is devolving into semantics, but just because compiler X does something doesn't mean that's the defined behaviour. – tadman Jan 24 '20 at 18:26
  • "additive operators" (as mentioned in the duplicate) extends to subtraction. – S.S. Anne Jan 24 '20 at 18:40
  • 2
    The question marked as a duplicate discusses pointer+integer addition. It does not directly address subtraction of two pointers. But [N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 6.5.6 paragraph 9 does: "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 violation of a "shall" outside a constraint implies undefined behavior. – Keith Thompson Jan 24 '20 at 18:48
  • I remember same discussion here about `sizeof(*(double *)NULL)` – 0___________ Jan 24 '20 at 20:13
  • BTW - interesting why DV the question. – 0___________ Jan 24 '20 at 20:34

2 Answers2

10

The expression has undefined behavior.

(This question had been closed as a duplicate of this question, but that only discusses pointer+integer arithmetic, not the pointer-pointer arithmetic that this question asks about. Feel free to close the question as a duplicate if there's an existing question that specifically asks about pointer-pointer subtraction.)

N1570 is a draft of the 2011 ISO C standard. Section 6.5.6 paragraph 9, discussing subtraction, says:

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.

(A single non-array object is treated as an element of a 1-element array, but that doesn't apply here.)

The pointer yielded by the expression (char*)NULL does not point to an element of an array object, or to any other object (6.3.2.3 paragraph 3), so ((char *)NULL - (char *)NULL) violates the shall. Violation of a shall outside a constraint or runtime-constraint results in has undefined behavior (section 4 paragraph 2).

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • On the other hand the Standard states that the NULL pointer is `(void *)0`. Other parts of the standard state that `(void *)integer == (void *)integer` then NULL must be == NULL – 0___________ Jan 24 '20 at 19:43
  • 2
    @P__J__ It states that `(void*)0` is a null pointer constant, and that evaluating it yields a null pointer. `((void*)0)` is one possible definition for the `NULL` macro. Yes, `NULL == NULL`. Can you clarify how that's relevant? The fact that two values are equal doesn't imply that subtracting them is well defined. Subtraction and equality are defined separately and have different rules. – Keith Thompson Jan 24 '20 at 19:48
  • if NULL == NULL they reference the same object and this reference is distinct from any other on the system (that is only one thing guaranteed by the standard). Ergo 6.5.6 ans J2 do not apply in this case – 0___________ Jan 24 '20 at 19:54
  • 2
    @P__J__: `NULL` does not reference any object, by definition! – chqrlie Jan 24 '20 at 19:54
  • 2
    @P__J__: **6.3.2.3 Pointers**: *[...] 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.* – chqrlie Jan 24 '20 at 19:58
  • But itcompares equal to other NULL pointer as 6.3.2.4 *Anytwo null pointers shall compare equal.* Any equal pointer subtraction result is zero, and IMO you are 100% wrong and it is only opinion based as there is nothing in the standard which contradicts it – 0___________ Jan 24 '20 at 20:02
  • BTW UVed both answers. – 0___________ Jan 24 '20 at 20:09
  • 5
    @P__J__ I cited the wording in the standard that contradicts your claim. A null pointer compares equal to itself, but unequal to a pointer to an object. A null pointer does not point to an object. Your assertion that "any equal pointer subtraction result is zero" is unfounded. A special case is that `(void*)0 - (void*)0` is invalid, because pointer arithmetic is not defined for a pointer to `void`. But more generally, the standard's definition of pointer subtraction specifically requires that both operands point to objects. What object do you think `NULL` points to? – Keith Thompson Jan 24 '20 at 20:17
  • 1
    As a related topic, note that `(char*)NULL<=(char*)NULL`, `(char*)NULL<(char*)NULL`, `(char*)NULL>=(char*)NULL`, and `(char*)NULL>(char*)NULL` are all undefined, but `(char*)NULL==(char*)NULL` and `(char*)NULL!=(char*)NULL` are well-defined. – R.. GitHub STOP HELPING ICE Jan 24 '20 at 22:38
  • if `NULL == NULL` then `NULL - NULL = 0` – 0___________ Jan 25 '20 at 08:29
  • 2
    @P__J__: "if `NULL == NULL` then `NULL - NULL = 0`" - That does not follow. Pointer arithmetic is not numeric arithmetic. It follows the rules specified in the C standard. Those rules explicitly do not define the behavior of arithmetic on null pointers. Restating your claim does not make it any more valid. And if `NULL` is defined as `((void*)0)`, then `NULL - NULL` is a constraint violation. – Keith Thompson Jan 25 '20 at 18:26
5

The short answer is yes, behavior is undefined:

C17 J.2 Undefined behavior (informative)

The behavior is undefined in the following circumstances:

  • Pointers that do not point into, or just beyond, the same array object are subtracted (6.5.6).

The long (normative) answer is this:

C17 6.5.6 Additive operators

...

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 <stddef.h> 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.

Since (char *)NULL does not point to an array, the expression (char *)NULL - (char *)NULL has undefined behavior, but as you have tested on various compilers, you may well get the value 0 determined at compile time. The C Standard does not guarantee this, but it would take a perverse compiler to produce anything else.

Community
  • 1
  • 1
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • NULL is `(void *)0`. Two pointers defined as the cast from the same integer are considered equal. Then NULL == NULL. If they are equal, they point to the same object. Then C15 J.2 is not valid here. Also 6.5.6 – 0___________ Jan 24 '20 at 19:46
  • 2
    `NULL == NULL` of course: comparing pointers to different objects or to non-objects is valid for `==` and `!=` but not for `<`, `<=`, `>`, nor `>=`. If 2 valid pointers compare equal, they point to the same array, or object, considered as an array of size 1, but this does not imply anything for non-valid pointers. Common sense tells you the value is `0`, but the C Standard does not guarantee it. A strict validation suite would instrument the code and insert assertions on `p-q` expressions checking for validity of `p` and `q` and verify they point to the same array. The posted code would fail. – chqrlie Jan 24 '20 at 19:53
  • Nothing in the standard says that two same pointers cannot be subtracted. NULL is only guaranteed to be distinct from any other pointers - but nothing else. So subtracting (adding) null to any other pointer is obviously UB but not NULL to NULL – 0___________ Jan 24 '20 at 19:57
  • 3
    @P__J__: The Standard is unambiguous: *When two pointers are subtracted, both shall point to elements of the same array object*... `NULL` does not point to an object, hence it violates this constraint. – chqrlie Jan 24 '20 at 20:00
  • NULL is not equal to reference any other object, and nothing else. As we do not dereference it - the result is IMO zero and it is not an UB. – 0___________ Jan 24 '20 at 20:05
  • The result is indeed most likely `0`, which does not contradict the fact that the Standard does not describe this as having defined behavior. Here is another example: what is the value of `-1 >> 0`? is it `-1` or implementation defined? answer: implementation defined. – chqrlie Jan 24 '20 at 20:09
  • It does not also describe it explicitly (cited points IMO do not apply here) as an UB. – 0___________ Jan 24 '20 at 20:11
  • 2
    The constraint is clear, `NULL` does **not** point to an object, hence the constraint is violated. – chqrlie Jan 24 '20 at 20:12
  • NULL is not equal to the reference to any other object. Not that it does not reference any object. It is something completely different. – 0___________ Jan 24 '20 at 20:13
  • 2
    reductio ad absurdum: If there existed such an object, a reference to this object would equal `NULL`, which contradicts the hypothesis. – chqrlie Jan 24 '20 at 20:18
  • it is tagged `language-lawyer` not common sense. So we can only interpret the Standard as the Bible. Nothing else. – 0___________ Jan 24 '20 at 20:20
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206608/discussion-between-chqrlie-and-p-j). – chqrlie Jan 24 '20 at 20:23
  • 3
    @chqrlie It's not a *constraint* it's a "shall" outside a constraint, That makes it undefined behavior. (A constraint violation must be diagnosed.) – Keith Thompson Jan 25 '20 at 00:22