Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!uflorida!novavax!twwells!bill From: bill@twwells.uucp (T. William Wells) Newsgroups: comp.lang.c Subject: Re: use of NULL Keywords: NULL zero 0 C Microsoft Message-ID: <399@twwells.uucp> Date: 18 Feb 89 19:23:37 GMT References: <1167@unisec.usi.com. <5312@turnkey.TCC.COM. <9582@smoke.BRL.MIL. <965@optilink.UUCP> Reply-To: bill@twwells.UUCP (T. William Wells) Organization: None, Ft. Lauderdale Lines: 96 Summary: Expires: Sender: Followup-To: Distribution: 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