From: scjones@thor.sdrc.com (Larry Jones)
Newsgroups: comp.lang.c
Subject: Re: Help.  (Unix C)
Summary: qsort comparison functions
Date: 5 Nov 92 16:20:27 GMT

In article <Bx8Cw8.5tB@portal.hq.videocart.com>, dfuller@portal.hq.videocart.com (Dave Fuller) writes:
> scjones@thor.sdrc.com (Larry Jones) writes:
> : An ANSI compiler will, quite properly, object to this code -- it is
> : neither correct nor portable.  The comparison function *must* be
> : defined with two "void *" arguments (or "char *" if your compiler is
> : too old to support "void *"); that's what qsort calls it with.  Some
> : implementations use the same representation for all pointer types and
> : on those implementations you can misdefine the function and it will
> : work, but this is not guaranteed and there are systems where it will
> : fail.  
> : 
>    First of all, i don't use an ANSI compiler except for about 20%
> of my code (the declaration of the function should have helped on that).

That's too bad -- using an ANSI compiler and prototypes can point out a
lot of subtle errors (like this one) that aren't easily found otherwise.

> But, even on an ANSI compiler this will work. void is used to take on 
> no predetermined meaning, or to actually mean return NOTHING from a 
> function if used as a declaration type. malloc returns void *, but it
> won't complain if you dont cast the return value and assign it to a 
> char *. Also, the strcmp()  function is used QUITE OFTEN in qsort.
> look at the definition for it though strcmp(const char*,const char*)
> it doesn't have void * declared as you say a routine that qsort calls
> *must* have.

When you assign a void * to another pointer type, there is a conversion
that takes place, just like when you assign a float to an int.  Neither
requires a cast, but the data is converted from one type to another. 
Now, as I said, many systems use the same representation for all
pointer types; on these systems you don't actually have to do anything
to convert from one pointer type to another, just like you don't have
to do anything to convert from a signed int to an unsigned int on a 2's
complement system (but you DO have to do things on a 1's complement or
sign/magnitude system).

However, what we're talking about with qsort is not assignment where the
compiler knows to apply the appropriate conversion.  What we're talking
about is calling a function with one type of argument but defining it
with a different type of argument.  This is like calling sqrt() with an
int argument (and no prototype).  The compiler doesn't know any better,
so it goes ahead and passes an int, but sqrt() treats it like a double
whose value is usually nothing like the value of the int, and you get
back a garbage result.  The same thing happens here: qsort() calls the
comparison function a passes it two void * arguments, but your
comparison function treats those arguments like structure pointers.  If
structure pointers happen to have the same representation as void
pointers, it works; if not, you get garbage.  The only difference
between this and the sqrt() case is that int and double have different
representations on most machines so that almost never works, but struct
pointers and void pointers have the same representation on most machines
so it usually does work.

char * and void * are guaranteed to have the same representation, so you
are guaranteed that you can get away with that error, but technically it
is still an error: void * and char * are different types.  Of course, if
you don't have an ANSI compiler, you *can't* use void *, you have to use
char *, so the guarantee allows existing programs to continue to work.

> And if the code were not portable, it compiles and works on a non-ansi
> compiler (and all of the ANSI ones i tried), so please take back the
> part where you say the code is not correct. it is.

The exact code you posted will, in fact, compile on an ANSI compiler
without diagnostics, but only because you haven't given the compiler
enough information to know it's wrong.  Use prototypes and it will no
longer compile.  It's wrong.
----
Larry Jones, SDRC, 2000 Eastman Dr., Milford, OH  45150-2789  513-576-2070
larry.jones@sdrc.com  or  ...uunet!sdrc!larry.jones
These findings suggest a logical course of action. -- Calvin
Xref: sparky comp.lang.c:16110 alt.cd-rom:4334