[comp.lang.c] NULL pointers as arguments

edw@ius2.cs.cmu.edu (Eddie Wyatt) (04/29/87)

   It seems to me that the problems of having foo(NULL) be correct on
whatever machine you are working on could be taken care of by 
having pointer arguements aways be type coerced into whatever the
largest pointer type is on the machine (in cases presented to
me that is char *).  This could be done as part of the language
definition just as the float parameter are typed coerced into
doubles.  This assumes there is some bound on pointer type sizes
though.  Any comments?

-- 
					Eddie Wyatt

rotondo@ernie.Berkeley.EDU (Scott Rotondo) (04/29/87)

In article <1130@ius2.cs.cmu.edu> edw@ius2.cs.cmu.edu (Eddie Wyatt) writes:
>    It seems to me that the problems of having foo(NULL) be correct on
> whatever machine you are working on could be taken care of by 
> having pointer arguements aways be type coerced into whatever the
> largest pointer type is on the machine (in cases presented to
> me that is char *).  This could be done as part of the language
> definition just as the float parameter are typed coerced into
> doubles.  This assumes there is some bound on pointer type sizes
> though.  Any comments?

It also assumes that this pointer argument can be recognized as such.
If NULL is (properly) defined as 0, it will look just like an integer
by the time the compiler sees it.  With a float this is not a problem
unless you write something like 0 when you mean 0.0.

By the way, it seems to me that there must be a bound on pointer type
sizes.  (One of them has to be the biggest.)

			-- Scott

zog@nbisos.UUCP (04/29/87)

In article <1130@ius2.cs.cmu.edu> edw@ius2.cs.cmu.edu (Eddie Wyatt) writes:
>
>   It seems to me that the problems of having foo(NULL) be correct on
>whatever machine you are working on could be taken care of by 
>having pointer arguements aways be type coerced into whatever the
>largest pointer type is on the machine (in cases presented to
>me that is char *).  This could be done as part of the language
>definition just as the float parameter are typed coerced into
>doubles.

This is NOT correct !

The fact that floats are passed as doubles is supported by the fact that
the compiler knows enough to coerce back when the called function picks
up the parameter.  It's still your responsibility to pass the correct types.

The main thrust here with pointers is that different types of pointers
can have COMPLETELY different representations.  Coercion to a common type
is not desireable.

All of these problems would go away if people would cast NULL to the
proper value.

"What about existing code which assumes .... ?"

Tough !  It's wrong !

We don't need to change the language definition to support junior programmers.
If you can't use a simple tool like lint, then step aside and let someone
who really cares do the task.  I'm so sick of all of the exceptions being
made to support and promote mediocrity !

Chris Herzog

chris@mimsy.UUCP (Chris Torek) (04/29/87)

In article <1130@ius2.cs.cmu.edu> edw@ius2.cs.cmu.edu (Eddie Wyatt) writes:
<   It seems to me that the problems of having foo(NULL) be correct on
<whatever machine you are working on could be taken care of by 
<having pointer arguements aways be type coerced into whatever the
<largest pointer type is on the machine (in cases presented to
<me that is char *).  This could be done as part of the language
<definition just as the float parameter are typed coerced into
<doubles.  This assumes there is some bound on pointer type sizes
<though.  Any comments?

That would work fine.  I understand that the DG MV series compiler
has a magic option that in essence does this.  I imagine it is an
option for the same reason that many people dislike the automatiac
float-to-double promotion. . . .
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP:	seismo!mimsy!chris	ARPA/CSNet:	chris@mimsy.umd.edu

hansen@pegasus.UUCP (04/29/87)

<  It seems to me that the problems of having foo(NULL) be correct on
< whatever machine you are working on could be taken care of by 
< having pointer arguements aways be type coerced into whatever the
< largest pointer type is on the machine (in cases presented to
< me that is char *).  This could be done as part of the language
< definition just as the float parameter are typed coerced into
< doubles.  This assumes there is some bound on pointer type sizes
< though.  Any comments?
<					Eddie Wyatt

Given that this were only done when no function prototype is in scope, this
simple suggestion would solve so many problems that it seems silly that it
wasn't suggested a LONG type ago. Treat the multitude of pointer types the
same way that the multitude of integer and floating types are treated. As
MOST functions will have prototypes defined, and most of the varargs
functions which cannot have prototypes defined usually don't pass more than
characte pointers around, there should be no efficiency problems with this
idea.

Given this idea, the only correct type for NULL would be "(void*)0", so that
it too could be correctly coerced, if necessary.  Would there be any
troubles casting pointers to functions? I can't think of anything else which
might cause troubles.

I don't have my dpANS copy handy, so I can't think of anything in there
which would preclude this concept.  It even seems to be ALLOWABLE under the
current dpANS definition.

Can anyone else find any flies in this idea?

					Tony Hansen
					ihnp4!pegasus!hansen

mason@gargoyle.UChicago.EDU (Tony Mason) (04/30/87)

>All of these problems would go away if people would cast NULL to the
>proper value.
>
>"What about existing code which assumes .... ?"
>
>Tough !  It's wrong !
>
>We don't need to change the language definition to support junior programmers.
>If you can't use a simple tool like lint, then step aside and let someone
>who really cares do the task.  I'm so sick of all of the exceptions being
>made to support and promote mediocrity !

It is NOT wrong.  K & R EXPLICITY state that NULL IS the correct manner in
which to represent this stuff. (K & R p. 97-98):

"We write NULL instead of zero, however, to indicate more clearly that this
is a special value for a pointer.  In general, integers cannot meaningfully
be assigned to pointers; zero is a special case."
                         ^^^^^^^^^^^^^^^^^^^^^^

It would be rather difficult to defend the position that code using THIS
assumption was written by "junior programmers" and that to conform to K & R
is to "support and promote mediocrity."  What we are arguing about are not
problems with using the special symbol '0' but rather scoping rules in C.
With the new ANSI standards, function prototyping should get rid of the
problem.  

Point is, we have to live with what C is now and has been in the past.  It
doesn't suffice to say "it's wrong."  K & R in the famous Appendix A sum it
up (p. 192):

"...it is guaranteed that assignment of the constant 0 to a pointer will
produce a null pointer distinguishable from a pointer to any object."

To say code using this fact is "wrong" isn't correct, OR productive.  Useful
suggestions (such as common coersion) may be acceptable FOR THE NULL POINTER
case.  From K & R, you could assume there was a pointer type "NULL" which was
distinguishable from other pointer types.  Thus, a NULL char * could be the
same as a NULL struct x *.  Coersion of NULL pointers only may be difficult,
but it would conform, and solve the problem.

Tony Mason
Univ. of Chicago, Dept. of C.S.
ARPA: mason@gargoyle.uchicago.edu
UUCP: ...ihnp4!gargoyle!mason

edw@ius2.cs.cmu.edu.UUCP (04/30/87)

In article <148@nbisos.NBI.COM>, zog@nbisos.NBI.COM (Chris Herzog) writes:

> 
> The fact that floats are passed as doubles is supported by the fact that
> the compiler knows enough to coerce back when the called function picks
> up the parameter.  It's still your responsibility to pass the correct types.
> 
> The main thrust here with pointers is that different types of pointers
> can have COMPLETELY different representations.  Coercion to a common type
> is not desireable.
> 
> All of these problems would go away if people would cast NULL to the
> proper value.
> 
> "What about existing code which assumes .... ?"
> 
> Tough !  It's wrong !
> 
> We don't need to change the language definition to support junior programmers.
> If you can't use a simple tool like lint, then step aside and let someone
> who really cares do the task.  I'm so sick of all of the exceptions being
> made to support and promote mediocrity !
> 
> Chris Herzog



FLAME ON

  Is it always your nature to try intimidating people??  I ask for 
comments not insults *ssh*l*.

FLAME OFF

  The only example given to me of where pointer sizes differ
amoung types is on word-address machines.  Character and
byte arrays are spaced optimized by packing many bytes
per word.  The problem is how to address individual bytes.
One word pointers just won't cut it.  An extra byte is needed
to address a given byte within the word (actually I would
imagine a whole word is used for the offset because of alignment
problems).

 Anyway, two words should be able to represent any pointer type
on a word-address machine, right?  Also the  type coercion of
lets say (float *) to  (char *) could be defined  as: the 
float pointer value with offset 0 (I'm assuming that all floats
are aligned on word boundries). NULL could be defined as
((char *) 0) or ((void *) 0).

  With reguards to your comments about floating point args,
the C compiler knows to type coerce doubles back to singles
only because you have declared the input parameter to be
of type float.  The same situation would holds for pointers types.
The compiler would know what to type coerce the "generic"
pointer parameter to by what it is declare to be in the function
parameter list.

  "Word" is being synonymously with the unit that represents the
size of a pointer to a "word" in this article.

-- 
					Eddie Wyatt

gwyn@brl-smoke.ARPA (Doug Gwyn ) (04/30/87)

In article <2975@pegasus.UUCP> hansen@pegasus.UUCP (60021254-Tony L. Hansen;LZ 3B-315;6243) writes:
>Can anyone else find any flies in this idea?

Yes, it would add unnecessary overhead in implementations that have to
use different sizes for different pointer types.

guy%gorodish@Sun.COM (Guy Harris) (05/01/87)

> It is NOT wrong.  K & R EXPLICITY state that NULL IS the correct manner in
> which to represent this stuff. (K & R p. 97-98):

Sorry, that's not in Appendix A; it's exegesis, not prescription.

> Point is, we have to live with what C is now and has been in the past.  It
> doesn't suffice to say "it's wrong."  K & R in the famous Appendix A sum it
> up (p. 192):
> 
> "...it is guaranteed that assignment of the constant 0 to a pointer will
> produce a null pointer distinguishable from a pointer to any object."
> 
> To say code using this fact is "wrong" isn't correct, OR productive.

Yes, it *is* correct, and it *is* productive.

It's correct because we're not discussing assignments here, we're
discussing argument passing.  Assignments perform type conversions of
various sorts.  Argument passing does not perform the same
conversions - see p. 186:

	   Any actual arguments of type 'float' are converted to
	'double' before the call; any of type 'char' or 'short' are
	converted to 'int'; and, as usual, array names are converted
	to pointers.  *No other conversions are performed
	automatically; in particular, the compiler does not compare
	the types of actual arguments with those of formal arguments.
	If conversion is needed, sue a cast; see \(sc 7.2, 8.7.*
	("italics" mine)

Note, BTW, that another conversion not performed is the "int" to
"long" conversion; it is no more correct to do

	setbuf(stdout, NULL);

than it is to do

	lseek(fd, 0, 0);

Both are incorrect.

It is productive because there *are* implementations of C - legal
ones - where you are required to cast your pointers.  Throwing in
some hack in one implementation to make things "work" without a cast
doesn't make incorrect code work on those other implementations.
Making the code correct by putting in the cast *does* make the code
work on correct implementations.

> Useful suggestions (such as common coersion) may be acceptable FOR THE
> NULL POINTER case. From K & R, you could assume there was a pointer type
> "NULL" which was distinguishable from other pointer types.  Thus, a NULL
> char * could be the same as a NULL struct x *.

You're not using "type" here in the sense of a type in C, which is
causing a problem.  A NULL "char *" could be the same as a NULL
"struct x *", but it *need* not be.  If you infer from K&R that there
is a pointer type, in the sense of a C language type, NULL, you would
be inferring something not implied by K&R.  Consider:

	7.13 Conditional operator

	   ...otherwise, one must be a pointer and the other the
	constant 0, and the result has the type of the pointer.

This means that if "cp" is of type "char *", then the expression

	0 ? cp : 0

has "the type of the pointer", namely "char *", while if "sxp" is of
type "struct x *", the expression

	0 ? sxp : 0

has the type "struct x *".  Both expressions yield null pointers, and
both expressions have different types.

> Coersion of NULL pointers only may be difficult, but it would conform,
> and solve the problem.

Coercion of NULL pointers only may or may not be difficult.  However,
that's irrelevant, because it would break a fair bit of code and
would 1) *not* conform to any reasonable specification of C and 2)
would cause far more problems than it would solve.  How is "f":

	void
	f(p)
		int *p;
	{
		...
	}

supposed to handle calls of the form "f(NULL)" (which would be
treated as "f((char *)NULL)" under this proposal) and of the form
"f(&x)", unless the "&x" is also coerced to type "char *"?

This coercion would be unpleasant on a machine where coercing "int *" to
"char *" is not a zero-cost operation, unless (as Tony Hansen pointed
out) you were in an ANSI C environment and could use function
prototypes to tell the compiler not to perform this coercion in most
cases (but you stated that the problem is, in fact, that we don't
have an ANSI C standard yet), or (as Chris Torek pointed out) it were
a compiler option (which means you'd have to put the casts in anyway
unless you wanted to pay the performance penalty of doing the
coercion - note also that this would require two versions of your C
library, one compiled with this option, and one without).

jsdy@hadron.UUCP (05/14/87)

I haven't been able to trace down all the (branching) followups on
this; but what I have seen implies that there is still a lot of
disagreement.  Let me see if I can elucidate, with some facts and
concepts that have not (far as I can see) yet been brought together.

In article <1130@ius2.cs.cmu.edu> edw@ius2.cs.cmu.edu (Eddie Wyatt) writes:
>   It seems to me that the problems of having foo(NULL) be correct on
>whatever machine you are working on could be taken care of by 
>having pointer arguements aways be type coerced into whatever the
>largest pointer type is on the machine (in cases presented to
>me that is char *).  This could be done as part of the language
>definition just as the float parameter are typed coerced into
>doubles.  This assumes there is some bound on pointer type sizes
>though.  Any comments?

Well, this is kind of what a (void *) declaration is for.  An ob-
ject of type (void *) can be coerced into any pointer type and back,
which rather implies that its actual bit size is at least as large
as any other pointer but does not mean that it actually is one of
the other pointer types.  Specifically, a ((void *) 0) can be co-
erced into a NULL pointer of any type.  This rather argues that
there is a bound on pointer sizes, although I can think of some
(not necessarily pathological) architectures for which this might
not be the case.

But!  A NULL pointer does not necessarily have a 0 representation
in every type, or even necessarily the same representation in any
two types!  If I declare foo(int *arg) in one module, but try to
pass foo(void *) or foo(char *) in another, I may be totally scrod
[fishy] if the data types have sufficiently different representa-
tions and there is no function prototype in the second module to
cause coercion (which, from the discussion, I assume is the case).
One could coerce all pointer arguments to whatever the (void *)
representation is; but that could be an expensive operation.  And
if NULL continues to be defined as 0 (which it will continue to do
on some slow-to-change compilers for a while), we still have the
problem of even determining that this is a pointer constant at all.

	Joe Yao		jsdy@hadron.COM (not yet domainised)
	hadron!jsdy@{seismo.CSS.GOV,dtix.ARPA,decuac.DEC.COM}
{arinc,att,avatar,cos,decuac,dtix,ecogong,kcwc}!hadron!jsdy
     {netex,netxcom,rlgvax,seismo,smsdpg,sundc}!hadron!jsdy