I want to compare two void pointers like this:
void foo(void* p1, void* p2) {
  if (p1 < p2) {
    void *tmp = p1;
    p1 = p2;
    p2 = tmp;
  }
  // d         
        
Comparing two pointers is only guaranteed to be sane if they point to parts of the same "object" (struct, union or array) (or one past the end for arrays).
In practice, this is because of the existence of segmented memory model computers, where comparing only the segment offset is much faster than comparing both the segment offset and the segment id. If said segments overlap, two pointers with identical segment offsets could compare equal even though they point to different areas of memory.
Such systems are less common now than they were 20 years ago.
By @drawmcgowen, this is in C11 6.5.8.
While the results of comparing pointers to unrelated objects (not in the same struct, union or array) is undefined, I am unaware of a platform where the undefined behavior is more than "does not compare in the order you think it should". 
If you really need this, and are willing to restrict which platforms your code is portable to, you can possibly get reasonable guarantees. However be advised that since this is undefined behavior, any future version of your compiler could possibly make this code not function correctly.
What is worse, is that some compilers exploit undefined behavior for optimization opportunities. For example, suppose you have a buffer, and one pointer at the start of the buffer.
If you compare another pointer to it, either (A) it is inside the buffer, so is >= than it, or (B) it isn't inside the buffer, so the result is undefined.
A simple optimization if you can prove a vector is at the front or one-past-the-end of a buffer is to drop the comparison (if >= for front, or <= for back) and replace it with a constant.
Your compiler could figure this out at any point in the optimization of your code, with any point release.
It could even say "this is a pointer to a heap allocated object", and prove that every pointer is either equal to the pointer, or unrelated -- thus < and > are always undefined behavior, and branches that do this can be completely eliminated from your code.
Relying on undefined behavior means you now have to audit the machine code generated by your code now, and in every compile in the future.
Originally this question was tagged with c++.  In C++, std::less is guaranteed to well order all pointers.  (this was added to allow pointers to be sorted in various std containers and algorithms)  This may be of use if you are working in a mixed C/C++ system.
For templates less,[...], the specializations for any pointer type yield a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by the built-in operators < [...] For template specializations less [...] if the call operator calls a built-in operator comparing pointers, the call operator yields a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by those built-in operators.