Is pointer comparison undefined or unspecified behavior in C++? - c++

The C++ Programming Language 3rd edition by Stroustrup says that,
Subtraction of pointers is defined only when both pointers point to
elements of the same array (although the language has no fast way of
ensuring that is the case). When subtracting one pointer from another,
the result is the number of array elements between the two pointers
(an integer). One can add an integer to a pointer or subtract an
integer from a pointer; in both cases, the result is a pointer value.
If that value does not point to an element of the same array as the
original pointer or one beyond, the result of using that value is
undefined.
For example:
void f ()
{
int v1 [10];
int v2 [10];
int i1 = &v1[5] - &v1[3]; // i1 = 2
int i2 = &v1[5] - &v2[3]; // result undefined
}
I was reading about unspecified behavior on Wikipedia. It says that
In C and C++, the comparison of pointers to objects is only strictly defined if the pointers point to members of the same object, or elements of the same array.
Example:
int main(void)
{
int a = 0;
int b = 0;
return &a < &b; /* unspecified behavior in C++, undefined in C */
}
So, I am confused. Which one is correct? Wikipedia or Stroustrup's book? What C++ standard says about this?
Correct me If I am misunderstanding something.

Note that pointer subtraction and pointer comparison are different operations with different rules.
C++14 5.6/6, on subtracting pointers:
Unless both pointers point to elements of the same array object or one past the last element of the array object, the behavior is undefined.
C++14 5.9/3-4:
Comparing pointers to objects is defined as follows:
If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript compares greater.
If one pointer points to an element of an array, or to a subobject thereof, and another pointer points one past the last element of the array, the latter pointer compares greater.
If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control and provided their class is not a union.
If two operands p and q compare equal (5.10), p<=q and p>=q both yield true and p<q and p>q both yield false. Otherwise, if a pointer p compares greater than a pointer q, p>=q, p>q, q<=p, and q<p all yield true, and p<=q, p<q, q>=p, and q>p all yield false. Otherwise, the result of each of the operators is unspecified.

Related

Operator less than between a non-null raw pointer and nullptr

Are the operations nullptr < ptr and ptr < nullptr well defined for a non-null raw pointer ptr != nullptr? Quotes from the C++ standard are welcome.
Such a comparison is well-formed, but its result is unspecified.
[expr.rel]/3 Comparing pointers to objects is defined as follows:
— If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript compares greater.
— If one pointer points to an element of an array, or to a subobject thereof, and another pointer points one past the last element of the array, the latter pointer compares greater.
— If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control (Clause 11) and provided their class is not a union.
[expr.rel]/4 If two operands p and q compare equal (5.10), p<=q and p>=q both yield true and p<q and p>q both yield false. Otherwise, if a pointer p compares greater than a pointer q, p>=q, p>q, q<=p, and q<p all yield true and p<=q, p<q, q>=p, and q>p all yield false. Otherwise, the result of each of the operators is unspecified.
A null pointer doesn't fall into any of the three clauses of [expr.rel]/3, and so it compares neither greater nor less than a non-null pointer. This case then falls into the "otherwise" clause of [expr.rel]/4.

Using std::less with nullptr

Does the assertion in the following code snippet always hold?
std::less<Object *> lessPtr;
Object * o = new Object();
assert(lessPtr (o, nullptr) == false);
Introduction
This question really boils down to whether the use of the less-than relational operator on pointer types where one operand is a nullptr will yield the "expected" result; which sadly isn't the case.
The result is unspecified.
Note: Do mind that std::less guarantees a total order; meaning that even if the result, when using the function object, is unspecified, it must yield the same unspecified value on each invocation.
What does the International Standard (N3337) say?
5.9p2 Relational operators [expr.rel]
Pointers to objects or functions of the same type (after pointer conversions) can be compared, with a result defined as follows:
If two pointers p and q of the same type point to the same object or function, or both point one past the end of the same array, or are both null, then p<=q and p>=q both yield true and p<q and p>q both yield false.
If two pointers p and q of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results of p<q, p>q, p<=q, and p>=q are unspecified.
If two pointers point to non-static data members of the same object, or to subobjects or array elements of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control (Clause 11) and provided their class is not a union.
If two pointers point to non-static data members of the same object with different access control (Clause 11) the result is unspecified.
If two pointers point to non-static data members of the same union object, they compare equal (after conversion to void*, if necessary). If two pointers point to elements of the same array or one beyond the end of the array, the pointer to the object with the higher subscript compares higher.
Other pointer comparisons are unspecified.
20.8.5p8 Comparison [comparision]
For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield a total order, even if the built-in operators <, >, <=, >= do not.
So, what is the standard really saying?
T * p = new T;
T * q = nullptr;
What is the verdict for p < q?
Since p and q don't point to different elements of the same array (including the element one past the last element of an array), and both don't point to non-static data members of the same object; the result when doing p < q (and p > q) is unspecified.
bool a = p < q; // unspecified
bool b = p < q; // unspecified
assert (a == b); // can fire
What about std::less?
However, when using std::less we are guaranteed a total order - which effectively means that the below assertion cannot fire (standard-20.8.5p8).
std::less<T*> comp;
bool a = comp (p, q); // unspecified
bool b = comp (p, q); // unspecified
assert (a == b); // can not fire
No, the ordering of a null pointer relative to any non-null pointer is unspecified.
The result of the comparision operators is unspecified if the operands "point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null".
std::less and friends extend this to specify that there's a total order, but don't specify where null pointers occur in that order. So it's guaranteed that null will consistently be either greater than, or less than, any given non-null pointer. But it's not specified to be either less than, or greater than, all non-null pointers.

Is it safe to compare pointers of same type?

char** buffer{ /* some buffer */ };
char* ptr1{buffer[0]};
char* ptr2{buffer[10]};
assert(ptr1 < ptr2);
If two pointers point to different locations in the same buffer, is it safe to compare them?
I want to know if a range of pointers is valid by comparing: assert(rangeBeginPtr < rangeEndPtr).
You can compare pointers with the relational operators (<, >, <= and >=) provided they both point to an element of the same array, or one past that array. Anything else is unspecified behaviour as per C++11 5.9 Relational operators. So, given:
char xyzzy[10];
char plugh[10];
All these are specified to function correctly:
assert(&(xyzzy[1]) < &(xyzzy[4]));
assert(&(xyzzy[9]) < &(xyzzy[10])); // even though [10] isn't there.
but these are not:
assert(&(xyzzy[1]) < &(xyzzy[15]));
assert(&(xyzzy[9]) < &(plugh[3]));
The type doesn't come into it except that it has to be the same type if you're comparing two elements in the same array. If you have two char * variables, that's unspecified if they point to different arrays even though they have the same type.
You can determine the order of pointers only with on array object and if they are non-void. However, within one array object the comparison is well defined. The relevant clause in the standard is 5.9 [expr.rel] paragraph 2:
[...] Pointers to objects or functions of the same type (after pointer conversions) can be compared, with a result defined as follows:
If two pointers p and q of the same type point to the same object or function, or both point one past the end of the same array, or are both null, then p<=q and p>=q both yield true and p<q and p>q both yield false.
If two pointers p and q of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results of p<q, p>q, p<=q, and p>=q are unspecified.
If two pointers point to non-static data members of the same object, or to subobjects or array elements of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control (Clause 11) and provided their class is not a union.
If two pointers point to non-static data members of the same object with different access control (Clause 11) the result is unspecified.
If two pointers point to non-static data members of the same union object, they compare equal (after conversion to void*, if necessary). If two pointers point to elements of the same array or one beyond the end of the array, the pointer to the object with the higher subscript compares higher.
Other pointer comparisons are unspecified.
== and != are valid and well-defined for all pointers of the same type. <, <=, >, and >= are only meaningful for pointers that point to objects in the same array or one-past-the-end of the array. They are also meaningful for pointers to sub-objects of a class object if the sub-objects have the same type and the same access specifier. If those conditions aren't met, the result is unspecified; one immediate consequence is that a<b and b<c does not imply that a<c, so you cannot use <, etc. as the comparator for a sort function.
std::less, std::less_equal, std::greater, and std::greater_equal for pointer types all define a total ordering; they can be used for sorting.

Pointer Arithmetic confusion

I know you can add a pointer to an int, and subtract two pointers, and a pointer and an int, but can you add a int to a pointer. So 5 + pointer.
You can, but restrictions apply. Pointer arithmetics is only valid within an array (or 1 past the end of an array).
Here's some of the rules:
5.7 Additive operators [expr.add]
5) [...] If both the pointer operand and the result point to elements of the same array object, or one past
the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is
undefined.
and
6) When two pointers to elements of the same array object are subtracted, the result is the difference of the
subscripts of the two array elements. [...] Unless both pointers point to elements of the same array object, or
one past the last element of the array object, the behavior is undefined.
pasted here for confirmation.
So
int* x = new int;
int* y = new int;
is okay, but:
y-x;
x + 4;
y - 1;
or even comparisons using binary comparison operators are undefined behavior.
However x+1 and 1+x are okay (a single object counts as an array of size 1)
Adding an int to a pointer is syntactically okay but there are so many issues that you have to watch out for, e.g. overflow errors.
Ideally, you should do it only within an array.

Are comparisons on out-of-range pointers well-defined?

Given the following code:
char buffer[1024];
char * const begin = buffer;
char * const end = buffer + 1024;
char *p = begin + 2000;
if (p < begin || p > end)
std::cout << "pointer is out of range\n";
Are the comparisons performed (p < begin and p > end) well-defined? Or does this code have undefined behaviour because the pointer has been advanced past the end of the array?
If the comparisons are well defined, what is that definition?
(extra credit: is the evaluation of begin + 2000 itself undefined behaviour?)
I'll assume the C++11 standard. According to section 5.7 (Additive Operands) paragraph 5, the behavior of *p = begin + 2000 is undefined first, before you even get to the comparison:
If both the pointer operand and the result point to elements of the
same array object, or one past the last element of the array object,
the evaluation shall not produce an overflow; otherwise, the behavior
is undefined.
The evaluation of begin+2000 is undefined, it's going past the end of the array - you can go up to one past the end, but not further.
From C++11 §5.7/5 Additive operators:
When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. [...] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is
undefined.
For pointer comparisons to be specified, assuming you have valid pointers to start with, they essentially need to be pointers to the same array (or one past the end), or pointers to non-static data members of the same access control of the same object (unless it's an union...).
The details are in §5.9/2 Relational operators:
Pointers to objects or functions of the same type (after pointer conversions) can be compared, with a result defined as follows:
If two pointers p and q of the same type point to the same object or function, or both point one past
the end of the same array, or are both null, then p<=q and p>=q both yield true and p<q and p>q
both yield false.
If two pointers p and q of the same type point to different objects that are not members of the same
object or elements of the same array or to different functions, or if only one of them is null, the results
of p<q, p>q, p<=q, and p>=q are unspecified.
If two pointers point to non-static data members of the same object, or to subobjects or array elements
of such members, recursively, the pointer to the later declared member compares greater provided the
two members have the same access control (Clause 11) and provided their class is not a union.
If two pointers point to non-static data members of the same object with different access control
(Clause 11) the result is unspecified.
— If two pointers point to non-static data members of the same union object, they compare equal (after
conversion to void*, if necessary). If two pointers point to elements of the same array or one beyond
the end of the array, the pointer to the object with the higher subscript compares higher.
Other pointer comparisons are unspecified.
Your program's behavior is undefined, but not because of the comparison.
The evaluation of the expression begin + 2000 has undefined behavior because the result would point more than one element past the end of the 1024-element array.
Quoting C++11 (actually the N3485 draft), 5.7p4 [expr.add]:
When an expression that has integral type is added to or subtracted
from a pointer, the result has the type of the pointer operand. [...]
If both the pointer operand and the result point to elements of the
same array object, or one past the last element of the array object,
the evaluation shall not produce an overflow; otherwise, the behavior
is undefined.
In short, just computing an out-of-bounds pointer has undefined behavior; it doesn't matter what operations you perform on that pointer after that.