use of NULL

Clayton Cramer cramer at optilink.UUCP
Wed Feb 22 08:22:04 AEST 1989


In article <399 at twwells.uucp:, bill at twwells.uucp (T. William Wells) writes:
: 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.

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!



More information about the Comp.lang.c mailing list