3
  1. Is NULL - NULL defined.?

  2. Is (char *)NULL - (char *)NULL defined.?

  3. Is (uintptr_t)NULL - (uintptr_t)NULL defined?

I know that it works in all used by me implementations. But how does it look like from the standard point of view? I cant find the clear answer.

Edit: From the dupe I assume that the question one answer is: YES.

What about the second and third questions?

0___________
  • 60,014
  • 4
  • 34
  • 74
  • 5
    The C Standard documents `NULL` as being defined as a macro *which expands to an implementation-defined null pointer constant* Depending on the actual definition, the expression `NULL - NULL` may have a defined value or not. For example: * if `NULL` is defined as `#define NULL 0`, then `NULL - NULL` is actually a constant expression of type `int` with a value of `0`. * if `NULL` is defined as `#define NULL ((void *)0)`, the expression `NULL - NULL` is a constraint violation as arithmetic is not defined on void pointers. – chqrlie Nov 08 '20 at 14:34
  • 2
    Not me, but I will add to what is alluded to in an answer: C18 **§6.5.6.9** 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."* But a `NULL` value does not point to any object, and casting it to another pointer type does not change that. – Weather Vane Nov 08 '20 at 15:02

1 Answers1

7

The C Standard documents NULL as being defined as a macro which expands to an implementation-defined null pointer constant

Depending on the actual definition, the expression NULL - NULL may have a defined value or not. For example:

  • if NULL is defined as #define NULL 0, then NULL - NULL is actually a constant expression of type int with a value of 0.
  • if NULL is defined as #define NULL ((void *)0), the expression NULL is a constraint violation as arithmetic is not defined on void pointers.

The second question: are (char*)NULL - (char*)NULL or (uintptr_t)NULL - (uintptr_t)NULL defined?. These expressions are no longer constraint violations:

  • since the conversion from void * to an arithmetic type is implementation defined, nothing can be said of the value of (uintptr_t)NULL - (uintptr_t)NULL. It will be 0 on most current systems, but the C Standard does not define it.

  • converting to (char *) is a slightly different matter: the difference of 2 pointers is only defined if they point to the same array, or the position after the last element of the array, an object being considered an array of one element for this discussion. This is not the case in (char *)NULL, which is a null pointer, hence does not point to any array or object.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Yes I know how it works in most implementations. I have edited the question adding two additional cases. – 0___________ Nov 08 '20 at 14:38
  • 1
    ***`but since the conversion from void * to an arithmetic type is implementation defined`*** yes indeed, but here we have two **identical** conversions. I do not see anything in the C standard stating that two identical statements can give different results. – 0___________ Nov 08 '20 at 14:42
  • 2
    @P__JsupportswomeninPoland, changing the nature of the question like that is a bit unfair to chqrlie who answered beforehand. – Elliott Nov 08 '20 at 14:42
  • 1
    @Elliott I have added two additional questions. `chqrlie` answer is same good as before the changes. I think you misunderstood the idea of not changing questions. – 0___________ Nov 08 '20 at 14:44
  • THx. Here is something for anothewr question which I will ask now. – 0___________ Nov 08 '20 at 14:55
  • 1
    @P__JsupportswomeninPoland: Re “I do not see anything in the C standard stating that two identical statements can give different results”: Something is not specified if it is stated that it is not unspecified. It is specified only if there is an affirmative statement for it somewhere. A `void *` might have padding bits, e.g., if the implementation uses a 48-bit address space but uses 64 bits for pointers and disregards the upper 16 bits. Although it is unlikely to occur for `(void *) 0`, converting to a pointer type might just set the low 48 bits and acquire whatever happened to be… – Eric Postpischil Nov 08 '20 at 15:12
  • 2
    … lying around in the upper 16 bits. So `void *a = &x, *b = &x;` might result in different bits in `a` and `b` even though they point to the same thing and compare equal. The conversion to `uintptr_t` might be defined to include all 64 bits even though 16 are irrelevant to the address. So `(uintptr_t) a` might not equal `(uintptr_t) b`. From this, we see it is allowed by the C standard, even if unlikely, that `(uintptr_t) (void *) 0 == (uintptr_t) (void *) 0` may be false. – Eric Postpischil Nov 08 '20 at 15:14
  • @P__JsupportswomeninPoland: Further, the C standard allows other things that are the results of identical source code and initial data to produce different results. Floating-point expressions may be evaluated with nominal precision in one instance and excess precision in another. Padding bytes in structures may vary. – Eric Postpischil Nov 08 '20 at 15:16
  • @EricPostpischil float expressions are not very good examples here. I am moving it to the separate question. – 0___________ Nov 08 '20 at 17:08