7

Since pointer arithmetic is defined within the same array I'm in doubt if we can subtract NULL from another NULL. I'm concerned about the implementation of:

//first and second can both either be from the same array 
//or be both NULL
prtdiff_t sub(void *first, void *second){
    //Do I really need this condition?
    if(!first && !second)
        return (ptrdiff_t) 0;

    return second - first;
}

Note: This question about C. If you are looking for C++ question, it is here (the answer is different!). There is also common question for both C and C++.

anton_rh
  • 8,226
  • 7
  • 45
  • 73
St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • @HarshitAgrawal any reference would be highly appreciate – St.Antario Apr 18 '19 at 14:01
  • 4
    @St.Antario Actually you have explained it yourself and likely have your reference ... – Eugene Sh. Apr 18 '19 at 14:01
  • Looks weird, because the case where only one is NULL is not handle. – Ôrel Apr 18 '19 at 14:02
  • @Ôrel It is an artificial example to show the actual problem I'm concerned about. – St.Antario Apr 18 '19 at 14:03
  • 2
    Well, checking here for NULL has a little help as any *other* invalid pointer will break it equally. – Eugene Sh. Apr 18 '19 at 14:03
  • 2
    @St.Antario while I was looking for references two answers posted. :p – HarshitMadhav Apr 18 '19 at 14:04
  • @St.Antario I think you misunderstood the concept of null and actually thought that null has a specific value. – HarshitMadhav Apr 18 '19 at 14:05
  • @HarshitAgrawal NULL is clearly defined as: **An integer constant expression with the value 0, or such an expression cast to type `void *`, is called a null pointer constant.** So I used to think that we could subtract one 0 from another 0... – St.Antario Apr 18 '19 at 14:06
  • 1
    @St.Antario So if `NULL` is defined as an integer constant expression you can subtract `NULL` from `NULL`, but not that is not portable because it might not be (and usually isn't) defined as an integer constant expression. – Ian Abbott Apr 18 '19 at 14:12
  • 2
    Even if `NULL` is defined as an integer 0, then after assigning `char *a = NULL; char *b = NULL;`, then the subtraction `a - b` is still illegal. – Ian Abbott Apr 18 '19 at 14:19
  • 1
    Also, you cannot subtract `void *` pointers to elements of the same array, because you cannot have an array of `void`. (Yes, I know GCC allows pointer arithmetic on pointers to `void` as an extension to the C language.) – Ian Abbott Apr 18 '19 at 14:22
  • @St.Antario I have posted an answer below :p I was very slow in answering because I was trying to post the answer in simple language for future readers – HarshitMadhav Apr 18 '19 at 14:32
  • _It is an artificial example to show the actual problem I'm concerned about._ In that case, please post a more realistic example. The code shown is broken, and not just when `first` and `second` happen to be `NULL`. (And yes, I know I'm a bit late with that observation). – Paul Sanders May 14 '22 at 19:24

4 Answers4

11

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.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • 2
    *On many implementations* the `intptr_t` difference would be a byte difference, but the result of converting a pointer to an integer is implementation-defined, so that result is not guaranteed. – John Bollinger Apr 18 '19 at 14:14
  • Technically NULL is a valid adress and is the first byte of addressable memory. Hence it can point to an array object and so you can subtract NULL from NULL. It s only the memory management hardware that has been told that for applications NULL is not a valid adress and upon dereferencing it should throw an exception. Example: interrupt vectors start at zero. – Paul Ogilvie Apr 18 '19 at 14:18
  • @PaulOgilvie I believe `NULL` is defined not to be a valid address by the standard (reference needed..). The case on a specific architecture where the address `0` is actually valid (and having `NULL` representation the same as pointer to this address) is making the corresponding architecture/toolchain combo non-compliant. – Eugene Sh. Apr 18 '19 at 14:21
  • @EugeneSh., ... interesting. It means you cannot have code written in C that modifies the interrupt vector at address zero. – Paul Ogilvie Apr 18 '19 at 14:23
  • 6
    @PaulOgilvie Here is the citation: http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3 *..the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.*. But yeah, that would be one of these corner cases where you just say "well, my compiler is sane enough to understand what I want to do".. – Eugene Sh. Apr 18 '19 at 14:25
  • 3
    @PaulOgilvie: Semantically, the *null pointer value* is only guaranteed to compare unequal to the address of any object or function. It is not guaranteed to be address 0 (some older systems used `0xDEADBEEF` as the null pointer value). It is also considered an *invalid* pointer value (the behavior on attempting to dereference it is undefined). – John Bode Apr 18 '19 at 14:25
  • @JohnBode, thanks: _It is not guaranteed to be address 0_ is the key. – Paul Ogilvie Apr 18 '19 at 14:29
  • @PaulOgilvie http://c-faq.com/null/null1.html http://c-faq.com/null/varieties.html You can't have portable code written in C that modifies addresses at arbitrary locations. I remember reading that casting integer types to pointers is implementation-defined. – fdk1342 Apr 18 '19 at 14:39
7

No you can't do this: the difference between two pointers is only defined for pointers that point to elements of the same array, or one past the end. (For this purpose an object counts as a single element array).

(intptr_t)second - (intptr_t)first is valid though.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • The integer difference is definitely valid, but its *meaning* is implementation-defined (provided there is no overflow), on account of the result of converting pointers to integers being implementation defined. – John Bollinger Apr 18 '19 at 14:10
  • @JohnBollinger Implementation-defined in the sense of `intptr_t` is not required to be supported? Or in some another way? – St.Antario Apr 18 '19 at 14:14
  • 2
    As I said, @St.Antario, implementation-defined because the effect of converting a pointer value to an integer type is implementation-defined, regardless of the particular integer type involved. That `intptr_t` is optional is a separate issue. – John Bollinger Apr 18 '19 at 14:18
0

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.

But there is no such provision for C.

Ôrel
  • 7,044
  • 3
  • 27
  • 46
  • So C++ handles your case. – Ôrel Apr 18 '19 at 14:05
  • 10
    This question is tagged C, not C++. – dbush Apr 18 '19 at 14:07
  • 2
    If you add something like "..but there is no such a provision for C" and a conclusion, your answer might become considered as good one. – Eugene Sh. Apr 18 '19 at 14:16
  • 1
    @dbus I was looking for the same question but about C++. Unfortunately Google by default provides link only to this (pure C) question. I skipped this answer because it is downvoted and thought that subtracting null pointers is not valid in C++ either. But it is wrong. This is misleading. This answer is what I really needed. But it is downvoted. It is frustrating. Anyway there is similar question about [C++](https://stackoverflow.com/q/27681633/5447906). – anton_rh May 14 '22 at 20:02
-1

The simple answer is NO, YOU CAN'T SUBTRACT A NULL FROM ANOTHER NULL.

I think you misunderstood this definition:

NULL is clearly defined as: An integer constant expression with the value 0, or such an expression cast to type void, is called a null pointer constant. So I used to think that we could subtract one 0 from another 0.

Now, As for now let's take GOOGLE's Definition of NULL

Null means having no value; in other words null is zero, like if you put so little sugar in your coffee that it's practically null. Null also means invalid. From the Latin nullus, meaning "not any," poor, powerless null is not actually there at all.

Clearly, it states that null has no value. Think of it as you are trying to subtract nothing from nothing.

Now let's take it in other words, null is zero (if and only if defined value) you can subtract it for sure (but not you can do something like char *ab = NULL, char *aa= NULL and then performing subtraction like ab-aa is still illegal)

But no one can actually predict the value for null so, when you are unable to get the value you are not able to perform any operation(like subtraction, addition, etc.) on that.

HarshitMadhav
  • 4,769
  • 6
  • 36
  • 45