[comp.lang.c] pointers, tests, casts

ericb@libdev (Eric Bivona) (11/23/88)

I have a question about tests on pointers, w.r.t. the ANSI standard
for C.

I know about casting NULL or 0 to get an appropriate nil pointer to
test against:

	char *ptr;
	void *malloc();

	ptr = (char *)malloc(256);	/* or (size_t)256?  ;-) */
	if (ptr == (char *)0) {		/* or (char *)NULL */
		perror("malloc");
		exit(1);
	}

In assignments, a 0 or NULL is cast implicitly to the correct pointer
type (I think, please correct me if I'm wrong).  What about the '=='
comparison above?  Would "(ptr == 0)" get evaluated correctly?  Or to
push it a bit further, what about the VAX-ish standard (like
*(char *)0 = 0 ;-) "(!ptr)"?  I realize that it is probably better to
explicitly cast & compare, but it did kind of make sense to say "if
(!ptr)..."

Thanks in advance,

-Eric Bivona
 DCIS Project, Dartmouth College

chris@mimsy.UUCP (Chris Torek) (11/23/88)

In article <11130@dartvax.Dartmouth.EDU> ericb@libdev (Eric Bivona) writes:
>I know about casting NULL or 0 to get an appropriate nil pointer ...

Note that a cast is always sufficient, if not always necessary.  (I
find that much `extra' casting hampers readability, though.)

[edited]
>	if (ptr == (char *)0) {		/* or (char *)NULL */

>In assignments, a 0 or NULL is cast implicitly to the correct pointer
>type ....

Right.

>What about the '==' comparison above?  Would "(ptr == 0)" get evaluated
>correctly?

It would; the implicit cast occurs here, too.

>... what about ... "(!ptr)"?

That too is correct, by the rule that `!x' means `x == 0'.  Stylistically,
though, I prefer `if (ptr == NULL)'.

In fact, the only place that `0' does not implicitly turn into the right
kind of `nil' is in function calls (and, under dpANS rules, only those
where prototypes are not present or insufficient to convey the necessary
type information).
-- 
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 ) (11/23/88)

In article <11130@dartvax.Dartmouth.EDU> Eric.J.Bivona@Dartmouth.EDU writes:
>I have a question about tests on pointers, ...

	if ( !ptr )
and
	if ( ptr == 0 )
are both perfectly valid ways to test for a null pointer.  You can
explicitly cast the 0 to the proper type, but it's not necessary.

Explicit casting of 0 (or NULL, defined in several headers) is required
only when the compiler could mistake the 0 for an int constant rather
than the intended null pointer constant.  The main place this can occur
is in the arguments to a function when no prototype is in scope, e.g.
	execl( "/bin/sh", "-sh", "-i", 0 );
(This one is even worse since it's a variable-argument function, so not
even a prototype would help.)  The bug typified by this is seen in a
LOT of code ("well it worked on MY machine!").

guy@auspex.UUCP (Guy Harris) (11/24/88)

>In assignments, a 0 or NULL is cast implicitly to the correct pointer
>type (I think, please correct me if I'm wrong).

This is correct.

>What about the '==' comparison above?  Would "(ptr == 0)" get evaluated
>correctly?

In any valid C implementation, it will be.  The compiler certainly knows
enough to do the implicit conversion, just as it does in the case of an
assignment.

>Or to push it a bit further, what about the VAX-ish standard (like
>*(char *)0 = 0 ;-) "(!ptr)"?  I realize that it is probably better to
>explicitly cast & compare, but it did kind of make sense to say "if
>(!ptr)..."

This will also work in any valid C implementation.

dhesi@bsu-cs.UUCP (Rahul Dhesi) (11/25/88)

In article <8961@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>)
writes:
>	execl( "/bin/sh", "-sh", "-i", 0 );
>...The bug typified by this is seen in a
>LOT of code ("well it worked on MY machine!").

Sadly, the only UNIX programming textbook I've seen on the market, by
Rochkind, consistently uses 0 and NULL as actual parameters without any
cast.  This may be because Rochkind did all his programming using Venix
on an IBM XT, and the Venix compiler does not support any memory model
in which ints and char pointers are of different sizes.

There is probably an entire generation of future programmers ready to
graduate and begin planting execl(...., 0) timebombs in production
code.
-- 
Rahul Dhesi         UUCP:  <backbones>!{iuvax,pur-ee}!bsu-cs!dhesi

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (11/29/88)

In article <8961@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
| In article <11130@dartvax.Dartmouth.EDU> Eric.J.Bivona@Dartmouth.EDU writes:
| >I have a question about tests on pointers, ...
| 
| 	if ( !ptr )
| and
| 	if ( ptr == 0 )
| are both perfectly valid ways to test for a null pointer.  You can
| explicitly cast the 0 to the proper type, but it's not necessary.

Doug, as usual you are correct, but I have to point out that
	if (ptr == NULL)
also works, usually generates the same code, and gives a much better
idea of what the code is doing. I'm sure that some of the new readers of
this group would not quickly grasp the meaning of your first example,
and I'm not sure about the second. I just covered this topic in a C
course I'm teaching, and I am always amazed at how easily new C
programmers are confused by shorthand form which "mean the same thing."
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

bjm@sabin.UUCP (Brendan J. McMahon) (11/30/88)

In article <4860@bsu-cs.UUCP> dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
>Sadly, the only UNIX programming textbook I've seen on the market, by
>Rochkind, consistently uses 0 and NULL as actual parameters without any
>cast.  This may be because Rochkind did all his programming using Venix

Well,  I have learned a great deal from that book.  But when verifying
your claim, I noticed the use of gets() in his getargs function for his 
shell!  Thank God for USENET gurus to set me straight (or at least getting
me so confused that I am forced to verify everything that I read anywhere.)


-- 
Brendan J. McMahon
Sabin Metal Corp. |     Refiners of Precious Metals    | Hardware Trouble?
Scottsville, NY   |  ****** Au  Ag  Pt  Pd  Rh ******  | Give us a call, we'll
716-538-2194      |lazlo!sabin!bjm  || ritcsh!sabin!bjm| melt your trouble away

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/01/88)

In article <12690@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
-In article <8961@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
-| In article <11130@dartvax.Dartmouth.EDU> Eric.J.Bivona@Dartmouth.EDU writes:
-| >I have a question about tests on pointers, ...
-| 	if ( !ptr )
-| and
-| 	if ( ptr == 0 )
-| are both perfectly valid ways to test for a null pointer.  You can
-| explicitly cast the 0 to the proper type, but it's not necessary.
-Doug, as usual you are correct, but I have to point out that
-	if (ptr == NULL)
-also works, usually generates the same code, and gives a much better
-idea of what the code is doing. I'm sure that some of the new readers of
-this group would not quickly grasp the meaning of your first example,
-and I'm not sure about the second. I just covered this topic in a C
-course I'm teaching, and I am always amazed at how easily new C
-programmers are confused by shorthand form which "mean the same thing."

I wasn't recommending anything other than if(ptr==NULL) (which requires
that the programmer arrange for NULL to be properly defined first, but
I always do arrange that for my code).  In fact I wasn't making any
stylistic suggestion at all, just answering a specific question.

danw@tekchips.CRL.TEK.COM (Daniel E. Wilson) (12/01/88)

In article <494@auspex.UUCP>, guy@auspex.UUCP (Guy Harris) writes:
> >In assignments, a 0 or NULL is cast implicitly to the correct pointer
> >type (I think, please correct me if I'm wrong).
> 
> This is correct.
> 
> >What about the '==' comparison above?  Would "(ptr == 0)" get evaluated
> >correctly?

  I tend to avoid the whole problem by defining a simple macro.  Simply
by using this macro always I get a NULL pointer of the needed type.
This does avoid bugs.

     #define NIL(type) ((type *) NULL)

  Unless someone would like to give me warnings about the abuses
of macros. 8-)

bill@twwells.uucp (T. William Wells) (12/01/88)

In article <12690@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
: In article <8961@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
: | In article <11130@dartvax.Dartmouth.EDU> Eric.J.Bivona@Dartmouth.EDU writes:
: | >I have a question about tests on pointers, ...
: |
: |     if ( !ptr )
: | and
: |     if ( ptr == 0 )
: | are both perfectly valid ways to test for a null pointer.  You can
: | explicitly cast the 0 to the proper type, but it's not necessary.
:
: Doug, as usual you are correct, but I have to point out that
:       if (ptr == NULL)
: also works, usually generates the same code, and gives a much better
: idea of what the code is doing. I'm sure that some of the new readers of
: this group would not quickly grasp the meaning of your first example,
: and I'm not sure about the second. I just covered this topic in a C
: course I'm teaching, and I am always amazed at how easily new C
: programmers are confused by shorthand form which "mean the same thing."

I'm afraid that you've just contributed to the confusion.  In the
material you quoted, the type of `ptr' is not specified. That being
the case,

	if (ptr == 0)

is not equivalent to

	if (ptr == NULL)

They are only equivalent if the type of ptr is `void *' or `char *'.
Otherwise, there are are implementations, those defining NULL as
(char *)0, which will give an error on the latter statement.

To set the record straight, these are all equivalent:

	if (!ptr)
	if (ptr == 0)
	if (ptr == (the_type_of_ptr)0)
	if (ptr == (the_type_of_ptr)NULL)

If ptr is of type `void *' or `char *' then the following is also
equivalent:

	if (ptr == NULL)

---
Bill
{uunet|novavax}!proxftl!twwells!bill

shirono@hcx3.SSD.HARRIS.COM (12/01/88)

In comp.lang.c, bill@twwells.uucp writes:
> In article <12690@steinmetz.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
> : In article <8961@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
> : | In article <11130@dartvax.Dartmouth.EDU> Eric.J.Bivona@Dartmouth.EDU writes:
> : | >I have a question about tests on pointers, ...
> : |
> : |     if ( !ptr )
> : | and
> : |     if ( ptr == 0 )
> : | are both perfectly valid ways to test for a null pointer.  You can
> : | explicitly cast the 0 to the proper type, but it's not necessary.
> :
> : Doug, as usual you are correct, but I have to point out that
> :       if (ptr == NULL)
> : also works, usually generates the same code, and gives a much better
> : idea of what the code is doing.
>
> I'm afraid that you've just contributed to the confusion.  In the
> material you quoted, the type of `ptr' is not specified. That being
> the case,
> 
> 	if (ptr == 0)
> 
> is not equivalent to
> 
> 	if (ptr == NULL)
> 
> They are only equivalent if the type of ptr is `void *' or `char *'.
> Otherwise, there are are implementations, those defining NULL as
> (char *)0, which will give an error on the latter statement.

WRONG.

I'm afraid you may have lost some context, but the original poster
(Eric.J.Bivona@Dartmouth.EDU) asked his question in the framework of ANSI
C.  Doug Gwyn's answer is (of course) correct, and Bill Davidsen's remark,
while reflecting an opinion, is correct in implementation (i.e,
	if (ptr == NULL)
is correct).

Even in the K&R1 days, the only valid definition of NULL has been

#define NULL 0

Any other implies a broken compiler/compilation-environment, and should be
discarded.

--Roberto
______________________________________________________________________________
                               ||   Internet: shirono@ssd.harris.com
     Roberto Shironoshita      ||
      Harris Corporation       ||             ...!novavax---\
   Computer Systems Division   ||   UUCP:     ...!uunet-------!hcx1!shirono
                               ||             ...!mit-eddie-/
------------------------------------------------------------------------------
DISCLAIMER: The opinions expressed here are my own; they in no way reflect the
            opinion or policies of Harris Corporation.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/02/88)

In article <226@twwells.uucp> bill@twwells.UUCP (T. William Wells) writes:
>Otherwise, there are are implementations, those defining NULL as
>(char *)0, which will give an error on the latter statement.

The only valid definitions for NULL are 0 and ((void*)0).
As you say, implementations exist that do this wrong.

tps@chem.ucsd.edu (Tom Stockfisch) (12/02/88)

In article <3340@tekcrl.CRL.TEK.COM> danw@tekchips.CRL.TEK.COM (Daniel E. Wilson) writes:
>In article <494@auspex.UUCP>, guy@auspex.UUCP (Guy Harris) writes:
>> >In assignments, a 0 or NULL is cast implicitly to the correct pointer
>
>  I tend to avoid the whole problem by defining a simple macro.  Simply
>by using this macro always I get a NULL pointer of the needed type.
>This does avoid bugs.
>
>     #define NIL(type) ((type *) NULL)

It also doesn't always work, e.g.
	
	NIL( int (*)[5] )

or

	NIL( void (*)() )

I use (type *)0 when passing arguments and NULL everywhere
else (except, of course, the null character should be
written '\0').
Then noone has to look up NIL() in a header
file, I don't have to make sure the header file is included
in virtually every source file I write, and it always works.

-- 

|| Tom Stockfisch, UCSD Chemistry	tps@chem.ucsd.edu

guy@auspex.UUCP (Guy Harris) (12/02/88)

>Otherwise, there are are implementations, those defining NULL as
>(char *)0, which will give an error on the latter statement.

Great!  That means that if I get an error, I get to throw rotten eggs at
the implementor.  It's a good test to see who understood what they were
doing when they implemented C.... 

If NULL is properly defined as specified by the dpANS, namely as 0 or
"(void *)0",

	if (ptr == 0)

and

	if (ptr == NULL)

are equivalent.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/02/88)

In article <44100016@hcx3> shirono@hcx3.SSD.HARRIS.COM writes:
>Even in the K&R1 days, the only valid definition of NULL has been
>#define NULL 0

True of pre-ANSI C, but an ANSI C implementation can use either that
definition or
#define NULL ((void*)0)
I recommend the former even for ANSI C implementations.  The added
complexity buys just one thing, which is possible type mismatch
checking, but I don't think that is significant enough to justify
the change.

jwr@scotty.UUCP (Jim Reid) (12/03/88)

In article danw@tekchips.CRL.TEK.COM (Daniel E. Wilson) writes:
>
>  I tend to avoid the whole problem by defining a simple macro.  Simply
>by using this macro always I get a NULL pointer of the needed type.
>This does avoid bugs.
>
>     #define NIL(type) ((type *) NULL)

If you were the person who originally posted this macro a couple of years ago,
I'd personally like to thank you for it.

I've been using it in my code ever since.
I haven't come across a better all-purpose pointer type.

-- 

Jim Reid		{ames,harvard,rutgers}!rochester!kodak!scotty!jwr

--

wald-david@CS.YALE.EDU (david wald) (12/05/88)

In article <9038@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <44100016@hcx3> shirono@hcx3.SSD.HARRIS.COM writes:
>>Even in the K&R1 days, the only valid definition of NULL has been
>>#define NULL 0
>
>True of pre-ANSI C, but an ANSI C implementation can use either that
>definition or
>#define NULL ((void*)0)
>I recommend the former even for ANSI C implementations.  The added
>complexity buys just one thing, which is possible type mismatch
>checking, but I don't think that is significant enough to justify
>the change.

I may be sorry in the morning for asking this, but:

Isn't the latter generally preferable, given its possible use as a
parameter for a function with no prototype in scope?  Further, isn't the
former dangerous in this case, given that there is no guarantee for NULL
and (int)0 to have the same representation?



============================================================================
David Wald                                              wald-david@yale.UUCP
						       waldave@yalevm.bitnet
============================================================================

chris@mimsy.UUCP (Chris Torek) (12/05/88)

>In article <9038@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>)
>writes:
>>True of pre-ANSI C, but an ANSI C implementation can use either
[#define NULL 0] or
>>#define NULL ((void*)0)
>>I recommend the former even for ANSI C implementations. ...

In article <44803@yale-celray.yale.UUCP> wald-david@CS.YALE.EDU
(david wald) writes:
>I may be sorry in the morning for asking this, but:

Perhaps.

>Isn't the latter generally preferable,

Not particularly.

>given its possible use as a parameter for a function with no
>prototype in scope?

Unless that function takes a `void *' argument in that position, if
the NULL is uncast, the code remains incorrect.  The compiler cannot
diagnose this since the function might take a `void *' argument in that
position.  For instance:

	void **
	collect(void *first, ...) {
		va_list ap;
		int n;
		void **p, **q, *t;

		if (first) {
			va_start(ap, first);
			n = 2;
			while (va_arg(ap, void *) != NULL)
				n++;
			va_end(ap);
		} else
			n = 1;
		p = malloc(n * sizeof(void *));
		if (p == NULL)
			return (NULL);
		q = p;
		if (first) {
			*q++ = first;
			va_start(ap, first);
			while ((t = va_arg(ap, void *)) != NULL)
				*q++ = t;
		}
		*q = NULL;
		return (p);
	}

Collect() accepts only `void *'; the end of the list it is to collect
into a vector is marked by a nil `void *'.

(Incidentally, collect() is another example of why requiring an
`anchor' argument for va_start is bad.)

>Further, isn't the former dangerous in this case, given that there is
>no guarantee for NULL and (int)0 to have the same representation?

No more dangerous than the latter, given that there is no guarantee
for NULL and NULL (of two different types, e.g., `void *' nil and
`struct glorp *' nil) to have the same representation.
-- 
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 ) (12/06/88)

In article <44803@yale-celray.yale.UUCP> wald-david@CS.YALE.EDU (david wald) writes:
>Isn't the latter generally preferable, given its possible use as a
>parameter for a function with no prototype in scope?  Further, isn't the
>former dangerous in this case, given that there is no guarantee for NULL
>and (int)0 to have the same representation?

There's also no guarantee that (void *) and other pointer types have the
same representation.  You MUST cast NULL when using it as a function
argument with no prototype in scope.  (If it happens to work without a
cast, it's an accident.  Your program should not rely on accidents to
work correctly.)

guy@auspex.UUCP (Guy Harris) (12/06/88)

 >Isn't the latter [(void *)0] generally preferable, given its possible
 >use as a parameter for a function with no prototype in scope?  Further,
 >isn't the former dangerous in this case, given that there is no
 >guarantee for NULL and (int)0 to have the same representation?

There's no guarantee that "(void *)0" and "(type *)0" have the same
representation, either, unless "type" is either "char" or "void", as far
as I know, so "(void *)0" doesn't guarantee success, either.