[comp.lang.c] use of NULL

ckl@uwbln.UUCP (Christoph Kuenkel) (02/02/89)

Can anyone please enlighten me what the discussion is about?  Why at
all should I ever use NULL anywhere?  My impression is, that a) it does
not work as one expects and b) its not needed anyway.

I simply never use it and never had problems.  Furthermore,  I think
its much more obvious to write 0,  when I mean zero,  ((char *) 0) when
I mean a zero character pointer, etc. etc.

Do I miss something?

Christoph
-- 
# include <std/disclaimer.h>
Christoph Kuenkel/UniWare GmbH       Kantstr. 152, 1000 Berlin 12, West Germany
ck@tub.BITNET                ckl@uwbln             {unido,tmpmbx,tub}!uwbln!ckl

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/04/89)

In article <1340@uwbull.uwbln.UUCP> ckl@uwbln.UUCP (Christoph Kuenkel) writes:
>Why at all should I ever use NULL anywhere?

Some think that use of a mnemonic makes the code more intelligible.
If C were being designed now, I would recommend that "nil" be made
a language keyword.

>its much more obvious to write 0,  when I mean zero,  ((char *) 0) when
>I mean a zero character pointer, etc. etc.

Using 0 instead of NULL is perfectly acceptable.

cramer@optilink.UUCP (Clayton Cramer) (02/17/89)

In article <9582@smoke.BRL.MIL., gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
. In article <1340@uwbull.uwbln.UUCP. ckl@uwbln.UUCP (Christoph Kuenkel) writes:
. .Why at all should I ever use NULL anywhere?
. 
. Some think that use of a mnemonic makes the code more intelligible.
. If C were being designed now, I would recommend that "nil" be made
. a language keyword.
. 
. .its much more obvious to write 0,  when I mean zero,  ((char *) 0) when
. .I mean a zero character pointer, etc. etc.
. 
. Using 0 instead of NULL is perfectly acceptable.

No it isn't.  Segmented architecture machines will have problems with
that in large model.  Microsoft defines NULL as 0L, not 0, in large
model.  Pushing an int 0 instead of a long 0 will screw you royally
on the PC.
-- 
Clayton E. Cramer
{pyramid,pixar,tekbspa}!optilink!cramer
Disclaimer?  You must be kidding!  No company would hold opinions like mine!

chris@mimsy.UUCP (Chris Torek) (02/19/89)

>In article <9582@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
>>Using 0 instead of NULL is perfectly acceptable.

In article <965@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer)
substitutes too many `>'s (I patched the references line) and writes:
>No it isn't.  Segmented architecture machines will have problems with
>that in large model.  Microsoft defines NULL as 0L, not 0, in large
>model.  Pushing an int 0 instead of a long 0 will screw you royally
>on the PC.

Doug Gwyn is right; you are reading things into his posting that
are not there.  Using 0 wherever NULL is strictly legal will always
work.  Never mind the fact that, by trickery, Microsoft C defines NULL
in a way that works for INCORRECT calls in large model (but *not* for
medium nor compact models), as a concession to bad programmers' wrong
programs.

Time for another rerun....

From: chris@mimsy.UUCP (Chris Torek)
Newsgroups: comp.lang.c
Subject: Why NULL is 0
Summary: you have seen this before, but this one is for reference
Date: 9 Mar 88 02:26:10 GMT

(You may wish to save this, keeping it handy to show to anyone who
claims `#define NULL 0 is wrong, it should be #define NULL <xyzzy>'.
I intend to do so, at any rate.)

Let us begin by postulating the existence of a machine and a compiler
for that machine.  This machine, which I will call a `Prime', or
sometimes `PR1ME', for obscure reasons such as the fact that it
exists, has two kinds of pointers.  `Character pointers', or objects
of type (char *), are 48 bits wide.  All other pointers, such as
(int *) and (double *), are 32 bits wide.

Now suppose we have the following C code:

 	main()
	{
 		f1(NULL);	/* wrong */
 		f2(NULL);	/* wrong */
 		exit(0);
 	}
 
 	f1(cp) char *cp; { if (cp != NULL) *cp = 'a'; }
 	f2(dp) double *dp; { if (dp != NULL) *dp = 2.2; }

There are two lines marked `wrong'.  Now suppose we were to define NULL
as 0.  Clearly both calls are then wrong: both pass `(int)0', when the
first should be a 48 bit (char *) nil pointer and the second a 32 bit
(double *) nil pointer.

Someone claims we can fix that by defining NULL as (char *)0.  Suppose
we do.  Then the first call is correct, but the second now passes a
48 bit (char *) nil pointer instead of a 32 bit (double *) nil pointer.
So much for that solution.

Ah, I hear another.  We should define NULL as (void *)0.  Suppose we
do.  Then at least one call is not correct, because one should pass
a 32 bit value and one a 48 bit value.  If (void *) is 48 bits, the
second is wrong; if it is 32 bits, the first is wrong.

Obviously there is no solution.  Or is there?  Suppose we change
the calls themselves, rather than the definition of NULL:

	main()
	{
		f1((char *)0);
		f2((double *)0);
		exit(0);
	}

Now both calls are correct, because the first passes a 48 bit (char *)
nil pointer, and the second a 32 bit (double *) nil pointer.  And
if we define NULL with

	#define NULL 0

we can then replace the two `0's with `NULL's:

	main()
	{
		f1((char *)NULL);
		f2((double *)NULL);
		exit(0);
	}

The preprocessor changes both NULLs to 0s, and the code remains
correct.

On a machine such as the hypothetical `Prime', there is no single
definition of NULL that will make uncasted, un-prototyped arguments
correct in all cases.  The C language provides a reasonable means
of making the arguments correct, but it is not via `#define'.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

bill@twwells.uucp (T. William Wells) (02/19/89)

In article <965@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer) writes:
: . Using 0 instead of NULL is perfectly acceptable.
:
: No it isn't.  Segmented architecture machines will have problems with
: that in large model.  Microsoft defines NULL as 0L, not 0, in large
: model.  Pushing an int 0 instead of a long 0 will screw you royally
: on the PC.

Here we go again.

Microsoft can do what they damn well please. But they are not the
authority on C.

Microsoft, like many compilers, supports pointers that are of
different sizes. Because of such compilers, passing 0 OR NULL to a
function is wrong, *wrong*, WRONG and may result in code that is
broke, *broke*, BROKE!!!!!!!

Now, for all you out there who want to know what is really going on,
here's the scoop. When you pass a zero to a function, the compiler
assumes that you mean a zero *integer*. However, if the function
receiving the argument wants a pointer, you may not get the result
you want.  Some examples (for the Microsoft compiler):

foo(ptr, n)
far char *ptr;          /* this might be implicitly far */
int     n;
...

#define NULL 0          /* this comes from an include file. */

	foo(NULL, 12)

The generated code looks something like this:

	push    12      | will be interpreted as high word of ptr
	push    0       | will be interpreted as low word of ptr
	call    foo     | and foo will get random value for n.

On the other hand, consider this:

foo(ptr, n)
near char *ptr;
int     n;
...

#define NULL 0L

	foo(NULL, 12)

	push    12      | this gets ignored
	push    0       | is used for n
	push    0       | is used for the pointer
	call    foo

Here's another failure mode:

foo(fptr, n)
int     (*fptr)();
int     n;
{
...
	foo(NULL, 42);

This code will fail when data pointers are a different size from
function pointers.

Think about these examples and consider what what happens if you use
NULL in some program and then discover that you need more than 64K of
code or data.  Boy are you screwed.

So, what's the right way?

Either provide a prototype for foo, the appropriate one of:

	foo(far char *ptr, int n)
	foo(near char *ptr, int n)
	foo(int (*fptr)(), int n)

that is in scope when foo is called, or call foo with one of:

	foo((far char *)0, 12)
	foo((near char *)0, 12)
	foo((int (*fptr)())0, 42)

(or a reasonable facsimile), or, if you are feeling verbose,

	foo((far char *)NULL, 12)
	foo((near char *)NULL, 12)
	foo((int (*fptr)())NULL, 42)

AND NEVER PASS BARE 0 OR NULL AS A POINTER ARGUMENT!

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

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/19/89)

In article <965@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer) writes:
-In article <9582@smoke.BRL.MIL., gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
-. In article <1340@uwbull.uwbln.UUCP. ckl@uwbln.UUCP (Christoph Kuenkel) writes:
-. .its much more obvious to write 0,  when I mean zero,  ((char *) 0) when
-. .I mean a zero character pointer, etc. etc.
-. Using 0 instead of NULL is perfectly acceptable.
-No it isn't.  Segmented architecture machines will have problems with
-that in large model.  Microsoft defines NULL as 0L, not 0, in large
-model.  Pushing an int 0 instead of a long 0 will screw you royally
-on the PC.

It is always non-portable to pass the macro NULL, or 0 or 0L for that
matter, as a pointer argument to a function without casting it to the
correct pointer type.  MicroSoft's definition appears to have been an
ill-considered attempt to make sloppily-written code have a greater
chance of working when compiled in their environment.  However, such
code won't port correctly to other systems until the misusage of NULL
is fixed anyway.

There is nothing "magic" about NULL as defined by <stdio.h> etc.  It's
just a mnemonic for 0, ((void*)0), or some similar expression.  The C
language itself guarantees that the programmer can use unadorned 0 in
pointer comparisons, assignments, etc.  Given the way some vendors
have botched the definition of NULL, it may even be advisable to avoid
using the NULL macro.  It is never *necessary* to use it in portable C
programming.

cramer@optilink.UUCP (Clayton Cramer) (02/22/89)

In article <399@twwells.uucp:, bill@twwells.uucp (T. William Wells) writes:
: In article <965@optilink.UUCP: cramer@optilink.UUCP (Clayton Cramer) writes:
: : . Using 0 instead of NULL is perfectly acceptable.
: :
: : No it isn't.  Segmented architecture machines will have problems with
: : that in large model.  Microsoft defines NULL as 0L, not 0, in large
: : model.  Pushing an int 0 instead of a long 0 will screw you royally
: : on the PC.
: 
: Here we go again.
: 
: Microsoft can do what they damn well please. But they are not the
: authority on C.

I never claimed differently.  But the use of NULL when that's what
you mean will save someone some headaches when it comes time to
port a program to a segmented arguments.

: Microsoft, like many compilers, supports pointers that are of
: different sizes. Because of such compilers, passing 0 OR NULL to a
: function is wrong, *wrong*, WRONG and may result in code that is
: broke, *broke*, BROKE!!!!!!!

Not true.  In large model, stdio.h defines NULL to be 0L.  NULL requires
no casting to work correctly with the Microsoft C compiler.

: Now, for all you out there who want to know what is really going on,
: here's the scoop. When you pass a zero to a function, the compiler
: assumes that you mean a zero *integer*. However, if the function
: receiving the argument wants a pointer, you may not get the result
: you want.  Some examples (for the Microsoft compiler):
: 
: foo(ptr, n)
: far char *ptr;          /* this might be implicitly far */
: int     n;
: ...
: 
: #define NULL 0          /* this comes from an include file. */

Wrong.  The stdio.h file defines NULL as 0L in large model, and 0
for the small model.

: 	foo(NULL, 12)
: 
: The generated code looks something like this:
: 
: 	push    12      | will be interpreted as high word of ptr
: 	push    0       | will be interpreted as low word of ptr
: 	call    foo     | and foo will get random value for n.
: 
: On the other hand, consider this:
: 
: foo(ptr, n)
: near char *ptr;
: int     n;
: ...
: 
: #define NULL 0L
: 
: 	foo(NULL, 12)
: 
: 	push    12      | this gets ignored
: 	push    0       | is used for n
: 	push    0       | is used for the pointer
: 	call    foo
: 
: Here's another failure mode:
: 
: foo(fptr, n)
: int     (*fptr)();
: int     n;
: {
: ...
: 	foo(NULL, 42);
: 
: This code will fail when data pointers are a different size from
: function pointers.

That's why you don't #define NULL yourself -- use the stdio.h file.

: Think about these examples and consider what what happens if you use
: NULL in some program and then discover that you need more than 64K of
: code or data.  Boy are you screwed.
: 
: So, what's the right way?
: 
: Either provide a prototype for foo, the appropriate one of:
: 
: 	foo(far char *ptr, int n)
: 	foo(near char *ptr, int n)
: 	foo(int (*fptr)(), int n)
: 
: that is in scope when foo is called, or call foo with one of:
: 
: 	foo((far char *)0, 12)
: 	foo((near char *)0, 12)
: 	foo((int (*fptr)())0, 42)
: 
: (or a reasonable facsimile), or, if you are feeling verbose,
: 
: 	foo((far char *)NULL, 12)
: 	foo((near char *)NULL, 12)
: 	foo((int (*fptr)())NULL, 42)
: 
: AND NEVER PASS BARE 0 OR NULL AS A POINTER ARGUMENT!
: 
: ---
: Bill

Never pass bare 0 as a pointer argument -- but use the stdio.h
definition of NULL, and the segmented architecture will NOT screw
you.  Unless you are engaged in mixed model programming (only for
the experienced programmer), there's no need to declare anything
to be "near" or "far" -- the compiler and the preprocessor do 
the whole job for you.



-- 
Clayton E. Cramer
{pyramid,pixar,tekbspa}!optilink!cramer
Disclaimer?  You must be kidding!  No company would hold opinions like mine!

cramer@optilink.UUCP (Clayton Cramer) (02/22/89)

In article <16020@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
> >In article <9582@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
> >>Using 0 instead of NULL is perfectly acceptable.
> 
> In article <965@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer)
> substitutes too many `>'s (I patched the references line) and writes:
> >No it isn't.  Segmented architecture machines will have problems with
> >that in large model.  Microsoft defines NULL as 0L, not 0, in large
> >model.  Pushing an int 0 instead of a long 0 will screw you royally
> >on the PC.
> 
> Doug Gwyn is right; you are reading things into his posting that
> are not there.  Using 0 wherever NULL is strictly legal will always
> work.  Never mind the fact that, by trickery, Microsoft C defines NULL
> in a way that works for INCORRECT calls in large model (but *not* for
> medium nor compact models), as a concession to bad programmers' wrong
> programs.

This is not correct.  Microsoft C defines NULL in a way that works
for CORRECT calls in large model, and in medium and compact models.

"Using 0 whereever NULL is strictly legal" will not work in Microsoft
C, and because Microsoft has come up with the correct solution for
an otherwise damaged architecture.  Chris' additional comments make 
a distinction between data and code pointers.  This will be a problem
in compact and medium models (only), as described on page 142, 
section 6.3.3 of the "Microsoft C Optimizing Compiler User's Guide":

    Note that in medium and compact models, NULL must be used
    carefully in certain situations.  NULL actually represents
    a null data pointer.  In memory models where code and data
    pointers are the same size, it can be used with either.  
    However, in memory models where code and data pointers are
    different sizes, this is not the cas.  Consider the 
    following example:

And they show a similar example to Chris.  Note that they have
erred on the side of screwing up code pointers, not data pointers,
since data pointers are FAR more common in C than code pointers.

Note also that most programs of any great complexity end up in
large model on the PC, not medium or compact.


    
-- 
Clayton E. Cramer
{pyramid,pixar,tekbspa}!optilink!cramer
Disclaimer?  You must be kidding!  No company would hold opinions like mine!

chris@mimsy.UUCP (Chris Torek) (02/22/89)

>In article <16020@mimsy.UUCP> I wrote:
>>...  Using 0 wherever NULL is strictly legal will always work.  Never
>>mind the fact that, by trickery, Microsoft C defines NULL in a way that
>>works for INCORRECT calls in large model (but *not* for medium nor
>>compact models), as a concession to bad programmers' wrong programs.

In article <975@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer) writes:
>This is not correct.  Microsoft C defines NULL in a way that works
>for CORRECT calls in large model, and in medium and compact models.

Let me put it in as few words as possible:

If you do it my way, it always works.

If you do it your way, it sometimes works.

Now, which do you prefer?
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

Don_A_Corbitt@cup.portal.com (02/23/89)

Clayton,
  Counter example time for MSC and NULL.  (We already know of many counter 
examples for other machines, such as Pyramid...)

Compact model (data pointer 32 bit, function pointer 16 bit).

Function that has a function pointer argument called if error occurs (or calls 
exit() if no error handler passed in...)

void DoSomething(int p1, int p2, void (*errhand)(int));

(oops - make this medium model - 32 bit code pointer, 16 bit data)

Call DoSomething with no function prototype:
  (and no error handler)
  DoSomething(100, 200, NULL);

NULL is defined as 0 (16 bits).  DoSomething detects an error, checks the
errhand parameter, it is not 0 (32 bits), so it calls the function.  If you 
cast NULL to a function pointer, it will pass a 32 bit pointer, and all is
well.
	Don_A_Corbitt@cup.portal.com

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/23/89)

In article <973@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer) writes:
>Never pass bare 0 as a pointer argument -- but use the stdio.h
>definition of NULL, and the segmented architecture will NOT screw
>you.

Wrong.  Use of uncast NULL as an argument to a function is never correct
usage; although you can get away with it sometimes, it only "works" by
accident and may quit working suddenly if the implementation changes.
It is definitely not portable.  Always cast 0 or NULL to the correct
pointer type when used as a function argument.

mveao@cbnews.ATT.COM (eric.a.olson) (02/23/89)

    I always felt that the major benefit to be derived from using
    NULL where a pointer to 0 is called for is that it makes
    you feel so good when you stop doing it. :-)

						eric olson
						eao@mvuxq.att.com

djones@megatest.UUCP (Dave Jones) (02/23/89)

From article <16069@mimsy.UUCP>, by chris@mimsy.UUCP (Chris Torek):

> 
> Let me put it in as few words as possible:
> 
> If you do it my way, it always works.
> 
> If you do it your way, it sometimes works.
> 

Most elegantly stated.

Is it true that a fundamentalist Hackers' cult in Berkeley
has offered $5 million to anyone is obliterates NULL, the 
ultimate SLM?

Probably just a rumor.


            Dave J.

piet@ruuinf (Piet van Oostrum) (02/23/89)

In article <973@optilink.UUCP>, cramer@optilink (Clayton Cramer) writes:
 `In article <399@twwells.uucp:, bill@twwells.uucp (T. William Wells) writes:
 `: In article <965@optilink.UUCP: cramer@optilink.UUCP (Clayton Cramer) writes:
 `: : . Using 0 instead of NULL is perfectly acceptable.
 `: :
 `: : No it isn't.  Segmented architecture machines will have problems with
 `: : that in large model.  Microsoft defines NULL as 0L, not 0, in large
 `: : model.  Pushing an int 0 instead of a long 0 will screw you royally
 `: : on the PC.
 `: 
And on and on and on and on .....

You are all wrong. ANSI C says that the CONSTANT 0 used in a pointer
context should always give the ``null'' pointer for that pointer type.

BUT

It must be clear from the context that a pointer type is required.

The only good way to give a null pointer to a function is to write

		funct ( (foo *) 0 );
                        ^^^^^^^

i.e cast it. If you have function prototypes, the cast will be done by the
compiler. Even if you want to use NULL you have to write:

		funct ( (foo *) NULL );

If you don't cast, it may work on your particular compiler, but some day it
will hit you, unless you throw away your program before anybody else gets a
chance to use it.
-- 
Piet van Oostrum, Dept of Computer Science, University of Utrecht
Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands
Telephone: +31-30-531806. piet@cs.ruu.nl (mcvax!hp4nl!ruuinf!piet)

mcdonald@uxe.cso.uiuc.edu (02/24/89)

Subject is the flame wars resulting from the never-ending NULL
(presumable pointer) affairs.

Chris Torek claims (correctly) that if one wishes to pass a null
pointer to a subroutine, that if you cast it to the proper type,
it will work:
      sub((char *)0); /* pass a character pointer*/
     subd((double *)0); /* pass a pointer to double */

I ask: Is it not possible to do away with this if one uses 
functions prototypes? Is this correct (I mean, ABSOLUTELY correct?)

void sub(char *);

void subd(double *);

sub(0);
subd(0);

void sub(char * xchar)
{ ... }
void sub(double *ddouble)
{ ... }

I ask this as a question, not as a proposition of a flame.

I have written a lot of programs for mixed (VERY mixed) model programs
on the PC, and used strict prototypes for all. They seem to work 
fine. I never cast anything in arguments, unless it is a signed to
unsigned (or vice-versa) conversion, and in that case I am paranoid
enough to write out each step explicitly. Am I doing a no-no?



Doug McDonald

heather@SEAS.UCLA.EDU (02/24/89)

In article <9684@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <973@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer) writes:
>>Never pass bare 0 as a pointer argument -- but use the stdio.h
>>definition of NULL, and the segmented architecture will NOT screw
>>you.
>
>Wrong.  Use of uncast NULL as an argument to a function is never correct
>usage; although you can get away with it sometimes, it only "works" by
>accident and may quit working suddenly if the implementation changes.
>It is definitely not portable.  Always cast 0 or NULL to the correct
>pointer type when used as a function argument.

Excuse me for a probably naive question that I've had since following the
discussion of passing NULL to functions: Why doesn't the C standard
treat NULL or 0 (a static/constant NULL or 0, i.e. indicated at compile time)
passed to a function that has a pointer value in the function prototype 
as a special case and do the cast implicitly?

Heather Burris, UCLA

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/24/89)

In article <20928@shemp.CS.UCLA.EDU> heather@SEAS.UCLA.EDU (Heather Burris) writes:
>Why doesn't the C standard treat NULL or 0 (a static/constant NULL or 0,
>i.e. indicated at compile time) passed to a function that has a pointer
>value in the function prototype as a special case and do the cast implicitly?

It does (actually it's not a special case), but most existing C code
does not use prototypes, in which case the programmer has to explicitly
cast the NULL argument to the proper type.

wietse@wzv.UUCP (Wietse Z. Venema) (02/24/89)

In article <973@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer) writes:
>
>Never pass bare 0 as a pointer argument -- but use the stdio.h
>definition of NULL, and the segmented architecture will NOT screw
>you.  
>

Wrong.  Think of the middle memory model of microsoft c where
sizeof(data pointer) != sizeof(function pointer).  One should 
NEVER rely on a universal representation of null pointers.  
As written in a recent article, NULL is a mistake.
-- 
work:	wswietse@eutrc3.uucp	| Eindhoven University of Technology
work:	wswietse@heitue5.bitnet	| Mathematics and Computing Science
home:	wietse@wzv.uucp		| 5600 MB Eindhoven, The Netherlands

chris@mimsy.UUCP (Chris Torek) (02/24/89)

In article <20928@shemp.CS.UCLA.EDU> heather@SEAS.UCLA.EDU writes:
>Excuse me for a probably naive question that I've had since following the
>discussion of passing NULL to functions: Why doesn't the C standard
>treat NULL or 0 (a static/constant NULL or 0, i.e. indicated at compile time)
>passed to a function that has a pointer value in the function prototype 
>as a special case and do the cast implicitly?

This is a perfectly reasonable question, with a perfectly reasonable
answer.  It is not a special case and it works just fine.  The key
is the phrase `function prototype':  Not all compilers handle prototypes,
and not all programs include prototypes.

If the compiler does not *know* that the first argument to strtok() has
type `char *', it cannot convert a 0 argument to a nil-pointer-to-char.
It passes an int-0 (or, with Microsoft's `0L' definition, a long-0)
simply because it does not know any better.  Give it the information
(in the form of a prototype declaration, or a cast at invocation, or
both) and it does the conversion.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

guy@auspex.UUCP (Guy Harris) (02/25/89)

 >Excuse me for a probably naive question that I've had since following the
 >discussion of passing NULL to functions: Why doesn't the C standard
 >treat NULL or 0 (a static/constant NULL or 0, i.e. indicated at compile time)
 >passed to a function that has a pointer value in the function prototype 
 >as a special case and do the cast implicitly?

It does.  (It's not really a "special case"; conversions in calls to
functions with a prototype in scope are just like other conversions - at
least according to the May 13, 1988 draft, which says the conversions
are performed "as if by assignment".)

However:

	1) not all compilers *support* function prototypes, so unless
	   you *never* expect to compile your code in an environment
	   where the appropriate prototypes are not in scope, it's
	   probably wise to put the explicit casts in anyway.

	2) not all environments put the prototypes in the appropriate
	   include files; see 1).

	3) actual arguments that aren't matched by formal arguments in
	   the prototype (i.e., if the prototype has a "..." in it)
	   obviously can be converted only with the "default argument
	   promotions", which obviously doesn't include converting 0 to
	   a null pointer of the appropriate type.

Doug was probably referring to one or more of those cases when he said:

	"Use of uncast NULL as an argument to a function is never correct
	usage ..."

"Never" might, admittedly, be a little strong, if you take "correct" to
mean "proper according to the pANS"; if you have

	extern void	foo(char *ptr);

in scope, a call

	foo(0);		/* or foo(NULL); */

is correct - as long as you know for sure that in *every* environment in
which this code will be compiled, the prototype in question will be in
scope when "foo" is called.

throopw@xyzzy.UUCP (Wayne A. Throop) (02/25/89)

> cramer@optilink.UUCP (Clayton Cramer)
> But the use of NULL when that's what
> you mean will save someone some headaches when it comes time to
> port a program to a segmented arguments.

Not so.  Because using uncast NULL as an actual argument for
a formal argument of pointer type with no prototype in scope
IS NEVER WHAT YOU MEAN.  PERIOD.

It is simply not portable to do this.  The C lanuage as defined by
K&R or (proposed) by ANSI does not guarantee the behavior if this is
done.

> Not true.  In large model, stdio.h defines NULL to be 0L.  NULL requires
> no casting to work correctly with the Microsoft C compiler.

In some models, sometimes.  But in general, the usage is not portable
to all memory models on this one compiler, and certainly not portable
to all compilers.  It is unsupported by any language standard (as
opposed to partially supported by a language implementation).

--
All things sick and cancerous,
All evil great ans small,
All things foul and dangerous,
The Lord God made them all.
          --- Monty Python
-- 
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

throopw@xyzzy.UUCP (Wayne A. Throop) (02/25/89)

> cramer@optilink.UUCP (Clayton Cramer)
> Microsoft C defines NULL in a way that works
> for CORRECT calls in large model, and in medium and compact models.

This is surely an unusual meaning of "correct".  The C language
makes it clear that passing an uncast arithmetic constant as an
actual parameter to a formal parameter of type pointer when no
prototype is in scope is never correct.

--
Each nasty little hornet,
Each beastly little squid,
Who made the slimy urchin?
Who make the shark?  He did!
          --- Monty Python
-- 
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

throopw@xyzzy.UUCP (Wayne A. Throop) (02/25/89)

> heather@SEAS.UCLA.EDU
> Why doesn't the C standard
> treat NULL or 0 (a static/constant NULL or 0, i.e. indicated at compile time)
> passed to a function that has a pointer value in the function prototype 
> as a special case and do the cast implicitly?

Good point.  The answer is that the discussion has been about what
happens when there is no prototype in scope.  It happens as you
suggest when there is, but when there isn't, there isn't enough
information to do the cast correctly.  If the type of the formal
argument is unknown to the compiler, even if the compiler was aware of
NULL as a keyword, it would not know which type of null pointer was
wanted.  This is crucial for some architectures, where pointers to (or
of) different types have different storage requiremnts or formats.

--
God made integers.  All else is the work of man.
                    --- Leopold Kronecker (1823-1891)
-- 
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw