Each operand shall be a prvalue.

If both operands have arithmetic or unscoped enumeration type,
the usual arithmetic conversions ([expr.arith.conv]) are performed.

Otherwise, if one operand has arithmetic or unscoped enumeration type,
integral promotion is applied ([conv.prom]) to that operand.

A converted or promoted operand is used in place of
the corresponding original operand for the remainder of this section.

For addition, either both operands shall have arithmetic
type, or one operand shall be a pointer to a completely-defined object
type and the other shall have integral type.

For subtraction, one of the following shall hold:

- both operands have arithmetic type; or
- both operands are pointers to cv-qualified or cv-unqualified versions of the same completely-defined object type; or
- the left operand is a pointer to a completely-defined object type and the right operand has integral type.

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.

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

When two pointer expressions P and Q are subtracted,
the type of the result is an implementation-defined signed
integral type; this type shall be the same type that is defined as
std::ptrdiff_t in the <cstddef>
header ([support.types.layout]).

- Otherwise, if P and Q point to, respectively, array elements i and j of the same array object x, the expression P - Q has the value .[
*Note 2*:If the value is not in the range of representable values of type std::ptrdiff_t, the behavior is undefined ([expr.pre]).—*end note*] - Otherwise, the behavior is undefined.

For addition or subtraction, if the expressions P or Q have
type “pointer to cv T”, where T and the array element type
are not similar, the behavior is undefined.

[*Example 1*: int arr[5] = {1, 2, 3, 4, 5};
unsigned int *p = reinterpret_cast<unsigned int*>(arr + 1);
unsigned int k = *p; // OK, value of k is 2 ([conv.lval])
unsigned int *q = p + 1; // undefined behavior: p points to an int, not an unsigned int object
— *end example*]

66)66)

As specified in [basic.compound],
an object that is not an array element
is considered to belong to a single-element array for this purpose and
a pointer past the last element of an array of n elements
is considered to be equivalent to a pointer to a hypothetical array element
n for this purpose.