use of NULL

T. William Wells bill at twwells.uucp
Sun Feb 19 05:23:37 AEST 1989


In article <965 at optilink.UUCP> cramer at 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



More information about the Comp.lang.c mailing list