9

Note: This question is not about total order. A total order on pointers of the same type can be obtained using std::less.

According to this, comparing two pointers with operator< isn't allowed if they point for example into different allocations.

In which sense isn't it allowed? Is it implementation defined, unspecified or undefined behaviour?

I think I read somewhere that it's unspecified. An implementation isn't required to document what the behaviour is, but there must be some behaviour. So that would mean, comparing any two pointers is still legal, but doesn't neccessarily yield a total order. Does that mean, that we still have to get a consistent result, when comparing the same two pointers twice? The general case would be: Does invoking the same unspecified behaviour twice within an application always yield the same result?

int i1, i2;
int* a = &i1;
int* b = &i2;
bool b1 = a < b; // unspecified, right?
bool b2 = a < b;
assert(b1 == b2); // Is this guaranteed to be true?
haslersn
  • 515
  • 5
  • 10

3 Answers3

8

Comparing two unrelated pointers (i.e. pointers not pointing to the same memory, or not pointing to different parts of the same "array") can only be done using equality == and inequality !=. All other comparison is unspecified.

If you have two pointers pointing to the same place, or inside the same array, then you can compare them using the relative operators.

So if you have e.g.

int* p1 = new int[10];
int* p2 = new int[5];

you can only use == and != to compare the pointers p1 and p2.

But if you have

int a = new int[10];
int* p1 = &a[0];
int* p2 = &a[3];

then you can use also < and > (and of course <= and >=) to compare p1 and p2

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Do you have the specific reference that says that this is *undefined*? By reading *[expr.rel]*, I understand it is *unspecified*. – Holt Jun 06 '17 at 19:02
  • [Here](http://eel.is/c++draft/expr.rel#3) it says at the third point that _Otherwise, neither pointer compares greater than the other_. It isn't exactly an UB. Am I wrong? – skypjack Jun 06 '17 at 19:02
  • 3
    From the linked SO answer - "You can, however, use std::less and the other relational comparison function objects to compare any two pointers. The results are implementation-defined, but it is guaranteed that there is a total ordering." –  Jun 06 '17 at 19:02
  • @NeilButterworth The standards explicitly says: *"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 `std::less` is not related to the relational operators for pointers. – Holt Jun 06 '17 at 19:07
  • 1
    @Holt I didn't say it was. –  Jun 06 '17 at 19:08
  • @NeilButterworth Did not say you said it ;) I just wanted to point out that the standard explicit says it. – Holt Jun 06 '17 at 19:20
  • This doesn't answer whether the expression within the assertion is guaranteed to return true. – haslersn Jun 06 '17 at 20:49
  • @Creep4Play Nope, but if you get the results of two unspecified operations, and compare the results, won't that comparison also be unspecified? – Some programmer dude Jun 06 '17 at 21:18
  • @Someprogrammerdude For an operation with unspecified behaviour, there is no requirement for an implementation to document the behaviour but it must chose a behaviour. – haslersn Jun 06 '17 at 21:25
  • Standard quotes here are coming from several different versions of the standard. Reading N4618, which @skypjack quotes (a paragraph not in N4606), makes it sound like `p1 < p2` and `p2 < p1` should both return false in the first example. – 1201ProgramAlarm Jun 06 '17 at 21:43
  • @1201ProgramAlarm I think the combinations of (3.3) and 4 in [expr.rel] still makes the results of `p1 < p2` and `p2 < p1` unspecified - 4 says that the results is specified if `p1` and `p2` compare equal or if one compares greater than the other, which is not the case if (3.3) *"neither pointer compares greater than the other"*. – Holt Jun 07 '17 at 09:05
1
  1. It is not possible to compare two unrelated pointers(except using == and !=), that is comparing two unrelated pointer is undefined.
  2. However, it is still possible to create a vector of pointers and then sort that vector in terms of the address of those pointers! See example 2 below.

Example 1:

int a = 5,b = 6;
int *a_ptr = &a, *b_ptr = &b;
bool ans = a_ptr < b_ptr;//undefined behavior

Example 2:

#include <iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;

int main()
{
    std::string firstName = "Anoop",middleName = "Singh", lastName = "Rana";
    std::vector<string *> storage_ptr = {&firstName,&middleName,&lastName};
    std::vector<string *>::iterator begin = storage_ptr.begin();
    while(begin!=storage_ptr.end()){
       std::cout<<(**begin)<<std::endl;
       begin++;
    }
    std::sort(storage_ptr.begin(),storage_ptr.end(),greater<string *>());
    std::vector<string *>::iterator begin2 = storage_ptr.begin();
    while(begin2!=storage_ptr.end()){
       std::cout<<(**begin2)<<std::endl;
       begin2++;
    }
    return 0;
}

As you can see that the first while loop prints the vector in descending order while the second while loop prints the vector in ascending order showing that using the new standard we can use the function object greater<string *> to sort the vector of pointers.

Jason
  • 36,170
  • 5
  • 26
  • 60
-2

Comparing pointers as shown in your example makes little sense because "a" and "b" value depends on how is the data stored in memory, not on the content stored in that location.

Pointers are addresses in memory (usually stored as 32 bit integer). Unless you change any of them, their comparison will return the same result. You might not know what that result is, but you'll know that it'll be the same every time.

Therefore, in your case, you will get the same result for both b1 and b2, so the assertion will pass.

Here is an example where comparing pointers does make sense:

int data[1000];
int *end = data + 50;
for (int *c = data; c < end; c++)
     ... use *c
user3429660
  • 2,420
  • 4
  • 25
  • 41
  • *"a" and "b" value depends on how is the data stored in memory* In practice this is usually true, but the behavior is actually *undefined*. In the case of the question's example, the compiler could choose to simply optimize away all of that code. In that case, the memory doesn't come into play at all. – François Andrieux Jun 06 '17 at 19:12
  • To me, it is undefined in the sense that you cannot predict the outcome. My point was that, although the outcome is not predictable, it is consistent. – user3429660 Jun 06 '17 at 19:16
  • 2
    The term *undefined behavior* in c++ has a [strict meaning](http://en.cppreference.com/w/cpp/language/ub). That meaning implies that you cannot rationally predict the behavior of anything that exhibits *undefined behavior*. A compiler may detect that all of the variables in the example are uninitialized. It may then assume that the result of `*undefined* == *undefined*` is whatever is most convenient for it's optimizations. You can't assume `b1 == b2` is `true`. Edit : The question was edited since this comment was written. – François Andrieux Jun 06 '17 at 19:23
  • I just edited the question in order to fix the uninitialized variables. This question wasn't supposed to be about uninitialized variables, but about consistency when invoking the same unspecified behaviour twice. – haslersn Jun 06 '17 at 19:27
  • C++ standards states that comparing pointers leads to "unspecified behavior", not to "undefined behavior". – user3429660 Jun 06 '17 at 19:29
  • Long story short: pointers are addresses. Unless you change at least one of the address (a=something_else) their comparison will return the same result. You usually do not know what the result is, but you know it'll be consistent. – user3429660 Jun 06 '17 at 19:33
  • @user3429660 Could you please cite a reference that says that the same unspecified operation always yields the same result? – haslersn Jun 06 '17 at 20:48