13

Considering the following code, is it safe to do pointer arithmetic on nullptr?

I assume adding any offsets to a nullptr results in another nullptr, so far MSVC produce results as I expected, however I am a bit unsure about whether using nullptr like this is safe:

float * x = nullptr;

float * y = x + 31; // I assume y is a nullptr after this assigment

if (y != nullptr)
{
  /* do something */
}
Niall
  • 30,036
  • 10
  • 99
  • 142
user2188453
  • 1,105
  • 1
  • 12
  • 26
  • This is not what happens with clang and gcc, `float* a = nullptr; assert(a==nullptr); a++; assert(a!=nullptr); `. Adding something to `nullptr` doesn't result in `nullptr` with these compiles. I don't know if it is UB. – alfC Dec 06 '18 at 05:08
  • One short remark since no answer contains that: In practice, pointers are memory addresses. If you add an offset 31 to the null pointer naively, you obtain a pointer that points to the memory address `31`. That's not the null pointer. The compiler is allowed to do exactly this. If null pointer + offset was defined to be the null pointer, the compiler would have to add some conditional jump there. Since the committee cares about efficiency, the standard does not mandate what happens when you add a non-zero offset to the null pointer. – Handy999 Mar 01 '19 at 08:36

5 Answers5

12

You didn't define what "safe" means to you, but regardless, the code you propose has undefined behaviour. Pointer arithmetic is only allowed on pointer values that point into an array object, or perhaps to one-past-the-end of an array. (Non-array objects are considered an array of one element for the purpose of this rule.)

Since the null pointer is never the address of an object or one past an object, your code can never have well-defined behaviour.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    `int* a; a++;` is UB even if I dont dereference the pointer? Can you give a reference for that? – 463035818_is_not_an_ai Jul 22 '16 at 13:04
  • 5
    @tobi303: There's no reference for it, because the standard doesn't have to explicitly state that it's undefined. [expr.add]/5 defines the behavior of pointer+integer *only* for pointers to arrays (or pointer-to-object, which acts as an array of 1). If the pointer does not point to an array, then by definition the behavior is undefined (except in the case of adding 0, which [expr.add]/8 defines for all pointers). – Nicol Bolas Jul 22 '16 at 13:30
  • 3
    @tobi303 UB doesn't mean "crash", it means "we did not define what this does". As it happens, compilers can aggressively optimize and detect UB and cause your program to behave extremely unexpectedly. One school of optimization is "as UB can not occur in a well formed program, any chain of logic that leads to UB can be safely eliminated as a possibility, up to and including skipping explicit `if` checks in your code". In short, UB can lead to *time travel* bugs, where code far away from the UB behaves in ways that do not match the code you wrote. – Yakk - Adam Nevraumont Jul 22 '16 at 13:48
  • @NicolBolas thanks. something like [expr.add]/5 is what I meant when asking for a reference – 463035818_is_not_an_ai Jul 22 '16 at 14:37
  • 1
    @Yakk I know (more or less) what UB means, I just wasnt aware that `int* a;a++` is UB until 1 hour ago – 463035818_is_not_an_ai Jul 22 '16 at 14:38
10

Is it safe to do pointer arithmetic on nullptr?

C++ defines two kind of operations on nullptr. For:

float *x = nullptr;
float *y = nullptr;
  1. x +/- 0 = x

  2. x - y = 0 (x and y have the same type)

You cannot make an assumption about what is not defined, so you shouldn't do it.

user16217248
  • 3,119
  • 19
  • 19
  • 37
sjsam
  • 21,411
  • 5
  • 55
  • 102
  • 3
    Actually, there's a third operation defined as well : `x==y`. Seems trivial, but it really means that there is just one null pointer value. – MSalters Jul 22 '16 at 14:32
  • 1
    An interesting post about this: http://www.drdobbs.com/cpp/why-does-c-allow-arithmetic-on-null-poin/240001022 – Zark Bardoo Nov 02 '18 at 16:26
4

Addign 0 to nullptr has well defined behavior and gives you nullptr back.

Adding anything else to nullptr has undefined behavior.

[expr.add]/4 has

When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.

  • If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.
  • Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]), the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element i+j of x if 0≤i+j≤n and the expression P - J points to the (possibly-hypothetical) array element i−j of x if 0≤i−j≤n.
  • Otherwise, the behavior is undefined.

The first bullet point covers adding zero, the second deals with having an array, and the last bullet covers everything else.

Since we never have an array we can ignore the second bullet and just deal with the first and third. That gives us adding zero is defined behavior and adding any other value has undefined behavior.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
2

... is it safe to do pointer arithmetic on nullptr?

No, arithmetic on the nullptr is not well defined since it is itself not a pointer type (but conversions to NULL values of all pointer types exist).

See here;

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.


In general, arbitrary pointer arithmetic (even on the NULL value) is almost certainly going to cause problems - you didn't allocate that memory - it is not yours to attempt to read or write to.

For comparison purposes (e.g. one past the end), you will be fine, but otherwise the code you have will result in undefined behaviour.

For further reading, see Wikipedia on undefined behavior.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Niall
  • 30,036
  • 10
  • 99
  • 142
  • I'm not sure about the logic in your second paragraph: pointer arithmetic doesn't cause reading from or writing to memory. Memory has nothing to do with the OP's problem. – Kerrek SB Jul 22 '16 at 15:16
  • That was in more about the arbitrary part - side issue to the OP. I'll clean that up. – Niall Jul 22 '16 at 15:46
1

No, adding an offset to a nullptr does not result in a nullptr. This is undefined behavior.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • 6
    You are correct that adding a non-zero offset does not result in a nullptr. However, adding/subtracting zero to/from a null pointer ARE well defined, and result in a null pointer (this is one specific difference between C and C++ - in C this is undefined). – Peter Jul 22 '16 at 12:40