Path: utzoo!mnetor!uunet!husc6!mailrus!umix!umich!mibte!gamma!ulysses!andante!alice!dmr From: dmr@alice.UUCP Newsgroups: comp.lang.c Subject: Re: Bug in new K&R? Message-ID: <7861@alice.UUCP> Date: 9 May 88 08:39:26 GMT Organization: AT&T Bell Laboratories, Murray Hill NJ Lines: 76 Tim, the Bizarre and Oddly-Dressed Enchanter, questions (44200009@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) 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@research.att.com