Bug in new K&R?

dmr at alice.UUCP dmr at alice.UUCP
Mon May 9 18:39:26 AEST 1988


Tim, the Bizarre and Oddly-Dressed Enchanter, questions
(44200009 at uicsrd.csrd.uiuc.edu) the call to qsort in K&R 2, p. 119.
The call is

	qsort((void **) lineptr, 0, nlines-1,
	  (int (*)(void*,void*))(numeric ? numcmp : strcmp));

and the thing in question is that messy second line.  He's right
to remain suspicious, even though we seem to have passed the
Gwyn, Torek, and Spencer tests with no more than minor cuts and
abrasions.

The intent of the example is to show how to deal with function pointers.
Arrays of lines are being compared either numerically or lexically,
and either the numeric or string comparison function is being passed to qsort.
I suspect the example should have been simplified; it raises too many issues.
Here are some of the problems with it.  I leave aside the question
of whether it would be better to rewrite the call without the ?: or
otherwise fiddle it to make it more readable; the question is whether
it is correct.

First: is `numeric? numcmp: strcmp' legal?  We declared numcmp above
as `int numcmp(char *, char *)' while letting strcmp come from the
standard header; it is (as of Jan 11, but dropping the `noalias')
`int strcmp(const char *, const char *).'  The relevant question is
whether the two arms of the conditional are `pointers to compatible types,'
and the answer, I'm afraid, is no, because the parameters are not
identically qualified [3.5.3; p. 66, line 11 of the Jan 11 draft].

Second: [easy] is the cast (int (*)(void*,void*)) legal?  Yes; essentially
all vaguely sensible casts are legal.

Third: is the argument to qsort legal?  Yes, with the declaration
for qsort given on p. 119 of the text; the types of the parameter
and the argument agree.  But observe that the library qsort routine
(which is declared in <stdlib.h>) takes a third argument of type

	int (*)(const void*, const void*)

and the Standard warns that you are on your own if you write
your own version of library functions, and here the parameter types
are not even identical.

Fourth: Is it well-defined what happens when qsort calls the comparison
function?  Perhaps not, according to the standard.  `A pointer to a function
may be converted to a pointer to another type.... If a converted pointer
is used to call a function that is not compatible with the type
of the called function, the behavior is undefined' [3.3.4].
So it depends on whether a function of type

	(*)(void*, void*)

which is the one our qsort uses, will work when it calls a comparison
routine declared with either of

	(*)(const char*, const char*)
	(*)(char*, char*)

which are the types of the pointer versions of strcmp and numcmp.
In spite of the fact that the type void* is now guaranteed to have
the same representation as char*, I don't think that the type
rules of the dpANS guarantee this will work.

In our defense, given that the representation of char* is the same
as that of void*, it is reasonable to expect that you would be safe
in reproducing the book example provided your compiler accepted it.
However, at least as I read the last-issued version of the standard,
the compiler might well reject it.

A lot of people are going to be surprised when ANSI compilers become
common.  As I have argued before, type qualifiers are not an unmixed
blessing.

	Dennis Ritchie
	research!dmr
	dmr at research.att.com



More information about the Comp.lang.c mailing list