[comp.lang.c] How big is a

doug@edge.UUCP (Doug Pardee) (05/12/87)

Well, I would have thought that we'd have beaten this (char *)!=(int *)
thing to death.

But then I ran into a problem of my own...

I want to do what is sometimes called "object-oriented" programming.
Each source module consists of a bunch of routines which operate on a
particular kind of object.  Each instance of an object is described in
detail by a struct which is malloc'ed by the object's "create" routine.

Just that one source module knows the description of that struct.  To
the "outside world", each object is known only by a handle, which is
typically (here it comes, folks) a pointer to that malloc'ed struct.
Specifically, nobody will "#include foo.h" to get the struct definition.

Unfortunately, as we've seen, that means that the outside world doesn't
even know how big the pointer is.  So, it seems that I can't use the
straightforward approach of having the handle be the pointer to the
structure.

Possibility #1:  We cavalierly declare that no pointers are longer than
char pointers:
     sizeof (char *)   >=   sizeof (anything *)
and create object handles by casting the pointers with (char *).

Possibility #2:  We cavalierly declare that all pointers to pointers are
the same length:
     sizeof (char **)   ==   sizeof (anything **)
and the object's "create" routine should   malloc(sizeof(struct foo *))
and return the pointer to the pointer as a (char **) handle.

Possibility #3:  We observe that malloc is declared as "char *", and
so we assume that all pointers returned by malloc are truly (char *),
even if we subsequently cast them to (struct foo *) and put a struct foo
into that memory.  #3a: we save the original (char *) pointer and return
that as the handle.  #3b: we cast the (struct foo *) pointer back into
a (char *) pointer and return that as the handle [this is the same
code as possibility #1, but with different reasoning].

I had considered a possibility #4, based on returning both the pointer
and "sizeof pointer", but at best that seems to degenerate into a more
complicated version of possibility #2.

I'm leaning toward #3a.  This would seem to be meaningful only when using
malloc, which I am.
-- 
Doug Pardee -- Edge Computer Corp., Scottsdale, AZ -- ...!ihnp4!mot!edge!doug

chris@mimsy.UUCP (Chris Torek) (05/15/87)

In article <737@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes:
>Just that one source module knows the description of that struct.  To
>the "outside world", each object is known only by a handle, which is
>typically (here it comes, folks) a pointer to that malloc'ed struct.
... 
>Unfortunately, as we've seen, that means that the outside world doesn't
>even know how big the pointer is.

This is the sort of problem the (void *) type in the dpANS is intended
to solve.  For now, I have been using (char *), or in some situations,
(caddr_t), which is really just an alias for (char *) after all.  If
you want to be maximally paranoid, use a header file:

	#ifdef dpANS_conformant_compiler_flag
	typedef void *handle;
	#else
	typedef char *handle;
	#endif

and mark this file `system dependent'.

(Incidentally, a `handle' usually refers to a pointer to a pointer,
the double indirection allowing space compaction of large objects,
or even allowing the faking of virtual memory on certain hardware
configurations.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	seismo!mimsy!chris

chips@usfvax2.UUCP (Chip Salzenberg) (05/22/87)

Missile-Launcher: Class 5


In article <737@edge.UUCP>, doug@edge.UUCP (Doug Pardee) writes:
> To the "outside world", each object is known only by a handle, which is
> typically (here it comes, folks) a pointer to that malloc'ed struct.
> [...] 
> Possibility #1:  We cavalierly declare that no pointers are longer than
> char pointers:

Does the word "assume" ring a bell?

> Possibility #2:  We cavalierly declare that all pointers to pointers are
> the same length:

Again, an unsafe assumption.  If, for example, "char *" is bigger than "int *",
then "char *" may have more stringent alignment requirements than "int *".
Therefore, "char **" could be smaller than "int **".  (Any resemblance to
real architectures, living or dead, is purely coincidental.)

> Possibility #3:  We observe that malloc is declared as "char *", and
> so we assume that all pointers returned by malloc are truly (char *),
> even if we subsequently cast them to (struct foo *) and put a struct foo
> into that memory.

Use "void *" instead.  Void pointers are guaranteed to hold any pointer value
without losing precision.  Cast the return value of malloc() to "void *" and 
use the resulting void pointer as a handle.

If you want your code to be portable to compilers lacking a "void" type, make
a typedef (typedef void *HANDLE) so that you can conveniently change your mind.



-- 
Chip Salzenberg		    Address: "{gatech,cbatt,akgua}!usfvax2!ateng!chip"
AT Engineering, Tampa, FL   Redress: "chips@usfvax2.UUCP"
"Use the Source, Luke!"	    My opinions do not necessarily agree with anything.

drw@cullvax.UUCP (Dale Worley) (05/27/87)

doug@edge.UUCP (Doug Pardee) writes:
> I want to do what is sometimes called "object-oriented" programming.
> Each source module consists of a bunch of routines which operate on a
> particular kind of object.  Each instance of an object is described in
> detail by a struct which is malloc'ed by the object's "create" routine.
> [...]
> Unfortunately, as we've seen, that means that the outside world doesn't
> even know how big the pointer is.  So, it seems that I can't use the
> straightforward approach of having the handle be the pointer to the
> structure.

My taste is that the cleanest method is to say:

typedef char *handle;

Now, cast everything to and from 'handle' -- by ANSI C, casting a
pointer to char * and back is guaranteed to not screw up.

Dale
-- 
Dale Worley		Cullinet Software
UUCP: ...!seismo!harvard!mit-eddie!cullvax!drw
ARPA: cullvax!drw@eddie.mit.edu
Un*x (a generic name for a class of OS's) != Unix (AT&T's brand of such)

gwyn@brl-smoke.UUCP (05/28/87)

In article <1217@cullvax.UUCP> drw@cullvax.UUCP (Dale Worley) writes:
>Now, cast everything to and from 'handle' -- by ANSI C, casting a
>pointer to char * and back is guaranteed to not screw up.

Use (void *) if you got `em.

By the way, some people would prefer to reserve the name "handle" for
a fixed pointer to a dynamic pointer.  This technique is used, for
example, in the Apple IIGS to permit substitution of RAM patches for ROM
routines, to support dynamic load segments, and other similar purposes.

mouse@mcgill-vision.UUCP (der Mouse) (06/03/87)

In article <737@edge.UUCP>, doug@edge.UUCP (Doug Pardee) writes:
> Well, I would have thought that we'd have beaten this
> (char *)!=(int *) thing to death.

I rather think we have....

> I want to do what is sometimes called "object-oriented" programming.

> [wants to return "handles" to internal structures]

> Possibility #1:  We cavalierly declare that no pointers are longer
> than char pointers:
>      sizeof (char *)   >=   sizeof (anything *)
> and create object handles by casting the pointers with (char *).

This should work, but not because of the reason you quoted.  K&R says,
and presumably ANSI follows them in saying, that a pointer to any
object can be cast to a pointer to any smaller object and back without
change.  Since a char is the smallest object available, any pointer can
be cast to (char *) and back without change.  Therefore, this scheme is
safe, regardless of the sizes of the pointers.  (Since more of a
smaller object can fit into memory, this would tend to imply the
inequality you quoted, but it does not require it.)

If ANSI does not support this argument, I would certainly hope they
have some analogous pointer type, which can be substituted for (char *)
to repair the above exposition.

					der Mouse

				(mouse@mcgill-vision.uucp)

am@cl.cam.ac.uk (Alan Mycroft) (06/07/87)

doug@edge.UUCP (Doug Pardee) writes:
>> I want to do what is sometimes called "object-oriented" programming.
>> Each source module consists of a bunch of routines which operate on a
>> particular kind of object.  Each instance of an object is described in
>> detail by a struct which is malloc'ed by the object's "create" routine.
>> [...]
This seems to be something that ANSI (inter alia) haven't quite thought
through concerning the possible size of pointers.

ANSI say (3.5.2.2 Oct 86) that "an incomplete structure or union specifier
... may only be used when the size of an object of the specified type
is not needed".  Now, this has been used (reasonably in many senses)
to argue that declaring an object as
    struct unknown *foo;
is illegal since the compiler does not know the size of the pointer
(since this may depend on the size of the object pointed to,
consider machines where sizeof(char *) != sizeof(int *).

However, this reading would also prohibit mutually recursive STRUCT's
at all:
    struct ho { struct hum *chain; ... }
    struct hum { struct ho *chain; ... }

Proposal: to allow this necessary feature, ANSI should declare that
all pointers *to structs or unions* are of the same size, which
is not necessarily the same sizeof(char *) or sizeof(int *).

jbuck@epimass.EPI.COM (Joe Buck) (06/09/87)

In article <790@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes:
>> Possibility #1:  We cavalierly declare that no pointers are longer
>> than char pointers:

>This should work, but not because of the reason you quoted.  K&R says,
>and presumably ANSI follows them in saying, that a pointer to any
>object can be cast to a pointer to any smaller object and back without
>change.  Since a char is the smallest object available, any pointer can
>be cast to (char *) and back without change.

Ah, but what about pointers to functions?  There are models for the
80x86 architecture (boy, I'm glad I don't have to program that thing)
where pointers to data are 16 bits and pointers to functions are 32
bits.  In cases like this, a char pointer isn't wide enough.

-- 
- Joe Buck    jbuck@epimass.EPI.COM  (in the brave new world of domains!)
		{seismo,ucbvax,sun,decwrl,<smart-site>}!epimass.epi.com!jbuck

stevesu@copper.UUCP (06/14/87)

In article <353@formtek.UUCP>, kls@formtek.UUCP (Karl Swartz) writes:
> > Ah, but what about pointers to functions?  There are models for the
> > 80x86 architecture where pointers to data are 16 bits and pointers
> > to functions are 32  bits.
> 
> dpANSI explicitly avoids talking about casts between pointers to
> functions and pointers to (not-a-function).  The Rationale does
> say something on this, in section 3.2.2.3:
> 
>     "Nothing is said about pointers to functions, which may be
>     incommensurate with object pointers and/or integers."

Why not?  I understand that a (char *) might not be big enough,
but couldn't a (void *) be made large enough to hold the segment
numbers and other assorted rot for those funky memory models?
Or would that make (void *)'s unacceptably big and arithmetic on
them unacceptably slow?

I'm curious because the most stratospherically enlightened
programming paradigms tend to foster a blurring or removal of the
traditional text/data distinction.  I've written a few programs
(not many, I'll admit, and pretty esoteric ones at that) that
intermixed pointers to functions and pointers to data (through a
char *) with good effect.  (If you can't imagine a program that
would want to treat functions as data, you've never tried writing
a C interpreter.)

                                           Steve Summit
                                           stevesu@copper.tek.com

meissner@dg_rtp.UUCP (Michael Meissner) (06/14/87)

In article <1243@epimass.EPI.COM> jbuck@epimass.EPI.COM (Joe Buck) writes:
>	/* sombody else from edge.UUCP */
> >This should work, but not because of the reason you quoted.  K&R says,
> >and presumably ANSI follows them in saying, that a pointer to any
> >object can be cast to a pointer to any smaller object and back without
> >change.  Since a char is the smallest object available, any pointer can
> >be cast to (char *) and back without change.
> 
> Ah, but what about pointers to functions?  There are models for the
> 80x86 architecture (boy, I'm glad I don't have to program that thing)
> where pointers to data are 16 bits and pointers to functions are 32
> bits.  In cases like this, a char pointer isn't wide enough.

ANSI has a void * which is guaranteed to be large enough to point to any
data object (pre-ANSI could use char *).  However, ANSI explicitly says
that in standard conforming programs, you cannot convert from a pointer
to a function to a pointer to a data object (and vica versa).  It is left
as a common extension to be able to do so.
-- 
	Michael Meissner, Data General	Uucp: ...mcnc!rti!dg_rtp!meissner

It is 11pm, do you know what your sendmail and uucico are doing?

karl@haddock.UUCP (Karl Heuer) (06/16/87)

In article <1121@copper.TEK.COM> stevesu@copper.TEK.COM (Steve Summit) writes:
>>     "Nothing is said about pointers to functions, which may be
>>     incommensurate with object pointers and/or integers."  [dpANS:Rat]
>
>Why not?

One problem is efficiency, as you noted.  Why should normal programs, which do
not mix text/data pointers, have to pay the extra penalty to support esoteric
programs that do?

Also, on some machines (recall that the standard must address the least common
denominator, to some extent) there is nothing useful you can do with a
function pointer after casting it to a data pointer.  If the most that can be
done (in a portable program) is store it and get it back later, you might as
well use a union.

(Note that on machines in which it *is* safe and efficient, function-casting
will probably be implemented as an extension; this is acknowledged in dpANS
A.6.5.7 [Oct86])

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