[comp.lang.c] %p and different pointer representations

dff@Morgan.COM (Daniel F. Fisher) (02/24/89)

In article <9382@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu
(Steve Summit) writes:
>
>No, I just use %p.
>

Consider a hosted C implementation in which different pointers
have different representations, i.e. (char *) is different from
(int *) is different from (int (*)()), etc.  My question is

To what type should a pointer argument be cast when passing it to
fprintf() for printing using the %p conversion specifier?

In view of the following two examples, I don't see that there is
a single correct answer that will work in all architectures.  And
since %p is part of dANS C, I assume that the answer must be the
same for every pointer in every conforming hosted implementation.

First Example: In an IBM-PC implementation with small-data,
large-code, the data pointers (int *), (char *), etc. are all 16
bits., but function pointers (int (*)()), etc. are all 32 bits.
So if I wish to print a function pointer, I cannot cast it to a
data pointer without losing information.  One the other hand,
since the type cast used for both function pointers and data
pointers should be the same, I must cast all data pointers to
something that is as wide as a function pointer.

Second Example: In an implementation on an architecture that is
NOT byte addressable, int pointers and function pointers will be
word addresses and character pointers will be wider to contain
both a word address and a byte offset.  So if I wish to print a
character pointer, I cannot cast it to an int pointer or function
pointer without losing information.  One the other hand, since the
type cast used for all pointers must be the same, I must cast int
pointers and function pointers to something as wide as a character
pointer.

So in the first example, I want to cast everything to be as wide
as a function pointer and in the second, I want to cast everything
to be as wide as a character pointer (which is wider than a function
pointer).  What's a programmer to do?  Now if someone wants to tell
me that I should cast to (void *) because it is guaranteed to be at
least as wide as any pointer type in the implementation, then I will
say, "Thank you very much", "Wonderful", "Hooray for void star" and
"Gee, I didn't know that".

Well, what gives?

-- 
Daniel F. Fisher
dff@morgan.com

chris@mimsy.UUCP (Chris Torek) (02/25/89)

In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>To what type should a pointer argument be cast when passing it to
>fprintf() for printing using the %p conversion specifier?

Put it this way: `%p' prints one (void *).

>First Example: In an IBM-PC implementation with small-data,
>large-code, the data pointers (int *), (char *), etc. are all 16
>bits., but function pointers (int (*)()), etc. are all 32 bits.
>So if I wish to print a function pointer, I cannot cast it to a
>data pointer without losing information.

Yes.  Moreover, since sizeof(void *)==sizeof(char *)==2, when you
print a pointer-to-function using %p and a (void *) cast (on that
machine in that model), you will lose some information.

I think this is what is known as an oversight (and goes to show why
standards committees should avoid inventions)....
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/25/89)

In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>To what type should a pointer argument be cast when passing it to
>fprintf() for printing using the %p conversion specifier?

(void *), or, since it has the same representation, (char *).

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/25/89)

In article <16112@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>I think this is what is known as an oversight (and goes to show why
>standards committees should avoid inventions)....

No, it's not an oversight.  Obviously we could not require generic
object pointers to be large enough to act as generic function pointers
also.  This is clearly reflected in the specification for void *.
A second kind of generic pointer would have had to been invented to
hold function addresses.  We didn't invent one, on the grounds of
"insufficient utility".  Personally I would rather the %p format not
have been introduced, but given the existence of void *, it was a
natural addition.

It is pretty easy to snipe at X3J11's work and imply that you could
have done better, but I note that you didn't help formulate the
standard.  If you had any constructive criticism to offer, you should
have done so during the public review process.

scs@adam.pika.mit.edu (Steve Summit) (02/25/89)

In article <1089@vicorp.UUCP> charlie@vicorp.UUCP (Charlie Goldensher) writes:
>In article <9382@bloom-beacon.MIT.EDU>, scs@adam.pika.mit.edu (Steve Summit) writes:
>> No, I just use %p.
>That's fine if you don't expect to port the code to another machine.
>But be aware that not all versions of printf provide the %p option.

Yes, but see below.

In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>To what type should a pointer argument be cast when passing it to
>fprintf() for printing using the %p conversion specifier?

An excellent question.  The previously-posted answers (cast to void *)
are correct, as far as the standard goes.  The case of the 80x86
(IBM PC et al) is, as usual, er, "enlightening."  (I never think
of peering down cesspools as being particularly enlightening;
"engloomening" or "endrearening," perhaps.)

Under Microsoft's PC C compiler (I don't know about others), %p
expects a 32-bit ("far") pointer, in all memory models.  Therefore,
the pointer passed must, in general, be cast to "void far *".
Although this, I suppose, violates the standard, it's probably
the right solution for that architecture, since not forcing a far
pointer admits the possibility of losing information.  (You're
probably printing a pointer because you need to know its value;
therefore having printf throw half of it away would not be
terribly useful.)

Note that I am not defending the 80x86, nor excusing Microsoft
for violating the forthcoming standard (it's hardly the first
time, anyway).  I do think Microsoft's compromise was a good one,
under the circumstances.  (A better one might have been to make
%p strictly standard-conforming albeit potentially useless, and
use %P as a nonstandard extension to force 32 bit pointers.  This
idea has other problems, however.)

Although I've been using %p regularly on the PC, I hadn't thought
about its implications until Daniel asked his question.  I'm
afraid that one of two conclusions is inescapable:

     1.	The ANSI standard is flawed, as far as %p is concerned, or
     2.	an 80x86 that supports mixed-model programming cannot
	support C.

I've been impressed by the number of architectural abominations
that turn out to be compatible with C, given correctly-written,
portable code.  The extent to which this is true is a tribute to
Dennis Ritchie's wisdom and foresight.  (A case in point is the
recent frequent discussion of the inadvisability of simulating
non-zero-based arrays with "funny" pointers to where [0] would
be.  Such pointers have been illegal since at least 1978, yet
most of us have only recently come face-to-face with machines
which actually disallow them.)

That %p may not enjoy this kind of widespread, forward-thinking
portability is not a disparagement of X3J11.  (Before anyone gets
offended, this means I'd go with conclusion 2 above, at the risk
of alienating all of the 80x86 aficionadoes in the world, both of
them.)  %p has proved to be quite useful; it fills a need and I'm
glad it was added.  If nothing else, this difficulty merely lends
further credence to a truth many have long known: segmented
architectures are a crime against the industry.  (Intel and IBM
are guilty of mass programmer genocide for foisting it upon the
world as the people's architecture of choice.)

In practice, printing pointers is pretty unportable, but it isn't
much of a problem.  (Where's the peck of pickled peppers Peter
Piper picked?)  I can think of few uses of %p outside of
debugging code, and it's less important that debugging code be
portable.  (I usually get most of the bugs out before I start
porting.)  The few exceptions I can think of are programs like
linkers that want to print addresses, but those programs aren't
terribly portable, either.

To summarize, don't lose much sleep over %p difficulties.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) (02/26/89)

In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>Consider a hosted C implementation in which different pointers
>have different representations, i.e. (char *) is different from
>(int *) is different from (int (*)()), etc.  My question is
>
>To what type should a pointer argument be cast when passing it to
>fprintf() for printing using the %p conversion specifier?
>
>In view of the following two examples, I don't see that there is
>a single correct answer that will work in all architectures.  And
>since %p is part of dANS C, I assume that the answer must be the
>same for every pointer in every conforming hosted implementation.

Casting the pointer to (void *) is supposed to do what you want.  
The pANS says that a void pointer is large enough to handle any 
pointer type (ss 3.2.2.3), much like "char *" was supposed to 
handle any pointer type in old K&R.  The documentation for scanf 
(ss 4.9.6.2) and printf (ss 4.9.6.1), also say that the %p is 
expected to be a void pointer.

>First Example: In an IBM-PC implementation with small-data,
>large-code, the data pointers (int *), (char *), etc. are all 16
>bits., but function pointers (int (*)()), etc. are all 32 bits.
>So if I wish to print a function pointer, I cannot cast it to a
>data pointer without losing information.  One the other hand,
>since the type cast used for both function pointers and data
>pointers should be the same, I must cast all data pointers to
>something that is as wide as a function pointer.

Casting the pointer to "void *" is not supposed to lose information 
for any conforming implementation.  On the IBM-PC, using Turbo C 2.0,
printf("main %p\n", main) will print out a segment:offset address
like so:

	main 252B:000C

It does this for all memory models. (I just checked).  The address
which is printed corresponds to the link map which I generated as well.
I checked this for functions, pointers to functions, static and global
data in all memory models.   The results were always correct.

A side note here: the %p parameter prints the result out in an
implementation defined format, so the above example is what you get 
on the IBM-PC, while on a SUN you may get:


	main 0x00678A

The reason, of course, is to allow the implementation to print the 
address out in whatever format is most usefull for the particular 
architecture or implementation.


>Second Example: In an implementation on an architecture that is
>NOT byte addressable, int pointers and function pointers will be
>word addresses and character pointers will be wider to contain
>both a word address and a byte offset.  So if I wish to print a
>character pointer, I cannot cast it to an int pointer or function
>pointer without losing information.  One the other hand, since the
>type cast used for all pointers must be the same, I must cast int
>pointers and function pointers to something as wide as a character
>pointer.

The void pointer is gauranteed to be wide enough by the pANS.  If it 
is not able to handle this example, then you should have a serious 
chat with your compiler vendor.

>So in the first example, I want to cast everything to be as wide
>as a function pointer and in the second, I want to cast everything
>to be as wide as a character pointer (which is wider than a function
>pointer).  What's a programmer to do?  Now if someone wants to tell
>me that I should cast to (void *) because it is guaranteed to be at
>least as wide as any pointer type in the implementation, then I will
>say, "Thank you very much", "Wonderful", "Hooray for void star" and
>"Gee, I didn't know that".

Fair enough, now you do.  :-)

-- 
Mark H. Colburn                  "Look into a child's eye;
Minnetech Consulting, Inc.        there's no hate and there's no lie;
mark@jhereg.mn.org                there's no black and there's no white."

mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) (02/26/89)

In article <16112@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>Yes.  Moreover, since sizeof(void *)==sizeof(char *)==2, when you
>print a pointer-to-function using %p and a (void *) cast (on that
>machine in that model), you will lose some information.

Wait a minute, Chris.  The pANS says (ss 3.2.2.3, pp 37 Oct 31, 88):

"A pointer to void may be converted to or from a pointer to any 
 incomplete or other type.  A pointer to any incomplete or object 
 type may be converted to a pointer to void and back again; the 
 result shall compare equal to the original pointer."

What you are saying would seem to contradict this.

Also, I don't beleive that there is any requirement that (void *) has to be
2 bytes long.  In my opinion void should be defined to be at least as
large as the largest pointer available in the current compiler model.

Therefore, on the IBM-PC, sizeof (void *) should be 4 in every model except
for small or tiny.  This can be checked using the following program:

	#include <stdio.h>

	int main()
	{
	    printf("sizeof(char *) = %d\n", sizeof(char *));
	    printf("sizeof(void *) = %d\n", sizeof(void *));
	    printf("sizeof(int (*)()) = %d\n", sizeof(int (*)()));
	    return (0);
	}

Using Turbo C, I got the following results:

   +-------------------------------------------------------------------+
   | Model       | sizeof(char *) | sizeof(void *) | sizeof(int (*)()) |
   +=============+================+================+===================|
   | Huge        |        4       |        4       |         4         |
   +-------------+----------------+----------------+-------------------|
   | Large       |        4       |        4       |         4         |
   +-------------+----------------+----------------+-------------------|
>> | Medium      |        2       |        2       |         4         | <<
   +-------------+----------------+----------------+-------------------|
   | Small       |        2       |        2       |         2         |
   +-------------+----------------+----------------+-------------------|
   | Tiny        |        2       |        2       |         2         |
   +-------------+----------------+----------------+-------------------+


If you take a look at the medium model, you will notice the annomally.

However, on the IBM-PC, the compiler may be able to tell the difference
between a code and data pointer by checking which segment it is in.  I am
not sure exactly how Turbo C does this, although I will check.  This 
assumption would break down if the application was attempting to
dynamically load functions and execute them (which would tend to put them
into the data segment).  Taking a function pointer in this case, may,
indeed, case problems.  There are several applications that I know of that
do this.

I will point out, however, the the %p notation does appear to work in all
of the memory models correctly.  I did not try dynamic function loading,
but I suppose that I could.

-- 
Mark H. Colburn                  "Look into a child's eye;
Minnetech Consulting, Inc.        there's no hate and there's no lie;
mark@jhereg.mn.org                there's no black and there's no white."

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/26/89)

In article <9453@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>Under Microsoft's PC C compiler (I don't know about others), %p
>expects a 32-bit ("far") pointer, in all memory models.  Therefore,
>the pointer passed must, in general, be cast to "void far *".

This is the essential botch in the MicroSoft implementation(s).
void * is REQUIRED to be big enough to hold ANY pointer to object
or incomplete type without loss of information, and %p is REQUIRED
to correspond to a void * (or char *, same representation).
If a void * is not big enough, you already have problems quite
apart from %p.

> 2. an 80x86 that supports mixed-model programming cannot support C.

A standard-conforming implementation of C must make a SINGLE definite
choice of how big each object type is.  This includes pointers.

>segmented architectures are a crime against the industry.

There is nothing inherently wrong with segmented architectures;
some of the nicest computers ever built have been segment-oriented.

Some programming languages (such as Fortran) do not fit well the
segment model, so implementations of such languages on segmented
machines aren't generally satisfactory, although they can be made
to work.  Other languages (such as Algol) are an almost perfect
match to the segemented model.  C is not a perfect fit (its main
problem on a segmented machine is implementing malloc() reasonably)
but it's pretty close.  Unfortunately, many programmers weaned on
the VAX (or similar architectures) have developed bad habits that
don't port to other architectures.

The real crime, in my opinion, was the introduction of several
"memory models" for C programming on the 80*86 architecture and the
subsequent attempt to link together things compiled under different
models.  I'm familiar with the rationale behind that, but I don't
agree with it.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/26/89)

In article <591@jhereg.Jhereg.MN.ORG> mark@jhereg.MN.ORG (Mark H. Colburn) writes:
>The pANS says that a void pointer is large enough to handle any 
>pointer type ...

No, only any pointer to an incomplete or object type.
A function pointer may well not fit into a void*.

chris@mimsy.UUCP (Chris Torek) (02/26/89)

In article <592@jhereg.Jhereg.MN.ORG> mark@jhereg.Jhereg.MN.ORG
(Mark H. Colburn) writes:
>Wait a minute, Chris.  The pANS says (ss 3.2.2.3, pp 37 Oct 31, 88):
>
>"A pointer to void may be converted to or from a pointer to any 
> incomplete or other type.  A pointer to any incomplete or object 
> type may be converted to a pointer to void and back again; the 
> result shall compare equal to the original pointer."

I believe this paragraph refers only to `data' pointers---at least, the
last time I checked carefully, `void *'s were not supposed to be used
to hold function pointers.  Function pointers are sometimes bigger than
data pointers, as with medium model IBM PC compilers and (to a greater
extent) with Sperry systems (I hear one compiler for the Univac 1100
has 18-bit pointers to data objects and 162-bit pointers to functions).
Again, the last time I checked---I do not now have access to the current
proposed standard---the standard had essentially two major sub-groups
of pointers, and said that, for all data types T1, T2:

	if sizeof(T1) < sizeof(T2), sizeof(T1 *) >= sizeof(T2 *)

	sizeof(T1 *) <= sizeof(char *) == sizeof(void *)

	sizeof(T1 (*)()) == sizeof(T2 (*)())

In English: a pointer to one data type can only have more bits than a
pointer to another if the first type (T1) has more bits than the
second; all data pointers (but not necessarily function pointers)
fit in (char *)/(void *); and all function pointers fit in each other.

>What you are saying would seem to contradict this.

I do remember that, at one point, I thought (void *) had to hold
function pointers as well.  Knowing of medium model and of that Sperry
compiler, I checked the then-current draft, and (after much squinting
and re-reading of fine print) discovering otherwise.

>Also, I don't beleive that there is any requirement that (void *) has to be
>2 bytes long.  In my opinion void should be defined to be at least as
>large as the largest pointer available in the current compiler model.

I would agree.  Alas, the standard says that (void *) and (char *) must
have the same representation, so to comply with our opinions (as
opposed to the letter of the proposed standard), an IBM PC compiler or
Sperry compiler or . . . would have to make (char *) be twice, or nine
times, or whatever, as big as necessary.  (If the proposed standard
says only that (void *) must hold function pointers, one could avoid
this penalty for (char *) pointers.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) (02/26/89)

Damn.  I hate it when the article propagate faster than the cancel message
that follows them...

In article <16120@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>I do remember that, at one point, I thought (void *) had to hold
>function pointers as well.  Knowing of medium model and of that Sperry
>compiler, I checked the then-current draft, and (after much squinting
>and re-reading of fine print) discovering otherwise.

This is the delusion that I was under as well.  It turns out to be wrong.
 
>>Also, I don't beleive that there is any requirement that (void *) has to be
>>2 bytes long.  In my opinion void should be defined to be at least as
>>large as the largest pointer available in the current compiler model.

Unfortunately, there is.  I spent last night re-reading the pANS, and there
is a paragraph which states that (void *) must be the same size and have
the same alignment as (char *).  This seems really silly to me.  Why
introduce a new type if it has no new functionality?  I understand that the
commitee did not want to break existing code, but if add a new feature...

As soon as I found this out, I canceled the article, since it was
definitely wrong.  At the same time I prayed that it would not get out, but
I knew better...

By the way, before Doug slams me for not participating in the comment cycle
of the standard, I did, however, I piggybacked on another persons ballot
since I got involved to late to become a balloting member myself.  Somehow
I missed this when I was reviewing the standard.

>I would agree.  Alas, the standard says that (void *) and (char *) must
>have the same representation, so to comply with our opinions (as
>opposed to the letter of the proposed standard), an IBM PC compiler or
>Sperry compiler or . . . would have to make (char *) be twice, or nine
>times, or whatever, as big as necessary.  (If the proposed standard
>says only that (void *) must hold function pointers, one could avoid
>this penalty for (char *) pointers.)

True.  My original concept of a void pointer was a pointer which was
maximally aligned and large enough to hold any pointer.  It would seem that
this would be more useful that the (char *) paradigm that is being
proposed.  What it means, is that there is no portable way to pass an
arbitrary pointer to a function or store one in a data structure.  That is
really too bad.

-- 
Mark H. Colburn                  "Look into a child's eye;
Minnetech Consulting, Inc.        there's no hate and there's no lie;
mark@jhereg.mn.org                there's no black and there's no white."

tjr@cbnewsc.ATT.COM (thomas.j.roberts) (02/27/89)

 In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>To what type should a pointer argument be cast when passing it to
>fprintf() for printing using the %p conversion specifier?
>First Example: In an IBM-PC implementation with small-data,
>large-code, the data pointers (int *), (char *), etc. are all 16
>bits., but function pointers (int (*)()), etc. are all 32 bits.
>So if I wish to print a function pointer, I cannot cast it to a
>data pointer without losing information.

In TURBO C, you can do:
	int *p;
	printf("%Fp",(void far *)p);
in ANY MEMORY MODEL, for any size of data pointer p, and get printed
"XXXX:YYYY", where XXXX is the segment, and YYYY is the offset for pointer p.
If p is a small data pointer, DS will be supplied by the compiler.
Note that using the "near" keyword is dangerous in a large-data memory model,
because DS != SS, and DS is supplied when converting near pointers to far
pointers. I believe that the keywords "_ds", "_es", and "_ss" are intended
to permit proper use of small pointers in a large-pointer memory model.

For a function pointer, do:
	typedef int far (*FAR_FUN_PTR)(); /* NOT TESTED */
	printf("%Fp",(FAR_FUN_PTR)p);
Again, the "near" keyword is dangerous - it is not clear whether it can 
sensibly be used with function pointers in a large-code memory model.
In a large-code model, simply do:
	int (*fun_ptr)();
	printf("%Fp",fun_ptr);

Tom Roberts
att!ihnet!tjr

henry@utzoo.uucp (Henry Spencer) (02/28/89)

In article <597@jhereg.Jhereg.MN.ORG> mark@jhereg.MN.ORG (Mark H. Colburn) writes:
>... I spent last night re-reading the pANS, and there
>is a paragraph which states that (void *) must be the same size and have
>the same alignment as (char *).  This seems really silly to me.  Why
>introduce a new type if it has no new functionality? ...

The functionality of "void *" is to be a replacement for all the places
where "char *" was used as a generic pointer because there wasn't any
generic pointer.  For example, the first argument to fwrite().  If "char *"
was not generic enough for those places in your implementation, your
implementation was broken to begin with.  The requirement for representation
compatibility with "char *" is vital for backward compatibility with all
that code that *does* use "char *" as a generic pointer.

If you study things like the conversion rules, you'll see that "void *"
does have new functionality.  But expecting it to fix your broken compiler
(or the broken machine that is the cause of the compiler breakage) is a
bit too much to hope for.
-- 
The Earth is our mother;       |     Henry Spencer at U of Toronto Zoology
our nine months are up.        | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

bright@Data-IO.COM (Walter Bright) (02/28/89)

In article <9453@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>Under Microsoft's PC C compiler (I don't know about others), %p
>expects a 32-bit ("far") pointer, in all memory models.  Therefore,
>the pointer passed must, in general, be cast to "void far *".
>Although this, I suppose, violates the standard, it's probably
>the right solution for that architecture, since not forcing a far
>pointer admits the possibility of losing information.
>I do think Microsoft's compromise was a good one,
>under the circumstances.  (A better one might have been to make
>%p strictly standard-conforming albeit potentially useless, and
>use %P as a nonstandard extension to force 32 bit pointers.)

Under Zortech's printf(), %p expects a pointer of the default data pointer
size. %lp always expects a far pointer.
Thus, to print each type of pointer in each memory model:
	void *		func *		void near *	void far *
S	%p		%p		%x		%lp
M	%p		%lp		%x		%lp
C	%p		%x		%x		%lp
L	%p		%p		%x		%lp

Things to note:
1. Using default data pointers, %p always works.
2. Using far pointers, %lp always works, it's easy to remember because
   of the similarity with %lx.
3. Using near pointers, %x always works. It seemed redundant to add a
   special flag for %p for near pointers, since %x did the job nicely.
4. Be careful using function pointers! A compromise is to cast them to
   (void far *), and use %lp on them.

P.S. I wrote it.

cjc@ulysses.homer.nj.att.com (Chris Calabrese[mav]) (02/28/89)

>  In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
> >To what type should a pointer argument be cast when passing it to
> >fprintf() for printing using the %p conversion specifier?
> >First Example: In an IBM-PC implementation with small-data,
> >large-code, the data pointers (int *), (char *), etc. are all 16
> >bits., but function pointers (int (*)()), etc. are all 32 bits.
> >So if I wish to print a function pointer, I cannot cast it to a
> >data pointer without losing information.
> 
> In TURBO C, you can do:
> 	int *p;
> 	printf("%Fp",(void far *)p);
> in ANY MEMORY MODEL, for any size of data pointer p, and get printed
> "XXXX:YYYY", where XXXX is the segment, and YYYY is the offset for pointer p.
> If p is a small data pointer, DS will be supplied by the compiler.
> Note that using the "near" keyword is dangerous in a large-data memory model,
> because DS != SS, and DS is supplied when converting near pointers to far
> pointers. I believe that the keywords "_ds", "_es", and "_ss" are intended
> to permit proper use of small pointers in a large-pointer memory model.

[Other examples and arguments deleted]

#define Flame

Yes, but the whole point of %p and void is to make such things
_more_ portable, where as this example _only_ works on 80*86
architectures, and relies on keywords that aren't even part of C,
but are a non-conforming polution of keyword name space invented
for IBM PC compilers.

#undef Flame

The point is, %ld works on 99% of the machines out there now,
why should I use %p if it makes things even less portable?
The representation of the pointer is non-portable,
but accessing it should not be.  I can see that you
don't really need to pass around function pointers as void*,
but there should be a way of printing their values!
However, please don't give me
	%h - prints a pointer to a function
	     (h is the first free letter after f, which is taken)
-- 
Name:			Christopher J. Calabrese
Brain loaned to:	AT&T Bell Laboratories, Murray Hill, NJ
att!ulysses!cjc		cjc@ulysses.att.com
Obligatory Quote:	``Now, where DID I put that bagel?''

mtr@ukc.ac.uk (M.T.Russell) (02/28/89)

In article <9730@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>A function pointer may well not fit into a void*.

This presumably implies that comparison and assignment between function
pointers and void* pointers is illegal, and thus that

	f = NULL;

and

	if (f == NULL)

are both illegal if f is a function pointer and NULL is defined as (void *)0.
If this is so, then I don't see that (void *)0 is a reasonable definition
of NULL.

Is (void *)0 a special case?  Gcc 1.32 -ansi -pedantic doesn't think so -
it objects to the above constructs.

Mark Russell
mtr@ukc.ac.uk

karl@haddock.ima.isc.com (Karl Heuer) (03/02/89)

In article <597@jhereg.Jhereg.MN.ORG> mark@jhereg.MN.ORG (Mark H. Colburn) writes:
>What it means, is that there is no portable way to pass an arbitrary pointer
>to a function or store one in a data structure.

Any function-pointer object may be cast to any other function-pointer type and
back without change; hence any such type (e.g. `void (*)(void)') may be used
as a generic function-pointer type.  Therefore, a completely generic pointer
type (to function or data) is `union {void (*)(void); void *}', though I can't
think of any practical use for such a thing.  (The (nonportable) uses I can
come up with are better served by a cast rather than a union.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

c08_d103@jhunix.HCF.JHU.EDU (Ex-God) (03/02/89)

I need a few things clarified; I don't have a copy of the standard,
and a few people have said mutually contradictory things.  So tell me
if the following is right:

Any object or incomplete pointer fits in (void *).
Any object or inconplete pointer fits in (char *).
Any function pointer fits in any other function pointer.
Function pointers do not have to fit into (void *) (or vice-versa).

Also, how's this for a solution:
#if sizeof (void *) > sizeof (void (*)())
#define generic_ptr (void *)
#else
#define generic_ptr (void (*)())
#endif
Then use generic_ptr instead of void *, and any pointer type
(including pointers to functions) will fit (as long as you remember to
cast everything you assign).

I can see one problem with this solution -- if, for example, an
implementation stored functions in one area and objects in another and
there was no pointer type that could point to either.  I suppose you
could use a union or something....

I guess the best solution would have been to continue using char * as
a generic object pointer, to make all function pointer the same size,
and to make void * big enough to hold either.  Someone should have
thought of that earlier, I guess....

     -- Andrew Barnert (Andy Social/Andy Christ/Andy Matter/ex-God)
        ins_balb@jhunix/ins_balb@jhuvms/c08_d103@jhunix
The opinions expressed in this message are yours.
"If you can't stand the Big Chill, burn down the freezer." -- Jello Biafra

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/02/89)

In article <248@harrier.ukc.ac.uk> mtr@ukc.ac.uk (M.T.Russell) writes:
>In article <9730@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>>A function pointer may well not fit into a void*.
>This presumably implies that comparison and assignment between function
>pointers and void* pointers is illegal, and thus that
>	f = NULL;
>and
>	if (f == NULL)
>are both illegal if f is a function pointer and NULL is defined as (void *)0.
>If this is so, then I don't see that (void *)0 is a reasonable definition
>of NULL.
>Is (void *)0 a special case?  Gcc 1.32 -ansi -pedantic doesn't think so -
>it objects to the above constructs.

Yes, null pointer constants are special cases.  (void*)0 is not really
a pointer of type void*, it's just one way of writing a null pointer
constant and thus has special properties.  In particular it can be
assigned to ANY non-const pointer variable and compared against ANY
pointer type.  It compares unequal to a pointer to any object or function.
A valid pointer expression of any type has a value that fits exactly one
of the following categories:
	(1) valid pointer to object or function
	(2) null pointer
An uninitialized static-duration pointer variable starts out with a value
that is a null pointer; other pointer variables acquire null-pointer
values only by having one assigned to them, either directly as a null
pointer constant or indirectly via some other null-valued variable.

GCC 1.32 sounds broken here.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/02/89)

In article <928@jhunix.HCF.JHU.EDU> ins_balb@jhunix.UUCP (Andy Matter) writes:
>I need a few things clarified; I don't have a copy of the standard,
>and a few people have said mutually contradictory things.  So tell me
>if the following is right:
>Any object or incomplete pointer fits in (void *).

Yes, any POINTER to an object or to an incomplete type after conversion
to void* and back and will still compare equal to the original.

>Any object or inconplete pointer fits in (char *).

Yes, but you have to exert some care to get it into or out of one.
In particular, you have to use casts.

>Any function pointer fits in any other function pointer.

Yes, but you have to exert some care.
In particular, you have to use casts.

By the way, to USE a pointer, it has to be of the right type at the
place it is used.  It is best and simplest to maintain pointers as
their correct types all along, rather than unnecessarily mapping
them back and forth into other types.  For one thing, if you use
proper types everywhere, the compiler can help detect usage errors.

>Function pointers do not have to fit into (void *) (or vice-versa).

Yes.

>Also, how's this for a solution:
>#if sizeof (void *) > sizeof (void (*)())
>#define generic_ptr (void *)
>#else
>#define generic_ptr (void (*)())
>#endif
>Then use generic_ptr instead of void *, and any pointer type
>(including pointers to functions) will fit (as long as you remember to
>cast everything you assign).

Won't work.  There is no guarantee that an object pointer can be
converted to a function pointer or vice-versa.

>I guess the best solution would have been to continue using char * as
>a generic object pointer, to make all function pointer the same size,
>and to make void * big enough to hold either.  Someone should have
>thought of that earlier, I guess....

"Someone" did think of it, and realized that it wasn't acceptable.

I don't understand why so many people seem to be concerned about
packing both object AND function pointers into the same type of
variable.  (By the way, it CAN be done; use a union.)  What sort
of program calls for such weirdness, anyway?  The only genuine
"problem" I've seen mentioned is the inability to portably print
out a function pointer, something I don't think is all that useful.

henry@utzoo.uucp (Henry Spencer) (03/03/89)

In article <928@jhunix.HCF.JHU.EDU> ins_balb@jhunix.UUCP (Andy Matter) writes:
>#if sizeof (void *) > sizeof (void (*)())
>#define generic_ptr (void *)
>#else
>#define generic_ptr (void (*)())
>#endif
>Then use generic_ptr instead of void *...

Unfortunately, if I recall correctly (my Oct draft is out on loan), in
strict ANSI C it is illegal to convert function pointers into object
pointers and vice-versa.  Before you scream, consider that there are
architectures on which this is not a meaningful operation, and it is
almost always a highly machine-specific operation.  The ability to do
such conversions is mentioned under "common extensions" in the appendix.
-- 
The Earth is our mother;       |     Henry Spencer at U of Toronto Zoology
our nine months are up.        | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

ked@garnet.berkeley.edu (Earl H. Kinmonth) (03/03/89)

In article <9765@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <928@jhunix.HCF.JHU.EDU> ins_balb@jhunix.UUCP (Andy Matter) writes:

>I don't understand why so many people seem to be concerned about
>packing both object AND function pointers into the same type of
>variable.  (By the way, it CAN be done; use a union.)  What sort
>of program calls for such weirdness, anyway?  The only genuine

It doesn't take a particularly weird program to require this.  Almost any
program that interprets a script containing both function calls and
variables (sym table references) will require this.  The hoc calculator
program in Kernighan/Pike, UNIX PROGRAMMING ENVIRONMENT, requires this.
Any awk like program will require it.  Any script for a spread sheet
that uses functions and variable names will probably require it.  So too
will an editor that allows complex scripts.  Generally such applications
will generate a vector that contains a mix of calls to functions and
objects (pointers to elements in a symbol table).

scs@adam.pika.mit.edu (Steve Summit) (03/03/89)

In article <11915@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>Therefore, a completely generic pointer
>type (to function or data) is `union {void (*)(void); void *}', though I can't
>think of any practical use for such a thing.

In article <9765@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>I don't understand why so many people seem to be concerned about
>packing both object AND function pointers into the same type of
>variable.  (By the way, it CAN be done; use a union.)  What sort
>of program calls for such weirdness, anyway?  The only genuine
>"problem" I've seen mentioned is the inability to portably print
>out a function pointer, something I don't think is all that useful.

I will have to use a variation on this union in the symbol table
and the rvalue structure of a C interpreter I wrote once, if I
ever want it to pass ANSI muster.  (I'm not sure I'll bother.)

So far, the interpreter's symbol table has assumed that there is
one type which can contain any pointer, to code or data.  There
is also an "nm" ("namelist") command, which prints the symbol
table; this command has been using %p but will require revision
if %p will not portably print a function pointer.

The same difficulties would hold for any assemblers, linkers, or
other object file manipulation programs (nm, etc.) written in C
on a system with incompatible code and data pointers.  Those
programs don't have to be portable, and can use whatever system-
dependent kludges are required.  It's more troublesome for the
interpreter, most of which (outside of its dynamic linker) is
quite portable.

Admittedly, a C interpreter is a rather extreme, one-of-a-kind
program, that pushes the edges of the envelope in several ways.
(The dynamic linker reads object code into malloc'ed memory and
then jumps to it, a maneuver which some architectures reject
outright.  Others require a special call to create new text
segments, which the interpreter would have to use.)

                                            Steve Summit
                                            scs@adam.pika.mit.edu

P.S. The astute reader may wonder why an interpreter needs to
handle code pointers at all, since the "code" being interpreted
by an interpreter is really data.  However, an interpreter that
allows intermixing of interpreted and compiled code must keep
"code" pointers, whether to interpreble or compiled code, as true
code pointers, partly to keep function calls made from
interpreted code consistent, and more importantly to allow calls
from compiled code back to interpreted code to be made at all.
(When you link and relocate object code with undefined external
references, you've got to have a real function pointer to fill
in.  Arranging that calls to interpreted routines from compiled
code work is a challenging problem, and is left as an exercise
for the reader :-).  A correct solution allows installing
interpreted routines as signal handlers, a fascinating concept.)

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/04/89)

In article <9571@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>I will have to use a variation on this union in the symbol table
>and the rvalue structure of a C interpreter I wrote once, if I
>ever want it to pass ANSI muster.  (I'm not sure I'll bother.)

It's not a question of ANSI C; you have to do it right if your code
is going to be maximally portable, quite apart from ANSI C.  X3J11
did not invent the distinction between code and data addresses; they
simply acknowledged that there often is a distinction and that it
would be prohibitively expensive to have to disguise this distinction
in some common environments.

karl@haddock.ima.isc.com (Karl Heuer) (03/04/89)

In article <9571@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>In article <11915@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>>Therefore, a completely generic pointer type (to function or data) is
>>`union {void (*)(void); void *}', though I can't think of any practical
>>use for such a thing.
>
>[Symbol table, etc.]

I stand corrected.  I was thinking in terms of using the union as a type pun;
a tagged union (or any other protocol which guarantees that the member fetched
is the one most recently stored) is indeed useful.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

bill@twwells.uucp (T. William Wells) (03/04/89)

In article <9765@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
: >Also, how's this for a solution:
: >#if sizeof (void *) > sizeof (void (*)())
: >#define generic_ptr (void *)
: >#else
: >#define generic_ptr (void (*)())
: >#endif
: >Then use generic_ptr instead of void *, and any pointer type
: >(including pointers to functions) will fit (as long as you remember to
: >cast everything you assign).
:
: Won't work.  There is no guarantee that an object pointer can be
: converted to a function pointer or vice-versa.

Also, the sizeofs don't work in #ifs. Not according to the dpANS, nor
in many existing compilers.

---
Bill
{ uunet | novavax } !twwells!bill
(BTW, I'm going to be looking for a new job sometime in the next
few months.  If you know of a good one, do send me e-mail.)