2

Am I right in thinking it is fine to treat a pointer as an int for the purposes of sorting an array of pointers, e.g.

qsort(ptrs, n, sizeof(void*), int_cmp);

I want to sort the ptrs to ascertain whether there are any duplicates, irrespective of the type of thing the pointer is pointing to, so the qsort is a precursor to doing that.

my int_cmp() is pretty standard, e.g.

int int_cmp(const void *a, const void *b)
{
    const int *ia = (const int *)a; // casting pointer types
    const int *ib = (const int *)b;

    /* integer comparison: returns negative if b > a
    and positive if a > b */
    return *ia  - *ib;
}

it seems to work in my unit-tests but is there some reason why considering a ptr as an int may cause problems for such a scenario that i may have overlooked?

14
  • 2
    No, you can't treat a pointer as an int. The size of a pointer may not be the same as the size of an int. Also remember that there are many types of data that can't be directly compared, or used in arithmetic expressions (like for example structures). Commented Apr 4, 2014 at 12:55
  • 2
    The only integer variables that are guaranteed to be able to hold a pointer are intptr_t and uintptr_t. Commented Apr 4, 2014 at 12:58
  • 1
    shold be const int *ia = *(const int **)a; const int *ib = *(const int **)b; Commented Apr 4, 2014 at 13:05
  • 1
    So, What do you do when an array of int, rather than an array of int *? Commented Apr 4, 2014 at 13:25
  • 1
    my example cmp function is for an array of ints : array of pointers, e.g. qsort(ptrs, n, sizeof(void*), int_cmp); used int_cmp for array of pointers. Commented Apr 4, 2014 at 15:52

2 Answers 2

3

No, you're not right at all, unless you want to sort the pointers by address. The actual address rarely has any meaning though, so that's very unlikely.

For detecting duplicate pointers, you should just compare the pointers as such, that's well-defined.

I would probably go with a solution using uintptr_t:

static int order_pointers(const void *pa, const void *pb)
{
  const uintptr_t a = *(void **) pa, b = *(void **) pb;

  return a < b ? -1 : a > b;
}

Haven't tested this, but something like that should work.

The conversion to uintptr_t is necessary since you cannot validly compare random pointers. I quoth the C99 draft standard, §6.5.8.5:

When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two pointers to object or incomplete types both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object, pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. All pointers to members of the same union object compare equal. If the expression P points to an element of an array object and the expression Q points to the last element of the same array object, the pointer expression Q+1 compares greater than P. In all other cases, the behavior is undefined.

I bolded the final sentence since that's what applies here.

Sign up to request clarification or add additional context in comments.

6 Comments

yes thats exactly what i want to do - sort by address in order to then assess whether i have duplicate ptrs in the array, e.g. 2 addresses that are the same
Why the conversion to an integer? See TripeHound's answer.
@alk Because undefined behavior. :)
i'm glad i asked the original question - it isn't that simple after all.. i should maybe have posted the larger question, 'how to remove duplicate ptrs, i.e. ptrs that point to the same underlying objects, from an array of ptrs in a generic way independent of the underlying objects..
@unwind as an aside, is your default approach to make functions static? i would have thought you would want to use a cmp function like that potentially across many files?
|
2

Providing you use comparison, not subtraction, you can stick with void pointers: the following worked for a simple test:

int int_cmp( const void *pa, const void *pb )
{
    const void* a = *(void**)pa ;
    const void* b = *(void**)pb ;

    if( a < b ) return -1 ;
    if( a > b ) return  1 ;
    return 0 ;
}

9 Comments

-1, this is undefined behavior. Not to toot my own horn too much, but ... see my answer.
@alk Yes, unless you know the pointers are pointing at (or just beyond) the same "object", see the standard quote in my answer.
@unwind: I don't study (or read) the standards, so am happy to bow to deeper knowledge, but if comparing two pointers that might point at anything is undefined, how can be converting them to integers be more well defined?
@TripeHound C doesn't guarantee that a pointer is encoded as just a plain integer. There might be segmented memory at play for instance. For such architectures, the compiler has to insert cleverness when doing the conversion to integer to "flatten" the space so that distinct pointers become distinct integers. And there are no limits on which integers can be compared, as far as I know.
And in [this_answer]{stackoverflow.com/questions/1845482/what-is-uintptr-t-data-type} they quote: In C99, it is defined as "an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer". so does that mean comparing intptr_t is also undefined (all you can do is convert them back to void pointers?)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.