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.
Related
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.
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.
I ran into a new warning message after a compiler upgrade.
warning: ordered comparison of pointer with integer zero [-Wextra]
if (inx > 0)
As it turns out inx is a pointer. Normally I would expect to see this old code compared against 0, or NULL. This got me to thinking about signed and unsigned values, and possible risk.
A bit of research suggests:
A pointer greater than zero in c++, what does mean?
Can a pointer (address) ever be negative?
memory address positive or negative value in c?
malloc returns negative value
These seem to suggest that an address (returned by malloc) can never be zero
Which took me to my old copy of the standard.
4.10 Pointer conversions
1 A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero
or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result
is the null pointer value of that type and is distinguishable from every other value of pointer to object or
pointer to function type. Such a conversion is called a null pointer conversion. Two null pointer values of the
same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is
a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (4.4).
A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t.
It specifically states that two null pointers compare equal.
With that in mind, is that little piece of code undefined behavior? or is there another piece to the puzzle I am missing?
It's not undefined behaviour, but the result is unspecified if inx is not null.
C++11 5.9/2: 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.
So you can be sure that the the conditional code won't execute if inx is null - but not that it will if it's not null. The comparison should probably be inx != 0, which is well defined to be true if and only if inx is non-null.
You're looking at pointer conversions, but you should be looking at pointer comparisons.
Specifically, comparisons between pointers that don't refer to (subobjects of) the same array or object.
Section 5.9, paragraphs 3 and 4, this wording is found in C++14 drafts.
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.
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.
In your case, no "pointer compares greater than" relationship is defined, and therefore the operators act according to their "otherwise" behavior, giving unspecified results. This comparison won't directly crash the program, but it could take either branch through the if, assuming that inx is non-null.
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.
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.