[comp.lang.c] More NULL questions

jg@hpldola.HP.COM (Joe Gilray) (10/06/89)

Recently I've seen some discussion on the use of NULL, there was
a lot of discussion but I still feel that I need the answers to the
following questions:

1) what is the danger with using
         struct thing *ptr;
         .
         .
         .
         if (ptr != NULL) ...
   as apposed to
         struct thing *ptr;
         .
         .
         .
         if (ptr != (struct thing *)NULL) ...
   ?

2) Is there danger using
         int a, b;
         .
         .
         .
         my_func(a, NULL, b);
   as apposed to
         int a, b;
         .
         .
         .
         my_func(a, (struct thing *)NULL, b);
   when
   a) you are using function prototypes?
   b) you are NOT using function prototypes (like me)?

Thanks for any help.

-Joe Gilray

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/07/89)

In article <5950001@hpldola.HP.COM> jg@hpldola.HP.COM (Joe Gilray) writes:
>1) what is the danger with using
>         if (ptr != NULL) ...
>   as apposed to
>         if (ptr != (struct thing *)NULL) ...

There is no danger, assuming the C implementation is correct.

>2) Is there danger using
>         my_func(a, NULL, b);
>   as apposed to
>         my_func(a, (struct thing *)NULL, b);
>   when
>   a) you are using function prototypes?

The only danger is that someone may port your code to a "classic C"
environment and miss making the necessary edit.

>   b) you are NOT using function prototypes (like me)?

Yes, it is erroneous to feed NULL to a function where a pointer argument
is expected.

devine@shodha.dec.com (Bob Devine) (10/07/89)

In article <5950001@hpldola.HP.COM>, jg@hpldola.HP.COM (Joe Gilray) writes:
> 1) what is the danger with using
>          struct thing *ptr;
>          if (ptr != NULL) ...
>          as opposed to:
>          if (ptr != (struct thing *)NULL) ...

  No danger.  The comparison of `ptr' with NULL causes the NULL
to be converted to the correct type.  Look at the binary ops conversions.
I consider it good coding style to put the cast there; it lets the
future reader understand the code with less puzzlement.

> 2) Is there danger using
>          int a, b;
>          my_func(a, NULL, b);
>    as apposed to
>          my_func(a, (struct thing *)NULL, b);

  You are in trouble if you do not use function prototypes
if your system's pointer representation is not the same size
as an int.  Moreover if the pointer has some strange bit
pattern representation then the NULL may not be passed correctly.

  Other places where a conversion takes place without the
explicit cast are: in returns, assignments, and all ops.

Bob Devine

chris@mimsy.UUCP (Chris Torek) (10/07/89)

In article <5950001@hpldola.HP.COM> jg@hpldola.HP.COM (Joe Gilray) writes:
>1) what is the danger with using
>         struct thing *ptr;
>         if (ptr != NULL) ...
[vs]
>         if (ptr != (struct thing *)NULL) ...
>   ?

None.  NULL (typically defined as `0' or, in newer compilers, `(void *)0')
becomes a nil pointer when it is used in a comparison or assignment
context where a pointer is needed.  The comparison contexts that can
change `0' to a particular nil pointer are `==' and `!='.  Assignment
contexts are comprised of assignments, casts, and prototyped arguments
to functions.

>         my_func(a, NULL, b);
[vs]
>         my_func(a, (struct thing *)NULL, b);
>   when
>   a) you are using function prototypes?

If a prototype for my_func() is in scope at the point of the call, the
only danger is confusion on the part of the reader (note that all other
contexts listed above are `short range', i.e., you can simply `look
near' the `0' or `NULL' to see if it is being used in an assignment
context, but not so in this case).

>   b) you are NOT using function prototypes (like me)?

If no prototype is in scope, the uncast NULL expands to 0, 0L, or (void
*)0, all of which have different types than (struct thing *)0, and all
of which could have different representations as well.  In other words,
if it works at all, that is mere luck.  (Whether the luck is good or
ill is a question perhaps best left unanswered.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

ok@cs.mu.oz.au (Richard O'Keefe) (10/07/89)

In article <5950001@hpldola.HP.COM>, jg@hpldola.HP.COM (Joe Gilray) writes:
> 1) what is the danger with using
>       struct thing *ptr; ... if (ptr != NULL) ...
>    as Opposed to
>       struct thing *ptr; ... if (ptr != (struct thing *)NULL) ...

None.  You can even use    ... if (ptr) ...

> 2) Is there danger using
>       int a, b; ... my_func(a, NULL, b);
>    as Opposed to
>       int a, b; ... my_func(a, (struct thing *)NULL, b);
>    when
>    a) you are using function prototypes?

No.  If there is a prototype in scope, passing an argument is like
assignment, NULL will be converted to the appropriate pointer type.

>    b) you are NOT using function prototypes (like me)?

Yes.  You will get an int, which may not be the same size as a pointer,
and if it is, may not be the same bit pattern as (struct thing *)NULL.

It isn't just NULL.  With some C compilers, and on some machines,
the cast in
	struct thing *ptr; ...
	if (fwrite((char *)ptr, sizeof *ptr, 1, stream) != 1) ...
is absolutely necessary.  (The cast is (void *) in ANSI C, and is
not needed if you have the prototype of fwrite() in scope.)

aep@ivan (Alex E. Pensky) (10/09/89)

In article <443@shodha.dec.com> devine@shodha.dec.com (Bob Devine) writes:
>In article <5950001@hpldola.HP.COM>, jg@hpldola.HP.COM (Joe Gilray) writes:
>> 2) Is there danger using
>>          int a, b;
>>          my_func(a, NULL, b);
>>    as apposed to
>>          my_func(a, (struct thing *)NULL, b);
>
>  You are in trouble if you do not use function prototypes
>if your system's pointer representation is not the same size
>as an int.  Moreover if the pointer has some strange bit
>pattern representation then the NULL may not be passed correctly.
>

Even if pointers and integers are the same size and have the same 
representation, you are still in trouble if your compiler passes int
parameters and pointer parameters via different mechanisms.  In such
a case, omitting both the prototype and the cast will mean that the *entire*
parameter list will be received incorrectly by my_func().

Yes, such compilers exist, and yes, I have been bitten by one after
forgetting the casts.

 -----------------------------------------------------------------------------
 Alex Pensky    ...!{cwjcc,decvax,pyramid,uunet}!abvax!aep       (216)646-5211
 Allen-Bradley Company             747 Alpha Drive, Highland Heights, OH 44143
 -----------------------------------------------------------------------------

devine@shodha.dec.com (Bob Devine) (10/10/89)

In article <903@abvax.UUCP>, aep@ivan (Alex E. Pensky) writes:
> Even if pointers and integers are the same size and have the same 
> representation, you are still in trouble if your compiler passes int
> parameters and pointer parameters via different mechanisms.  In such
> a case, omitting both the prototype and the cast will mean that the *entire*
> parameter list will be received incorrectly by my_func().
> Yes, such compilers exist, and yes, I have been bitten by one after
> forgetting the casts.

  What different mechanisms?  C only supports call-by-value for
parameters (I'm ignoring the special casing of arrays here).  How
can pointers be passed differently than ints?  It sounds like you
used a broken compiler.

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/11/89)

In article <448@shodha.dec.com> devine@shodha.dec.com (Bob Devine) writes:
-In article <903@abvax.UUCP>, aep@ivan (Alex E. Pensky) writes:
-> Even if pointers and integers are the same size and have the same 
-> representation, you are still in trouble if your compiler passes int
-> parameters and pointer parameters via different mechanisms.
-  What different mechanisms?  C only supports call-by-value for
-parameters (I'm ignoring the special casing of arrays here).  How
-can pointers be passed differently than ints?  It sounds like you
-used a broken compiler.

[This list sure has been getting a lot of GUESSWORK posted to it recently.]

A simple example is that the first M integer parameters might be passed
in data registers and the first N pointer parameters passed in address
registers.  Yes, there are compilers that follow conventions like this,
and even stranger ones.  They are not "broken".

chris@mimsy.UUCP (Chris Torek) (10/12/89)

>In article <903@abvax.UUCP> aep@ivan (Alex E. Pensky) writes:
>>Even if pointers and integers are the same size and have the same 
>>representation, you are still in trouble if your compiler passes int
>>parameters and pointer parameters via different mechanisms. ...

In article <448@shodha.dec.com> devine@shodha.dec.com (Bob Devine) writes:
>  What different mechanisms?  C only supports call-by-value for
>parameters (I'm ignoring the special casing of arrays here).  How
>can pointers be passed differently than ints?  It sounds like you
>used a broken compiler.

I dare say Mr. Pensky meant `different machine mechanisms that both
implement call-by-value'.  For instance, it is generally a Good Thing
to pass one or two parameters in registers, rather than on the stack,
on many machines.  As an example, consider the 680x0 (x=0,1,2,3).  On
this CPU, pointers fit best in address registers (a0..a7) and integers
fit best in data register (d0..d7).  One could pass the first two
pointer arguments in a0 and a1, and the first two integer arguments in
d0 and d1 (allowing up to four arguments without any memory traffic).

(Incidentally, the `special case for arrays' is at a higher level than
argument passing.  Arguments are expressions (rvalues) and arrays in
such contexts are converted to pointers by the rule I am constantly
describing.  Since pointers are normally used to effect
call-by-reference when needed, and since array declarations of formal
parameters are quietly converted to pointer declarations, the language
`fakes up' call-by-reference for arrays, but in truth something else is
going on entirely.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

scjones@sdrc.UUCP (Larry Jones) (10/12/89)

In article <448@shodha.dec.com>, devine@shodha.dec.com (Bob Devine) writes:
> In article <903@abvax.UUCP>, aep@ivan (Alex E. Pensky) writes:
> > Even if pointers and integers are the same size and have the same 
> > representation, you are still in trouble if your compiler passes int
> > parameters and pointer parameters via different mechanisms.  In such
> > a case, omitting both the prototype and the cast will mean that the *entire*
> > parameter list will be received incorrectly by my_func().
> > Yes, such compilers exist, and yes, I have been bitten by one after
> > forgetting the casts.
> 
>   What different mechanisms?  C only supports call-by-value for
> parameters (I'm ignoring the special casing of arrays here).  How
> can pointers be passed differently than ints?  It sounds like you
> used a broken compiler.

It's not call-by-value vs something else, it how you *do* the
call-by-value.  For example, a compiler could choose to pass
three separate arguments lists: one containing the integer parms,
one containing the floats, and the third containing the pointers.
This is a perfectly valid implementation where passing an
incorrectly typed argument can screw up all the following
parameters rather than just the erroneous one.

"But," I hear you cry, "why would anyone ever implement anything
so stupid?!?"  Well, start thinking about putting those parameter
lists in registers rather than memory, and it should be obvious.
----
Larry Jones                         UUCP: uunet!sdrc!scjones
SDRC                                      scjones@SDRC.UU.NET
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
"I have plenty of good sense.  I just choose to ignore it."
-Calvin

aep@ivan (Alex E. Pensky) (10/12/89)

In article <903@abvax.UUCP>, I, aep@ivan (Alex E. Pensky) wrote:
>> Even if pointers and integers are the same size and have the same 
>> representation, you are still in trouble if your compiler passes int
>> parameters and pointer parameters via different mechanisms.  In such
>> a case, omitting both the prototype and the cast will mean that the *entire*
>> parameter list will be received incorrectly by my_func().

In article <448@shodha.dec.com> devine@shodha.dec.com (Bob Devine) responds:
>  What different mechanisms?  C only supports call-by-value for
>parameters (I'm ignoring the special casing of arrays here).  How
>can pointers be passed differently than ints?  It sounds like you
>used a broken compiler.

Imagine this:  Compiler supports passing in registers rather than
on the stack.  Motorola 68xxx compiler.  Scalars are passed in data
registers, pointers in address registers.

    foobar( (char *)NULL )	passes a 32-bit zero in register A2, but

    foobar( NULL )		passes a 32-bit zero in register D3

If foobar() is written to expect a char *, it will look for its argument
in register A2.  Who knows what's in A2, in the latter case above?

Welcome to the Tektronix CLANDS II cross-development system!!  No, the
compiler is not broken, it's just trying to make my program run faster
by reducing function call overhead.

Moral of the story:  If you ever assume that a cast is not really necessary
when mixing types, you are writing code that is not only machine-specific,
it is also COMPILER-SPECIFIC.

 -----------------------------------------------------------------------------
 Alex Pensky    ...!{cwjcc,decvax,pyramid,uunet}!abvax!aep       (216)646-5211
 Allen-Bradley Company             747 Alpha Drive, Highland Heights, OH 44143
 -----------------------------------------------------------------------------