Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!rutgers!mailrus!tut.cis.ohio-state.edu!cs.utexas.edu!csd4.milw.wisc.edu!uxc.cso.uiuc.edu!tank!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c Subject: Re: "do ... while ((NULL + 1) - 1);" -- valid C? Message-ID: <18996@mimsy.UUCP> Date: 11 Aug 89 09:01:17 GMT References: <1043@levels.sait.edu.au> <961@virtech.UUCP> <10684@smoke.BRL.MIL> <940@lakesys.UUCP> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 111 In article <940@lakesys.UUCP> chad@lakesys.UUCP (D. Chadwick Gibbons) writes: >... In most decent implementations, NULL is indeed defined as either >0 or 0L. Right. >But this can't be, and isn't, true in all implementations, No and yes: it could be, but it is not. >... In many current implmentations, NULL is often defined as ((char *)0) >since it is the only "safe" thing to do [meaning `the only way the vendor >can keep the authors of bad code happy']. This is both unsafe and wrong, even if it does keep such authors happy. Consider: If we write char *cp; int *ip; ip = cp; the compiler must issue some kind of diagnostic (it says so in the proposed ANSI C specification, and it says in K&R-1 that this operation is machine-dependent, and all quality compilers do indeed generate a warning). This situation does not change if we write ip = (char *)ip; It does change if we write instead ip = (int *)(char *)ip; which puts the value in ip through two transformations (from pointer-to-int to pointer-to-char, then from pointer-to-char to pointer-to-int), and these two together are required to reproduce the original value (this is something of a special case). So: consider what happens if some implementer has wrongly put the line #define NULL ((char *)0) inand and so forth, and we write ip = NULL; The compiler sees ip = ((char *)0); which, as far as the type system is concerned, is identical to ip = cp; ---that is, it is machine dependent, and requires a warning. We can (probably) eliminate the warning% by adding a cast: ip = (int *)NULL; which expands to ip = (int *)((char *)0); On *most* machines, this `just happens' to work. But if we look very closely at the language definition, we find that it is not *required* to work. The version of this that is required to work is instead ip = (int *)(char *)(int *)0; We are not allowed (outside of machine-dependent code) to change a pointer-to-char into a pointer-to-int unless the pointer-to-char itself came into existence as the result of a cast from a pointer-to-int. The only way to *create* a nil-pointer-to-int in the first place is to write (int *)0, or (in the proposed ANSI C) (int *)(void *)0. Of course, the actual definition of NULL in and and so on is provided per machine, so if int *ip = (int *)(char *)0; `just happens' to work on that machine, the vendor could get away with it. But int *ip = NULL; is guaranteed to work *without* generating warnings on any machine where NULL is correctly defined, and one should not have to write int *ip = (int *)NULL; just to avoid getting warnings---nor should the compiler be silent about code like int *ip; char *cp; ip = cp; The rest of <940@lakesys.UUCP> is correct. ----- % The (probably) in eliminating warnings refers to the fact that a compiler can warn about anything it pleases: % cc -o foo foo.c cc: Warning: relative humidity and barometer pressure indicate that thunderstorms are likely cc: Warning: your shoelace is untied cc: Warning: this code looks ugly cc: Warning: your mother wears army boots cc: Warning: Hey! Keep away from me with that axe! cc: Warning: Ack! No, wait, I di(*&1to01llk -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris