[comp.std.c] Casting Function Pointers

martin@mwtech.UUCP (Martin Weitzel) (05/23/91)

There has recently been some discussion about the correct way to
write comparison functions for qsort. (I've seen it as well in the
CUJ as also in a local German group). There seems to be some
consensus that the comparison function must be written expecting
two pointers to void and eventually casting (or assigning) them
internally to the required pointer type.

Taking into consideration that the number and types of function
parameters for any function determines the calling sequence and
further considering that qsort already makes a call to the comparison
function (via the function pointer) and hence has a certain calling
sequence compiled in, this all seems reasonable to me. (By "calling
sequence" I mean the details how parameters are transferred to the
function from the place where the function is called and how the
parameters are accessed from inside the function. Of course, both
ways must match.)

But have a look at the example in K&R-II, page 119-120 and the
explanations that accompany it - isn't it, say, at least a bit
misleading, or, may be, even wrong? (Oh boy, was this hard to type
in now, suspecting the fathers of C to have written something that
might be wrong.)

The point in question is the cast of the comparison function pointer
when handed as parameter to qsort and the explanation for the cast:

	"The elaborate cast of the function argument casts the
	arguments of the comparison function. These will generally
	have no effect on actual representation but assure the
	compiler that all is well."

First, IMHO the cast they apply has not the least effect on the
ARGUMENTS of the COMPARISION function;

Second, IMHO ANSI C even GUARANTEES that the cast has no effect
on the REPRESENTATION, since all function pointers have the same
representation and pointers to char have the same representation
as pointers to void. (This second point would really be of minor
importance - basically it centers on whether one could omit
"generally" from the last sentence above. As this sentence could
be seen to refer also to the sentence immediately before the one
with which I started my citation, one could even argue that the
"generally" justified. But I mention it because I think it points
the naive reader into quite a wrong direction.)

Third and most important: Will the guarantees ANSI C gives with
respect to the (identical) representation of certain pointer types
extend to the calling sequence of a function? Or, in the context
of the above example: Does the identical representation of pointers
to char and pointers to void extend to an identical calling sequence
for |int f1(void *, void *)| and |int f2(char *, char *)| ? If not,
the example in K&R-II is not guaranteed to work!
-- 
Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83

gwyn@smoke.brl.mil (Doug Gwyn) (05/24/91)

In article <1145@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:
>	"The elaborate cast of the function argument casts the
>	arguments of the comparison function. These will generally
>	have no effect on actual representation but assure the
>	compiler that all is well."

K&R2 was written before the final ANSI C standard was published.
In fact, at the time it was written it was technically wrong on
this point.  However, since then X3J11 decided to require void*
and char* to have the same representation, and via a "trickle-up"
effect this implies compatiblility of the two types of function-
pointer argument to qsort().

However, I recommend coding the function according to the correct
interface specification and using casts within the function to
adapt it to the actual situation.

diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/24/91)

In article <1145@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:
>There has recently been some discussion about the correct way to
>write comparison functions for qsort.  There seems to be some
>consensus that the comparison function must be written expecting
>two pointers to void and eventually casting (or assigning) them
>internally to the required pointer type.

I'd agree with that.

>But have a look at the example in K&R-II, page 119-120

Sorry, I don't have one, but ...

>(Oh boy, was this hard to type in now, suspecting the fathers of C to
>have written something that might be wrong.)

There are a few errors and inconsistencies in K&R-I.  And actually I'm waiting
for K&R-III, because the proposed standard changed a little bit after K&R-II
and before finalization.

>	"The elaborate cast of the function argument casts the
>	arguments of the comparison function. These will generally
>	have no effect on actual representation but assure the
>	compiler that all is well."

This wording appears very sloppy, and as you said, gives wrong impressions.
It's not clear if the authors made a technical error (which is entirely
possible) in addition to sloppy wording.  (Their code might make it clear.)

>Third and most important: Will the guarantees ANSI C gives with
>respect to the (identical) representation of certain pointer types
>extend to the calling sequence of a function?

No.  A function with two parameters might have a different calling sequence
from a function with zero parameters.  A function with an int parameter might
have a different calling sequence from a function with a struct parameter.

>Does the identical representation of pointers to char and pointers to void
>extend to an identical calling sequence for
>  int f1(void *, void *)    and    int f2(char *, char *)  ?

I think yes.  The two function types are not compatible (by the definition
of compatible) but should be interchangeable (no definition here) because
of the identical representation of void* and char*.
--
Norman Diamond       diamond@tkov50.enet.dec.com
If this were the company's opinion, I wouldn't be allowed to post it.
Permission is granted to feel this signature, but not to look at it.

diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/24/91)

To clarify my article <1991May24.005025.7714@tkou02.enet.dec.com>:
>In article <1145@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:
>>Third and most important: Will the guarantees ANSI C gives with
>>respect to the (identical) representation of certain pointer types
>>extend to the calling sequence of a function?
>
>No.  A function with two parameters might have a different calling sequence
>from a function with zero parameters.  A function with an int parameter might
>have a different calling sequence from a function with a struct parameter.

The identical representation of void* and char* includes their appearance
in calling sequences of functions.  If that is what you meant, then the
answer is yes.  But two pointers to arbitrary function types, while they
must have identical representations, do not have identical calling sequences.
Sorry about overlooking your probably intended meaning.
--
Norman Diamond       diamond@tkov50.enet.dec.com
If this were the company's opinion, I wouldn't be allowed to post it.
Permission is granted to feel this signature, but not to look at it.

gwyn@smoke.brl.mil (Doug Gwyn) (05/25/91)

In article <1991May24.005025.7714@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes:
>There are a few errors and inconsistencies in K&R-I.  And actually I'm waiting
>for K&R-III, because the proposed standard changed a little bit after K&R-II
>and before finalization.

I'm not aware of any changes between the final draft of K&R2 (first printing)
and the official C standard that would make K&R2 less correct; however, there
was at least one change that made K&R2 more nearly correct.

henry@zoo.toronto.edu (Henry Spencer) (05/26/91)

In article <16259@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>I'm not aware of any changes between the final draft of K&R2 (first printing)
>and the official C standard that would make K&R2 less correct...

I know of at least one -- it's no longer promised that casting a pointer to
integer and back preserves its value if the integer is big enough -- and
there are probably a few more.
-- 
"We're thinking about upgrading from    | Henry Spencer @ U of Toronto Zoology
SunOS 4.1.1 to SunOS 3.5."              |  henry@zoo.toronto.edu  utzoo!henry

diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/27/91)

In article <16259@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>In article <1991May24.005025.7714@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes:
>>There are a few errors and inconsistencies in K&R-I.  And actually I'm waiting
>>for K&R-III, because the proposed standard changed a little bit after K&R-II
>>and before finalization.
>
>I'm not aware of any changes between the final draft of K&R2 (first printing)
>and the official C standard that would make K&R2 less correct; however, there
>was at least one change that made K&R2 more nearly correct.

dmr posted a list of changes about two years ago.  I'm not sure if I have it
in the portion of a damaged tape that might be recoverable.  (Warning about
backups:  when you make tapes on two different machines, in case one machine
might be misaligned or otherwise make tapes that no one else can read ...
well, dual redundancy might not be enough.)
--
Norman Diamond       diamond@tkov50.enet.dec.com
If this were the company's opinion, I wouldn't be allowed to post it.
Permission is granted to feel this signature, but not to look at it.

gwyn@smoke.brl.mil (Doug Gwyn) (05/27/91)

In article <1991May25.221756.16182@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>In article <16259@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>>I'm not aware of any changes between the final draft of K&R2 (first printing)
>>and the official C standard that would make K&R2 less correct...
>I know of at least one -- it's no longer promised that casting a pointer to
>integer and back preserves its value if the integer is big enough -- and
>there are probably a few more.

X3.159-1989 Section 3.3.4 Semantics require that there be some such
implementation-defined type.  Well, actually, it doesn't require that
the "before" and "after" pointers compare equal, but that is clearly
the intent, for implementations where this is even possible.  There
can be some implementations with, say, 128-bit addresses and 64-bit
data words, for which some information would necessarily be lost by
the transformation.  Thus, while K&R2 section A6.6 technically
promises more than the C standard requires, it does indicate what the
intended behavior is, for implementations for which it is feasible.

Was that really changed during the public review process?  I don't
have the old drafts to look it up in.

gwyn@smoke.brl.mil (Doug Gwyn) (05/27/91)

In article <1991May27.003536.25876@tkou02.enet.dec.com> diamond@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes:
>In article <16259@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>>I'm not aware of any changes between the final draft of K&R2 (first printing)
>>and the official C standard that would make K&R2 less correct; however, there
>>was at least one change that made K&R2 more nearly correct.
>dmr posted a list of changes about two years ago.

While that's true, it doesn't invalidate what I said.  The only substantive
change of any note in the errata list that could be blamed on late changes
in the draft standard was CLK_TCK => CLOCKS_PER_SEC.

diamond@jit533.swstokyo.dec.com (Norman Diamond) (05/27/91)

In article <16271@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>In article <1991May25.221756.16182@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>>In article <16259@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>>>I'm not aware of any changes between the final draft of K&R2 (first printing)
>>>and the official C standard that would make K&R2 less correct...
>>I know of at least one -- it's no longer promised that casting a pointer to
>>integer and back preserves its value if the integer is big enough -- and
>>there are probably a few more.
>
>X3.159-1989 Section 3.3.4 Semantics require that there be some such
>implementation-defined type.  Well, actually, it doesn't require that
>the "before" and "after" pointers compare equal, but that is clearly
>the intent, for implementations where this is even possible.

I nearly pounced on the first sentence with a "wrong" (in the style that
Mr. Gwyn was famous for, though recently he seems more sociable).  The
second sentence indeed points out that the first one is wrong.

As for intents, Section 3.3.4, and even the footnote, do not say.  There
is no requirement and not even a suggestion.  Intuitively, and for quality
of implementation, one might like to see it.  However, neither portable
code nor strictly conforming code could make any use of it.
--
Norman Diamond       diamond@tkov50.enet.dec.com
If this were the company's opinion, I wouldn't be allowed to post it.
Permission is granted to feel this signature, but not to look at it.

henry@zoo.toronto.edu (Henry Spencer) (05/27/91)

In article <16271@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>>I know of at least one -- it's no longer promised that casting a pointer to
>>integer and back preserves its value if the integer is big enough...
>
>...Was that really changed during the public review process?  I don't
>have the old drafts to look it up in.

I don't have them handy either.  I remember noticing that the "you can do
this" promise had disappeared, but I have no idea when it was.

Probably something to do with the AS/400. :-)
-- 
"We're thinking about upgrading from    | Henry Spencer @ U of Toronto Zoology
SunOS 4.1.1 to SunOS 3.5."              |  henry@zoo.toronto.edu  utzoo!henry

david@sco.COM (David Fiander) (05/28/91)

In article <1991May27.153520.4547@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>In article <16271@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>>>I know of at least one -- it's no longer promised that casting a pointer to
>>>integer and back preserves its value if the integer is big enough...
>>
>
>I don't have them handy either.  I remember noticing that the "you can do
>this" promise had disappeared, but I have no idea when it was.
>
>Probably something to do with the AS/400. :-)

From what you've (or was it doug) said, that wouldn't surprise me, but
the same thing happens on the CDC Cyber series, and probably on the Prime
9600 series (I can't check, it's not up right now).  These machines use
rings for protection and, on the Cybers, converting from a pointer to an
int may be all right, but converting from an int to a pointer zeros the
ring bits in the pointer created.  This caused us all sorts of problems,
because the test

	p == (char *)-1

will have a different result from the test

	(int)p == -1

malloc broke because of this, and it took us two or three 12-hour days to
figure it out.

--
David J. Fiander
SCO MMDF Development Team
SCO Canada, Inc.

norvell@csri.toronto.edu (Theo Norvell) (05/29/91)

In article <1991May28.124930.19893@sco.COM> david@sco.COM (David Fiander) writes:
>>In article <16271@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>>>>I know of at least one -- it's no longer promised that casting a pointer to
>>>>integer and back preserves its value if the integer is big enough...
>>>
>on the Cybers, 
>
>	p == (char *)-1
>
>will have a different result from the test
>
>	(int)p == -1
>

It would be a bit much to expect there to be a bijection between 48 bit
pointers and 64 bit integers.  On recent 180 C compilers
	p == (char *)(int)p
which is what Doug was talking about.  The value is preserved, even if the
representation is not.  This is because pointer comparison ignores ring
numbers -- it has to or two pointers to the same object could
compare as different.