Let's say I defined a structure and created a few instances of it. I have an array of pointers to these instances but some pointers point to the same instances of my structure. I want to remove duplicates in the array so I sort it by qsort()
function. Here's my comparison function:
int cmp(const void *a, const void *b)
{
struct foo **s1 = (struct foo**)a;
struct foo **s2 = (struct foo**)b;
return *s1 - *s2;
}
The question is, can I really sort using such a comparison? I know that subtructing pointers is undefined in this case but I care only about the integer value of pointers, because I simply want pointers pointing to the same instance of foo
to be next to each other.
Maybe I should use function like this:
int cmp(const void *a, const void *b)
{
struct foo **s1 = (struct foo**)a;
struct foo **s2 = (struct foo**)b;
if (*s1 > *s2)
return 1;
else if (*s1 == *s2)
return 0;
else
return -1;
}
Is there any difference in using them?
If you have
intptr_t
(which is an integer type), then you can cast yourvoid*
pointers to that, and then you can compare arbitrary values.intptr_t
is not a required type, but it is pretty common in practice.Subtracting one
intptr_t
from another might still overflow, so it is not a strictly portable solution. But comparison is fine. If you useduintptr_t
to avoid overflow, the difference will never be negative. That's a general problem with usinga - b
to implement qsort-style compare functions.Subtracting or comparing pointers which don't point into the same object is undefined behaviour. So neither of the proposed solutions in the question are valid:
§6.5.6 para 9 (Additive operators):
§6.5.8 para 5 provides a list of possible valid comparisons, which is a bit more generous than the restriction on subtraction, since you can compare pointers to two members of the same
struct
orunion
, provided the members themselves are of the same type. But unrelated objects do not fall into any of this list. It terminates with the sentence: "In all other cases, the behavior is undefined."If you want a truly portable solution which doesn't depend on
intptr_t
, then you could memcmp the pointers. But, really, I think that is only of theoretical interest.