[net.lang.c] void *

kwh@bentley.UUCP (KW Heuer) (04/22/86)

In article <1883@ucsfcgl.UUCP> ucsfcgl!arnold (Ken Arnold%CGL) writes:
>Purists will note that I really should say
>	ptr = (some_type *) malloc(10 * sizeof *ptr);
>But eventually malloc() will be of type "void *".

Okay, enlighten me.  When I first saw the idea of "void *" I thought it was
to be a name for the most restrictive pointer type ("ALIGN *" for those who
know the malloc source).  Then when I rechecked, I found that it was a new
name for the least restrictive pointer type, "char *".  Now you seem to say
that the cast operator is unnecessary in your example.  I'd like some more
detail about this beast.  Specifically, suppose we have the following:
	void  *v;
	char  *c;
	short *h;
	ALIGN *a;
Of the 12 nontrivial assignment statements, which ones will ANSI C accept
without a cast?  What will lint accept (with/without cast)?

Currently lint warns about things like h=c because it doesn't know whether c
is aligned properly for use as a short pointer.  Thus every nontrivial use of
malloc, even with the appropriate cast, generates a lint warning.  This is
why I thought that "void *" was going to mean "ALIGN *", which would be the
appropriate return value for malloc().  (Also more efficient, on word-based
machines.)

Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

gwyn@BRL.ARPA (VLD/VMB) (04/22/86)

You're mixing two features of malloc().  Malloc() should return (void *),
which is a generic pointer type to/from which all other pointer types
may be cast without loss of information.  (This is the only use of
(void *), and it is the only universal pointer type; other pointer
casts can be non-portable.)  Malloc() ALSO guarantees that the pointer
it returns is suitably aligned for safe casting to ANY pointer type.
This is a property of malloc(), not of (void *).

kwh@bentley.UUCP (KW Heuer) (04/24/86)

In article <200@brl-smoke.ARPA> gwyn@BRL.ARPA (VLD/VMB) writes:
>You're mixing two features of malloc().  Malloc() should return (void *),
>which is a generic pointer type to/from which all other pointer types
>may be cast without loss of information.  (This is the only use of
>(void *), and it is the only universal pointer type; other pointer
>casts can be non-portable.)  Malloc() ALSO guarantees that the pointer
>it returns is suitably aligned for safe casting to ANY pointer type.
>This is a property of malloc(), not of (void *).

I realize that; it's just that I was expecting the new datatype to
correspond to this property of malloc(), so malloc() wouldn't have to
be a special case.  I probably shouldn't have brought up this (ALIGN *)
issue in the same posting.

It's *not* true that (void *) can always be cast into another pointer
type without loss of information.  For example, "intp = voidp;" may
drop bits if "voidp" was initialized to an improperly aligned object.
What's true is that any pointer may be assigned *to* a void pointer,
and that *same* value may be safely copied back.  (Currently this is a
guaranteed feature of (char *).)

To rephrase my original question, what does this have to do with the
explicit use of the cast operator?  Is "intp = voidp;" going to be as
acceptable as the (more correct, to me) syntax "intp = (int *)voidp;"?

Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

arnold@ucsfcgl.UUCP (Ken Arnold%CGL) (04/24/86)

In article <728@bentley.UUCP> kwh@bentley.UUCP (KW Heuer) writes:
>In article <1883@ucsfcgl.UUCP> ucsfcgl!arnold (Ken Arnold%CGL) writes:
>>Purists will note that I really should say
>>	ptr = (some_type *) malloc(10 * sizeof *ptr);
>>But eventually malloc() will be of type "void *".
>
>Okay, enlighten me.  When I first saw the idea of "void *" I thought it was
>to be a name for the most restrictive pointer type ("ALIGN *" for those who
>know the malloc source).  Then when I rechecked, I found that it was a new
>name for the least restrictive pointer type, "char *".  Now you seem to say
>that the cast operator is unnecessary in your example.  I'd like some more
>detail about this beast.  Specifically, suppose we have the following:
>	void  *v;
>	char  *c;
>	short *h;
>	ALIGN *a;
>Of the 12 nontrivial assignment statements, which ones will ANSI C accept
>without a cast?  What will lint accept (with/without cast)?

Well, as *I* understand it (and my draft is, of course, out of date),
(void *) can be cast to any other pointer type without complaint.
So

	ptr = malloc( ... );

would be as legal as

	int_var = float_var;

i.e., the conversion would be automatic and silent (because it is
guaranteed to work properly).  Presumably, any other pointer assignment
would generate a warning unless accompained by a cast (and I sure hope
it is silent if I explicitly cast it).

		Ken Arnold

mc68020@gilbbs (04/26/86)

In article <200@brl-smoke.ARPA>, gwyn@BRL.ARPA (VLD/VMB) writes:
> You're mixing two features of malloc().  Malloc() should return (void *),
> which is a generic pointer type to/from which all other pointer types
> may be cast without loss of information.  (This is the only use of
> (void *), and it is the only universal pointer type; other pointer
> casts can be non-portable.)  Malloc() ALSO guarantees that the pointer
> it returns is suitably aligned for safe casting to ANY pointer type.
> This is a property of malloc(), not of (void *).

   Well, that's just great!  The C compiler on my system doesn't understand
such niceties as void.  So what do we do?  (do *NOT* tell me to get another
compiler, there *IS* no other compiler available for this system!)

-- 
Disclaimer:  I hereby disclaim any and all responsibility for disclaimers.

tom keller
{ihnp4, dual}!ptsfa!gilbbs!mc68020

(* we may not be big, but we're small! *)

ARPA@brl-smoke (04/28/86)

If your C compiler doesn't understand "void" (which has been in
the language for years; X3J11 did not invent it, just (void *)),
or lacks bit fields, enums, struct assignment, __LINE__, decent
library routines, etc., then I would think you would want to get
it fixed.  Even the compiler on my Apple //e understands most of
these (as well as (void *))!  Didn't you pay for vendor support?

I have a header file I #include in my applications, that is set
up on different systems to compensate for such things and to
provide a uniform set of extensions such as boolean data type.
Here are some relevant excerpts for a relatively puny system:

typedef char	*pointer;		/* generic pointer (void *) */
#define	const		/* nothing */	/* (undefine for ANSI C) */
#define	signed		/* nothing */	/* (undefine for ANSI C) */
#define	volatile	/* nothing */	/* (undefine for ANSI C) */
#define	strchr	index			/* 7th Edition UNIX, 4.2BSD */
#define	strrchr	rindex			/* 7th Edition UNIX, 4.2BSD */
#define	void	int			/* K&R Appendix A followers */

kwh@bentley (04/29/86)

In article <194@gilbbs.UUCP> gilbbs!mc68020 writes:
>   Well, that's just great!  The C compiler on my system doesn't understand
>such niceties as void.  So what do we do?  (do *NOT* tell me to get another
>compiler, there *IS* no other compiler available for this system!)

What I've done in the past is "typedef int void;" or "cc -Dvoid=int" when
trying to port new code to old compilers.  Now suddenly void takes on a
new meaning.  Hmm, I'd say your best bet (other than getting a compiler
that understands at least the current standard) is to process any such
sources with a sed script (or something smarter) to change "void *" to
"char *" and other "void" to "int".

Actually, I don't think "void *" will start showing up in programs for
a while after it becomes "standard".  Maybe someone will upgrade your
compiler in the meantime?

Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

jbs@mit-eddie (04/30/86)

In article <194@gilbbs.UUCP> mc68020@gilbbs.UUCP (Tom Keller) writes:
>[...]
>   Well, that's just great!  The C compiler on my system doesn't understand
>such niceties as void.  So what do we do?  (do *NOT* tell me to get another
>compiler, there *IS* no other compiler available for this system!)

Get another system, of course! :-) :-) :-)

Jeff Siegal

rbj@icst-cmr (Root Boy Jim) (05/03/86)

> Actually, I don't think "void *" will start showing up in programs for
> a while after it becomes "standard".  Maybe someone will upgrade your
> compiler in the meantime?
> 
> Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

Don't bet on it. There are a *lot* of people just dying for ANSI C to
hit the streets so they can sneer at people who don't have one :-)
Hey, with computers you gotta take whatever status symbols you can get.

	(Root Boy) Jim Cottrell		<rbj@cmr>
	"One man gathers what another man spills"

                                      ~
P.S. I am in favor of ANSI C. Si si senor! See?

guy@sun.UUCP (05/20/86)

> So, although not necessarily synonyms, they ("char *" and "void *" - gh)
> do have the same properties, except that "char *" conversions usually
> require a cast to be acceptable.

Well,

	1) they don't have the same properties - if you dereference a
	   "char *", you get a "char", while if you dereference a "void *",
	   you get a "void", which means you can't dereference a "void *"
	   without casting it to a pointer to some meaningful type (this,
	   in answer to your question below, is presumably why "void *"
	   was chosen)

	2) the fact that "char *" conversions require a cast is NOT a
	   characteristic of the language, but a characteristic of the
	   compiler/"lint".  A C compiler which never generated any
	   warnings about pointer conversions would not be in violation
	   of the language specification.

	   As such, a C compiler could, if its author wanted it to,
	   generate warning messages about "illegal pointer combination"
	   and about "possible pointer alignment problem" for implicit
	   conversions (i.e., conversions not involving casts) between
	   "char *" and other pointers, and not generate them for
	   implicit conversions between "void *" and other pointers.
	   It could generate both warning messages for all implicit pointer
	   conversions, and generate them for all explicit pointer
	   conversions except those involving "void *".

	   A compiler which did so would not assign "char *" and "void *"
	   the same properties, since replacing objects of type "void *"
	   with objects of type "char *" in some expressions would cause
	   warnings to be issued.

> What I had in mind was that "ALIGN *" would be a synonym for "int *" or
> whatever happens to be the most restrictive pointer type.  The "guarantee"
> would be as good as the "guarantee" on an "int *".

I.e., not very good.  Again, you would have to trust "malloc" to know what
it was doing, just as you would have to do if it returned "void *".
Somewhere inside the guts of "malloc" there would probably be an conversion
of a "char *" to a "void *" or an "ALIGN *", and you'd have to hope that the
algorithm used by "malloc" guaranteed that the pointer being converted were
aligned properly.

> Well, what *is* the justification for adding "void *" to the language?  To
> allow people to shove pointers around without casts?

No.  One could allow people to shove pointers around with casts simply by
removing some warning messages from some current C compilers.

> To make lint shut up about malloc()?

In a sense, yes.  It also makes it possible to use "opaque pointers" without
getting complaints from the compiler.  We currently run our kernel through
"lint" before it goes out the door; the "lint" rules in the 4.2BSD kernel
"makefile" run the "lint" output through "egrep" to eliminate "possible
pointer alignment problem" messages, since there are a number of cases where
there are no such problems but we have no way to tell "lint" that.  If
"caddr_t" were "void *" instead of "char *", we wouldn't have to strip out
all "possible pointer alignment problem" messages; any that appeared would
indicate that there was a real problem in the code.

> Or just to distinguish "generic pointers" from pointers that can be
> dereferenced?  (Could be done with a typedef.  So could "void", but
> they put it in anyway.)

You can do "void" and "caddr_t" (or whatever you want to call a "generic
pointer") with "typedefs", but you can't make the compiler recognize their
unique properties that way.  If you do "typedef int void;", the compiler
will not recognize that casting something to "void" throws its argument
away, and certainly won't recognize that "void foo();" declares a function
which doesn't return a value.  If you declared "caddr_t foop;", the compiler
would let you dereference "foop" in an expression.

As such, I presume by "distinguish" you mean "distinguish *for the reader of
code*", not "distinguish for the compiler".  That is one thing you can use
"void *" to do, but it's not the only reason it was proposed.

> I do not strongly object to the addition of "void *", but I am worried about
> it "hiding" errors.  (I'm one of those purists who thinks that programmers
> *should* use casts to change types.)

Yes, if some compiler implementer misses the obvious, it could be used to
hide errors.  (Unfortunately, I've seen a lot of cases where implementers
miss the obvious.)  If one assumes that the current PCC/"lint" rules for
complaining about pointer conversions are correct, the correct extension of
those rules to "void *" is:

	implicit conversions to and from "void *" should generate
	"illegal pointer combination" warnings (although they need not
	give "possible pointer alignment problem" warnings)

	explicit conversions (i.e., conversions with casts) should not
	give any "possible pointer alignment problem" warnings; if you
	specify the "-c" flag to older "lint"s, or the "-p" flag to
	newer ones (both of which turn on the "complain about all pointer
	casts" flag - older versions give "illegal pointer combination"
	warnings, and newer ones give "pointer casts may be troublesome"),
	it should probably continue to give warnings.

This means that if you're going to toss pointers around casually, the
compiler is at least going to warn you about it; you have to say "I know
what I'm doing, shut up" by adding casts if you don't want errors.  In fact,
I would suggest that all conversions between pointers other than "void *"
should get "illegal pointer combination" (or, perhaps, "questionable pointer
combination", at least in some cases, since the C standard does permit
certain combinations) warnings, even if casts are used.

> Also, it's a misnomer; it has nothing to do with "void".

I presume the reason why "void *" was chosen is, as stated above, that a
pointer of type "void *" would, when dereferenced, yield an object of type
"void", and as such dereferencing a "void *" would be illegal, just as using
a function of type "void" as if it returned a value would be.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.arpa

henry@utzoo.UUCP (Henry Spencer) (05/23/86)

> > Also, it's a misnomer; it has nothing to do with "void".
> 
> I presume the reason why "void *" was chosen is, as stated above, that a
> pointer of type "void *" would, when dereferenced, yield an object of type
> "void", and as such dereferencing a "void *" would be illegal...

I believe a contributing reason was the desire to avoid introducing new
keywords.  (Yes, I know, X3J11 has managed to introduce a few; it remains
true that there is a preference for avoiding it.)  Every new keyword is
bound to break some existing programs.
-- 
Join STRAW: the Society To	Henry Spencer @ U of Toronto Zoology
Revile Ada Wholeheartedly	{allegra,ihnp4,decvax,pyramid}!utzoo!henry

bs@alice.UucP (Bjarne Stroustrup) (08/12/86)

> What are the (proposed and current) legal operations for "void *" besides
> assignment, casting, and passing as a parameter?
> 
> -- Tom Stockfisch, UCSD Chemistry

In C++, you can also use the comparison operators ==, !=, <, <=, >, >=, and the
negation operator !.
Pointer arithmetic is specifically disallowed (since you by definition do not
know the size of the object pointed to).
Conversion from any pointer type to void* is defined, but there is no conversion
the other way:

	char* pc = &some_char;	// OK if some_char is a char
	void* pv = &something;	// OK
	pv = pc;		// OK; standard conversion
	pc = pv;		// not OK: no standard conversion
	pc = (char*) pv;	// OK
	if (pc == pv) ...	
	if (pv == 0) ...
	if (!pv) ...
	if (pv < pc) ...

In addition, the draft ANSI C proposal allows uncasted assignment of a void*
to a non-void pointer. This, I consider to be an unnecessary weakening of C's
type system; in the long run it will become a rather serious nuiscance.
C++ does not allow this and I hope that ANSI C will not allow it either.

The reason for the proposal's rule is simple. Malloc() returns a void* and
if a void* can be assigned to any pointer without a cast you can write

	double* pc = malloc(100*sizeof(double));
	char* pc = malloc(100);

rather than

	double* pc = (double*) malloc(100*sizeof(double));
	char* pc = (char*) malloc(100);

I don't consider this minor notational convenience a sufficient reason to
create a new hole in the type system. A better solution, which unfortunately
does not appear to be open to the ANSI C committee, is to have a free store
allocation operator, such as C++'s new:

	double* pc = new double[100];
	char* pc = new char[100];

An implicit coersion to void* is OK, but an implicit coersion
from void* to another pointer type is not. The reason is the former,
simply throws information and away whereas the latter implicitly ``adds''
information. Allowing the latter (as the ANSI C proposal does and C++ does not)
would provide a new way of converting types without using casts:

	void* p;
	double* pd;
	char* pc;

	pc = pd;	// illegal
	p = pd; pc = p;	// legal under the current ANSI C proposal

karl@haddock (08/16/86)

alice!bs (Bjarne Stroustrup) writes:
>In addition, the draft ANSI C proposal allows uncasted assignment of a void*
>to a non-void pointer. This, I consider to be an unnecessary weakening of C's
>type system; in the long run it will become a rather serious nuiscance.

I agree that this is a bad idea; I'm glad to hear that C++ disallows it.  If
you don't want to cast the result of malloc(), it's easy to hide it in macros
(I've been doing it for years):
	#define new(type)       ((type *)malloc(sizeof(type)))
	#define nnew(n, type)   ((type *)malloc((n)*sizeof(type)))
This does restrict "type" to something you can add "*" to, though.

Having said that, I'll now point out that X3J11 draft 01-May-1986 does say in
5.5 (Common Warnings), "[An implementation may generate a warning if] an
implicit narrowing conversion is encountered, such as the assignment of ...
a pointer to void to a pointer to any type of object other than char."  This
implies that such an uncasted assignment should be avoided, at least.

Karl W. Z. Heuer (ihnp4!ima!haddock!karl), The Walking Lint

mike@peregrine.UUCP (Mike Wexler) (08/18/86)

I agree that void* should not be automatically coerced to another pointer type.
I like the way the new operator is implemented in C++, but do not think it
would work as well in C.  In C++ operators can be redefined on a type by
type basis and it is convenient to have this operator called at certain
points.  In C, operators are not redefinable.  Therefor memory allocation
becomes a feature of the language and not a library function.  I do not think
this is a good idea.  One of the main advantages of C is that there is
a small kernal of the language that is all that is necessary to create a 
compiler.  The rest of the features are implemented in a library written in
C and replaceable by users that don't have the source code to the compiler.
-- 
Mike Wexler
Email address:(trwrb|scgvaxd)!felix!peregrine!mike
Tel Co. address: (714)855-3923
;-) Internet address: ucivax@ucbvax.BERKELY.EDU!ucivax%felix!mike@peregrine :-(

martin@minster.UUCP (martin) (08/20/86)

In article <86900014@haddock> karl@haddock writes:
>alice!bs (Bjarne Stroustrup) writes:
>>In addition, the draft ANSI C proposal allows uncasted assignment of a void*
>>to a non-void pointer. This, I consider to be an unnecessary weakening of C's
>>type system; in the long run it will become a rather serious nuiscance.
>
>I agree that this is a bad idea; I'm glad to hear that C++ disallows it. ...
>...
>Having said that, I'll now point out that X3J11 draft 01-May-1986 does say in
>5.5 (Common Warnings), "[An implementation may generate a warning if] an
>implicit narrowing conversion is encountered, such as the assignment of ...

I don't regard the generation of a warning as being in any way satasfactory!
You only have to look at the number of warnings that various distributed
programs generate, to realise that many people simply ignore warnings.
This is an obvious portability problem, so lets (please!) have a fatal error!