Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!rutgers!dayton!rosevax!sds!dave From: dave@sds.UUCP (dave schmidt x194) Newsgroups: comp.lang.c,news.software.b Subject: passing NULL to functions Message-ID: <150@sds.UUCP> Date: Sun, 26-Apr-87 16:06:04 EDT Article-I.D.: sds.150 Posted: Sun Apr 26 16:06:04 1987 Date-Received: Mon, 27-Apr-87 00:07:42 EDT Organization: SciCom Data Services, Minnetonka, MN Lines: 67 Xref: mnetor comp.lang.c:1908 news.software.b:551 In article <5794@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: > In article <888@viper.UUCP> john@viper.UUCP (John Stanley) writes: > > myfunc( i1, NULL, i2 ); > No matter what NULL is defined as, the above usage is non-portable and > will break on some systems. The problem is that the widths of pointers > of various types are in general different (and different from an (int)), > so that the wrong parameter alignment will occur unless one happens to > be lucky. If you pass NULL as a function parameter, you should always > cast it to the correct pointer type, as in > myfunc( i1, (struct foo *)NULL, i2 ); > > It is certainly true that there is a lot of code that makes this > mistake; I must have fixed several hundred occurrences of this > particular bug by now. That does not make it any less erroneous. I agree; the problem is compounded by the fact that certain C library routines encourage this sort of thinking. For example, the MicroSoft Xenix manual says of time(): long time(tloc) long *tloc; ... If 'tloc' (taken as an integer) is nonzero, the return value is also stored in the location to which 'tloc' points. While I think that this clearly indicates you should write: "longvar = time((long *)NULL);" rather than "longvar = time(NULL);", I have seen more instances of the latter than the former in other people's code. Note too that what the manual says is very misleading; assume that 'tloc' is a 32-bit pointer on a 64K byte boundary so that its address is, for example, 0xffff0000. Taking this as an integer (it doesn't say LONG integer) which is 16-bits will give you 0. I assume the manual means ... if 'tloc' (taken as an integer of the requisite size) is nonzero, ... but that's not what it says. Worse yet, consider what the manual says about tmpnam(): char *tmpnam(s) char *s; ... If 's' is NULL, ... If 's' is not NULL ... This CLEARLY implies that "tmpnam(NULL)" is CORRECT, which it is ***NOT***. I think we should roast weenies who write manual pages like this before we roast programmers who pass NULL as an argument to a function. To ease the problems associated with this, I advocate a #define that a friend of mine suggested: #define NIL(type) ((type)NULL) so that a programmer can write myfunc( i1, NIL(struct foo *), i2 ); and have it be portable and the intention clear. Along a similar vein, #define ALLOC(type, qty) ((type *)calloc((qty), sizeof(type))) which I have seen in several programs. Dave Schmidt