christer-sandberg wrote:
Thanks for your answer.
Eric Sosman wrote:
The argument to foo() is a `void*'. The cast specifies
an `int*' argument. `void*' and `int*' are incompatible,
If I don't remember wrong, according to ANSI-C from 99 a void pointer
can be assigned the value of a pointer to an object and they should
compare equal. In what sense do you mean they should be incompatible?
The Standardese answer is "In the sense of 6.2.7, where
type compatibility is defined."
A (possibly) more helpful answer is by example. You can
convert any `int*' to a `void*' and back again, but it need
not be true that you can do the reverse: there may be `void*'
values that cannot be converted to `int*' successfully. On
many or even most present-day systems, such conversions can
produce values that are not usable as `int*'. So it's clear
that `int*' and `void*' are not the same type, even though
there are some pointer values that both can represent.
Or here's another: On every system I have ever encountered
(though I don't believe the Standard actually requires it),
every `char' value can be converted to `double' and back, and
the result compares equal to the original. Does this mean that
`char' and `double' are compatible types? Of course not.
[...]
I meant niether of them. Rather something like what I wrote (sorry for
my bad English)
a macro ensuring the correct type of parameters to the callback
(otherwise the the parameters needs to be casted in the function
definition in which case there is nothing ensuring that the type of the
array is used) e.g.:
#define QSORT(d, n, t, f) (void(*)(t*, size_t, size_t, int(*)(const t*,
const t*))) \
qsort(d, n, sizeof(t), f);
I can be done some ather ways too, but as I see it, it is an advantage
to be able to force the type of array, the size of the array elements
and the types of the paramaters to the call.back be the same.
Ingenious -- but even worse than what I thought you were
attempting! I'd supposed you were trying to disguise `f' so
the compiler wouldn't complain when you passed it to qsort().
Instead, you're lying about the nature of qsort() itself:
- You're telling the compiler to pass the first argument
as a `t*', but qsort() expects to receive a `void*'.
If any conversion from `t*' to `void*' is needed, your
recipe will prevent it from happening; if `t*' and `void*'
are passed to functions differently (e.g. in different
registers), your recipe will put the first argument in
the wrong place.
- You're telling the compiler that the fourth argument is
a pointer to a function taking two `const t*' arguments,
and your recipe will cause the compiler to complain if
the actual `f' is different. But the real qsort() doesn't
want such an `f'; it wants an `f' that takes a pair of
`const void*'. If there's any difference between these
(e.g., if a function pointer isn't a mere address but
also carries something like a "dope vector"), you cannot
expect this to work.
- And despite all these shenanigans, qsort() is still going
to pass `void*' arguments -- not `t*' arguments -- when it
calls `f'. Since that's not what `f' expects to receive,
there's no telling what might happen.
I agree that gcc's insertion of abort() is a bit draconian,
but it's understandable: gcc is trying to catch "really bad
incompatibilities," but it can only discern "incompatibility;"
it seems to have no notion of "probably venial incompatibility."
And you have to admit that it's had at least one good effect:
it's brought you here to learn the error of your (ingenious
albeit misguided) ways!
Enforcement of matching types is a weak point of C: there
are a number of places where you must rely on vigilance and can
expect little if any help from the language or the compiler.
"Type-blind" functions like qsort() and bsearch() -- memcpy(),
for that matter -- use `void*' precisely because they want to
be able to operate on any kind of data object, but blindness
has its drawbacks. Type-checking is defeated (that's the
purpose, after all), but that also means you lose the benefits
of type-checking. Your effort to restore those benefits is
laudable, but I think it's doomed. You might go as far as
#define QSORT(a, n, f) \
qsort((a), (n), sizeof *(a), (f))
.... to ensure that the third argument is correct, but I can
think of no way to ensure that `f' is the right function to use
with the type of data that happens to be in `a'. (There's even
less hope that you could ensure that `f' imposes a total ordering
on the values of that type!) Some constraints of a function's
documentation are simply not expressible in the language; C
shares this problem with other languages, too.
"If you lie to the compiler [even with the best of
intentions], it will get its revenge."
--
Er*********@sun.com