2

I'm attempting to write a function to compare two arrays of pointers to see if they are the same. I'd like to use the function on any array of pointers, i.e. regardless of what the pointers point to - I'm just interested in equality of the pointers themselves

I've written this:

/**
 * Return true if arrays (of type ptr to ptr) are equal
 */
bool ptr_array_eq(const void **x, const void **y, size_t n)
{
    size_t i;
    for (i=0; i<n; i++) {
        if (x[i] != y[i]) {
            return false;
        }
    }
    return true;
}

my unit-test looks like this:

void testPTR_ARRAY_EQ(void)
{
    Mode *m1, *m2, *m3, *m4, *m5, *m6;

    Mode *a[] = {m1, m2, m3, m4, m5};
    Mode *b[] = {m1, m2, m3, m4, m5};
    Mode *c[] = {m2, m3, m4, m5, m6};
    Mode *d[] = {m1, m3, m4, m5, m6};

    CU_ASSERT(ptr_array_eq(a, a, 4));
    CU_ASSERT(ptr_array_eq(a, b, 4));
    CU_ASSERT(! ptr_array_eq(a, c, 4));
    CU_ASSERT(! ptr_array_eq(a, d, 4));
}

but when i compile i get the following warnings (not errors):

test_utility.c: In function ‘testPTR_ARRAY_EQ’:
test_utility.c:648:5: warning: passing argument 1 of ‘ptr_array_eq’ from incompatible pointer type [enabled by default]
../src/glamdring2.h:327:6: note: expected ‘const void **’ but argument is of type ‘struct Mode **’

with the compiler complaining that in my unit-test the types I'm using don't match the function prototype

But I'm not really interested in the underlying type, only the pointer to it. Should I:

  • a) just ignore the warnings
  • b) rewrite the function in a way to make the compiler not issue warnings
  • c) do some cast of the incoming pointers
  • d) accept that C doesn't like the idea of not knowing the underlying type and re-write the function for each type of pointer I might encounter
bph
  • 10,728
  • 15
  • 60
  • 135

1 Answers1

3

Your function requires (a pointer to the first element of) an array of const void*.

You're trying to pass it (a pointer to the first element of) an array of Mode*.

This is not permitted in C, because (among other reasons) it is not even guaranteed by the C standard that void* and Mode* are the same size. So the language doesn't let programs accidentally treat an array of one as though it were an array of the other.

Subject to the implementation-specific details being friendly, which they are in pretty much any implementation, you could use memcmp to compare the arrays.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 1
    +1: Here's a [link](http://stackoverflow.com/a/1241314/1336150) to another relevant answer that quotes the standard. – Eitan T Jul 10 '12 at 08:35
  • ok so my thinking that all pointers should be the same size is incorrect, i can see in that case that a generic function comparing pointers is not possible? – bph Jul 10 '12 at 08:36
  • @Hiett you can write it as a macro :) – Eitan T Jul 10 '12 at 08:36
  • @EitanT sounds interesting, could you possibly show an example? – bph Jul 10 '12 at 08:37
  • as another alternative, maybe I can just use memcmp, e.g. if (!memcmp (a, b, sizeof(a))) /* arrays are equal */? – bph Jul 10 '12 at 08:40
  • 1
    @Hiett: yes, I already edited to suggest that, you might have to hit "refresh" to see the most up-to-date version of my answer. `memcmp` will work provided two things: (1) either all pointers in the implementation use the same representation, or the caller always passes two arrays of the same type of pointer; and (2) there are no padding bits in pointer types in the implementation, thus ensuring that the object representations of two pointers are equal if and only if the pointers are equal. – Steve Jessop Jul 10 '12 at 08:42
  • 1
    @Hiett Here's an [online working example](http://ideone.com/2w67n). Make adjustments as you see fit. – Eitan T Jul 10 '12 at 08:47
  • @Hiett Use `sizeof(a)*n`, because `sizeof(a)` returns the size of a pointer. – Eitan T Jul 10 '12 at 08:59
  • @EitanT: in `testPTR_ARRAY_EQ`, `sizeof(a)` evaluates to the size of the array, because `a` is an array. Other callers might need to use `sizeof(some_pointer_to_pointer)*number_of_pointers`. – Steve Jessop Jul 10 '12 at 09:09
  • @SteveJessop Thanks for pointing that out. Do you think that to maintain generality it's best not to rely on the nature of the declaration and use `sizeof(*a) * n` (I missed the asterisk in my previous comment)? – Eitan T Jul 10 '12 at 10:27
  • 1
    Not sure - it's up to the caller to specify the right size, and I'm not sure it's possible to generalize for all callers what the easiest-to-read and most-robust way is to specify the size of data they have. Some users will need to multiply together two numbers, others will have the complete size easily to hand (as in `testPTR_ARRAY_EQ`). If you think that callers "should" always multipy two numbers, then you could imitate `calloc` and take two parameters, multiply them yourself, and check for overflow while you're about it. – Steve Jessop Jul 10 '12 at 10:30