john@orion.UUCP (10/05/84)
[] Here's a little *trick* that I picked up today to calculate offsets of elements in structures. #define OFFSET(X, Y) ((struct X *)0)->Y e.g. offset for an integer field -- &OFFSET(mystruct, anyint) offset for a character array -- OFFSET(mystruct, arrayname) Although there are more readable ways to write this using +/- and & I reckon its kinda neat. Also from the same source.... e.g. to rename files with suffix .n without suffix for i in *.n do ifs=$IFS; IFS=.; set $i; IFS=$ifs cp $i $1 done orion!john
guido@mcvax.UUCP (Guido van Rossum) (10/10/84)
Watch out! I did a similar thing to calculate the width of a structure: (int) ((struct foo *)0 + 1) This would give me the sizeof a struct foo, ROUNDED UP to the necessary alignment. It worked fine, until one day, I ported my program to an IBM PC with a Lattice C compiler. There it would always evaluate to 1 (apparently the compiler cancelled the two casts against each other). -- Guido van Rossum, "Stamp Out BASIC" Committee, CWI, Amsterdam guido@mcvax.UUCP "Immorality may be something, but it does not take the place of virtue and three square meals a day."
ron@brl-tgr.ARPA (Ron Natalie <ron>) (10/10/84)
This is useful except on the VAX it don't work for bit fields (now
before you scream, let me demonstrate...)
sturct foo {
int x;
char a:4, b:4;
char c;
};
You can't find the offset of the char that contains a and b.
Just another reason for not using bitfields, I guess.
-Ron
jdb@mordor.UUCP (John Bruner) (10/11/84)
A problem with expressions like &((foo *)0)->bar (which I confess I have used myself) is that (foo *)0 is NOT a pointer whose value is zero, it is a special case -- the NULL pointer. The two are identical only if your machine represents NULL as an integer zero. If your machine has a special representation for NULL/nil pointers (e.g. if it is a tagged architecture with a special NULL/nil pointer tag) then (foo *)0 and (int)0 are distinct. The above expression is based upon the use of (foo *)0 as a pointer to zero, not as the NULL pointer. I believe that the above expression should be an illegal attempt to perform address calculation using NULL. Consider the following "equivalent" code sequence on a machine for which NULL is not represented as an integer zero; the address calculation here will blow up, since "x" is assigned the NULL pointer, not a pointer to zero: foo *x; x = (foo *)0; ... = &x->bar; -- John Bruner (S-1 Project, Lawrence Livermore National Laboratory) MILNET: jdb@mordor.ARPA [jdb@s1-c] (415) 422-0758 UUCP: ...!ucbvax!dual!mordor!jdb ...!decvax!decwrl!mordor!jdb
guy@rlgvax.UUCP (Guy Harris) (10/12/84)
> Watch out! I did a similar thing to calculate the width of a structure: > > (int) ((struct foo *)0 + 1) > > This would give me the sizeof a struct foo, ROUNDED UP to the necessary > alignment. It worked fine, until one day, I ported my program to an > IBM PC with a Lattice C compiler. There it would always evaluate to 1 > (apparently the compiler cancelled the two casts against each other). That's a broken compiler. "(struct foo *)0" is of type "pointer to 'struct foo'", so adding 1 to it should make it point to the "next" object of type "struct foo". Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
kpmartin@watmath.UUCP (Kevin Martin) (10/12/84)
>Watch out! I did a similar thing to calculate the width of a structure: > > (int) ((struct foo *)0 + 1) > >This would give me the sizeof a struct foo, ROUNDED UP to the necessary >alignment. > Guido van Rossum > guido@mcvax.UUCP First off, if your 'sizeof' operator doesn't give you the size already rounded up to a multiple of the required alignment, you're in trouble! Second, on some hardware, casting a pointer to an integeral type *DOESN'T GIVE A BYTE INDEX FROM BYTE ZERO*! On a Honeywell Level 66 (or a 6000 or a DPS8 or a DPS88...), the above expression would give the value 01000000 (if sizeof(struct foo) == sizeof(int) which is 4 bytes). A better expression, which is a bit closer to legit (but still subject to compiler bugs) is: (char *)((struct foo *)0 + 1) - (char *)0 Or, for getting the offset of a struct element, (char *)(&((struct foo *)0)->element) - (char *)0 The two reasons that these still aren't legit are: (1) They should really use NULL, rather than 0 (but this is just being picky) (2) It is not at all clear that any operation on a NULL pointer other than comparison and asignment is allowed. The above code does a pointer+int, then a pointer difference, both using NULL pointers. Kevin Martin, UofW Software Development Group
keesan@bbncca.ARPA (Morris Keesan) (10/13/84)
>That's a broken compiler. "(struct foo *)0" is of type "pointer to 'struct >foo'", so adding 1 to it should make it point to the "next" object of type >"struct foo". > > Guy Harris > As John Bruner has already pointed out, this is not at all broken behavior on the part of the compiler. Section 7.14 of the C Reference Manual (p. 192 in K&R) says, " . . . it is guaranteed that assignment of the constant 0 to a pointer will produce a null pointer distinguishable from a pointer to any object." This means that although "(struct foo *)0" is indeed of type "pointer to 'struct foo'", it is guaranteed not to point to any object, so it is meaningless to refer to the "next" object. -- Morris M. Keesan {decvax,linus,ihnp4,wivax,wjh12,ima}!bbncca!keesan keesan @ BBN-UNIX.ARPA
lambert@mcvax.UUCP (Lambert Meertens) (10/13/84)
> >That's a broken compiler. "(struct foo *)0" is of type "pointer to 'struct >foo'", so adding 1 to it should make it point to the "next" object of type >"struct foo". In general, we can hope to find the `array offset' of a struct by (int)((struct foo*)N+1) - (int)(struct foo*)N If N = 0, this can be simplified to (int)((struct foo*)0+1). But there is something special about casting 0 to a pointer: the result does not point, so the notion of `next object pointed to' is ill defined. Now what if N != 0? As far as I can see, it is impossible to *deduce* from K&R that the result is not simply 1. Assume, for example, a compiler that ensures that values whose size in bytes is S are always aligned on a multiple of P(S), where P(S) is a power of two that is at least S. This is, admittedly, silly, but not forbidden. Now the internal addresses of S-sized values are all of the form N*P(S), and the code could then use N as the internal representation of the pointer. The code for ++ is then simply: add 1 to the integer representing the pointer. For pointer following, the code should multiply the pointer N by P(S), where S is the size of the type of objects pointed to (which looks silly but might be cheap on the hardware). Casts to int are now supercheap: internally they are the identity function. The above expression evaluates then to 1. With this scheme, one cannot guarantee that (int)(struct foo*)p = (int)(struct bar*)p but I do not see how this identity would follow from the C scriptures. Lambert Meertens ...!{seismo,philabs,decvax}!lambert@mcvax.UUCP CWI (Centre for Mathematics and Computer Science), Amsterdam -- Lambert Meertens ...!{seismo,philabs,decvax}!lambert@mcvax.UUCP CWI (Centre for Mathematics and Computer Science), Amsterdam
kendall@wjh12.UUCP (Sam Kendall) (10/13/84)
> A better expression, which is a bit closer to legit ... > ... for getting the offset of a struct element, > (char *)(&((struct foo *)0)->element) - (char *)0 > ... > (2) It is not at all clear that any operation on a NULL pointer other than > comparison and asignment is allowed. The above code does a pointer+int, > then a pointer difference, both using NULL pointers. > Kevin Martin, UofW Software Development Group (2) is correct because, it seems to me, if the reference manual defines no operations on a null pointer besides copying, equality comparison, and casting, then no other operations make sense (looking only at the language rather than at implementations of it). The expression can be made portable, at least under UNIX, by having extern end; somewhere in a header file, and then replacing "0" in the expression by "&end". For maximum portability, of course, use a real object of type struct foo in the expression. Sam Kendall {allegra,ihnp4,ima,amd}!wjh12!kendall Delft Consulting Corp. decvax!genrad!wjh12!kendall
guy@rlgvax.UUCP (Guy Harris) (10/14/84)
> >That's a broken compiler. "(struct foo *)0" is of type "pointer to 'struct > >foo'", so adding 1 to it should make it point to the "next" object of type > >"struct foo". > > > > Guy Harris > > > > As John Bruner has already pointed out, this is not at all broken behavior on > the part of the compiler. Section 7.14 of the C Reference Manual (p. 192 in > K&R) says, " . . . it is guaranteed that assignment of the constant 0 to a > pointer will produce a null pointer distinguishable from a pointer to any > object." This means that although "(struct foo *)0" is indeed of type > "pointer to 'struct foo'", it is guaranteed not to point to any object, so it > is meaningless to refer to the "next" object. Yes, but would you trust any compiler that optimized (int) ((struct foo *)0 + 1) into 1 not to optimize (int) ((struct rp11_regs *)177550 + 1) into 177551? At best, it may just be optimizing (0 of any kind) + N into N; it should still check the kind of 0 before it forges ahead with this optimization. Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/14/84)
The propagation of typecasts prematurely into expressions is an old PCC bug fixed years ago when the "repainting" stuff was added. It would sure be nice if people would start with the latest version of software when they do their adaptations.
gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/14/84)
As has been discussed before, a 0 pointer in C is not appropriately mapped to a special nil pointer in tagged architectures.
guy@rlgvax.UUCP (Guy Harris) (10/15/84)
> As has been discussed before, a 0 pointer in C is not > appropriately mapped to a special nil pointer in tagged > architectures. Do you mean "(existing) implementations of C on tagged architectures do not perform the appropriate mapping of a 0 pointer to a special nil pointer", or "it is not appropriate to map a 0 pointer to a special nil pointer in a tagged architecture?" If the latter, I disagree; there is no committment to the bit pattern of a 0 pointer in C, and no committment to being able to dereference it. As such, I see no reason not to map a 0 pointer onto bit pattern you want, as long as 1) it's distinct from *all* bit patterns for legitimate pointers in C (i.e., your C implementation must make sure no C function or variable has an address with that bit pattern) and 2) it fits in the same number of bits as any other pointer. Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
chris@umcp-cs.UUCP (Chris Torek) (10/15/84)
It would be interesting to make a version of the VAX compilers that map NIL pointers to 0x80000000 (or 0xffffffff). Whee! -- (This mind accidently left blank.) In-Real-Life: Chris Torek, Univ of MD Comp Sci (301) 454-7690 UUCP: {seismo,allegra,brl-bmd}!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@maryland
geoff@desint.UUCP (Geoff Kuenning) (10/16/84)
> The expression can be made portable, at least under UNIX, by >having > extern end; >somewhere in a header file, and then replacing "0" in the expression by >"&end". For maximum portability, of course, use a real object of type >struct foo in the expression. > Sam Kendall (What Sam is talking about would then read like this:) > (char *)(&((struct foo *)&end)->element) - (char *)&end Good idea, except am not sure &end is the best symbol to use. Is &end actually guaranteed to refer to a legal address by itself? What about the other "&e" symbols: etext and edata? Is it legal is we replace "&end" with "(&end-sizeof (struct foo))"? -- Geoff Kuenning First Systems Corporation ...!ihnp4!trwrb!desint!geoff
gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/16/84)
I don't want to repeat all the earlier arguments, but consider: A null pointer can be produced legally by arithmetic operations, as well as by hard-coded (foo *)0 constants in the source. To use a special nil value would require run-time checks to be applied to much pointer arithmetic. This is contrary to the intent of C.
guy@rlgvax.UUCP (Guy Harris) (10/17/84)
> I don't want to repeat all the earlier arguments, but consider: > A null pointer can be produced legally by arithmetic operations, > as well as by hard-coded (foo *)0 constants in the source. To > use a special nil value would require run-time checks to be applied > to much pointer arithmetic. This is contrary to the intent of C. If by "produced legally by arithmetic operations", you mean char *foo; int bar; foo = (bar - bar); that's false. When K&R refers to null pointers, it always refers to "the constant 0", specifically excluding anything but the number "0" in a source program (or #define constant expanding to that number): 7.14 Assignment operators ...However, it is guaranteed that assignment of the constant 0 to a pointer will produce a null pointer distinguishable from a pointer to any object. and 7.7 Equality operators A pointer may be compared to an integer, but the result is machine dependent unless the integer is the constant 0. Converting "an object of integral type" to a pointer, and back to an integer, "always carries an integer converted form a pointer back into the same pointer", but that merely means that an integer *variable* having the value 0 must map into a pointer which, when mapped into an integer, maps into the value 0. This can be implemented by an bit-for-bit identity mapping of pointers to integers and vice versa (BTW, I'd be the first to admit that not only our 16-bit "int", 32-bit pointer 68000 C compiler, but AT&T's 68000 C compiler when built with 16-bit "int"s, and every other 16-bit "int", 32-bit pointer C compiler, violates this; it implies that an "int" must have at least as many distinct values as a pointer). On a machine on which a null pointer had some special non-zero bit value, this would mean that char *p, *q; int foo; p = 0; q = foo - foo; if (p != q) printf("Not equal\n"); would print "Not equal". So what? As stated above, the C Reference Manual refers to null pointers as being conversions of *the constant* 0 to a pointer value; since the only integer value which has to be converted to a null pointer is a constant, there's no need for any run-time checks. Frankly, I think a C implementation with all-zero null pointers on a machine which strongly encouraged you to use some special non-zero pattern for null pointers would violate C's charter much more; it would mean the C implementation wasn't as close to the machine as it could be. If the "native" implementation language on the machine wasn't C, you'd have a real pain interfacing to it. Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
ron@brl-tgr.ARPA (Ron Natalie <ron>) (10/17/84)
> The expression can be made portable, at least under UNIX, by > having > extern end; > somewhere in a header file, and then replacing "0" in the expression by > "&end". For maximum portability, of course, use a real object of type > struct foo in the expression. FOO! WRONG! On non-byte addressed machines this isn't guaranteed to work either. -Ron
gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/18/84)
If you want to take issue with the producibility of a 0 pointer through arithmetic operations, please do not invent incorrect examples and attribute them to me, then shoot them down. Thanks.
gwyn@brl-tgr.UUCP (10/18/84)
Relay-Version: version B 2.10 5/3/83 based; site houxm.UUCP Posting-Version: version B 2.10.2 9/18/84; site brl-tgr.ARPA Message-ID: <5380@brl-tgr.ARPA> Date: Thu, 18-Oct-84 15:12:29 EDT Date-Received: Fri, 19-Oct-84 06:31:23 EDT <204@rlgvax.UUCP> Organization: Ballistic Research Lab Lines: 4 If you want to take issue with the producibility of a 0 pointer through arithmetic operations, please do not invent incorrect examples and attribute them to me, then shoot them down. Thanks.
guy@rlgvax.UUCP (Guy Harris) (10/19/84)
> If you want to take issue with the producibility of a 0 pointer > through arithmetic operations, please do not invent incorrect > examples and attribute them to me, then shoot them down. Thanks. You did not cite any examples; I was postulating the only one I could think of. If you think a pointer which must have the same bit pattern as a null pointer can be produced by any expression other than (something *)0 please exhibit it; my reading of K&R shows no such expression. Thanks. Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
guy@rlgvax.UUCP (Guy Harris) (10/19/84)
Arithmetic expressions that produce pointers: 1) Purely integer expressions: As discussed in my previous article, K&R indicates that no such expression, except a constant 0, is to be interpreted as a null pointer. The phrase "the constant 0" appears in several places (in the discussion of the conditional operator, as well as the places mentioned in my previous article); I do not think that the modifier "the constant" appears by accident. I believe it was explicitly put there to indicate that an arbitrary integral result of zero need not be converted into a null pointer; only an explicit zero constant need be so converted. If somebody has a statement to the contrary, from either K or R, they should exhibit it. 2) Pointer plus or minus an integer expression: The actual phrase in "7.4 Additive operators" reads A pointer *to an object in an array* and a value of any integeral type may be added. The latter is in all cases converted to an address offset by multiplying it by the length of the object to which the pointer points. The result is a pointer of the same type as the original pointer, and which points to another object in the same array, appropriately offset from the original object. A null pointer does not point to any object in an array. If you add an integer to a pointer, by the paragraph above the resulting pointer points to an object in an array. Therefore, it is not a null pointer. I am quite aware that if you have a pointer to an element in a character array on a PDP-11, and the element has the address 0177777, adding one to that pointer yields the result 0. This is not an argument that you can produce a null pointer by an arithmetic expression. First of all, arrays move forward in memory, so there *is* no next element in that array, as the element in question is at the end of your address space. Second of all, if you have a machine on which a null pointer does not have the value zero, and you add 1 to a pointer whose value is such that adding 1 to it will cause wrap-around, you have still not produced a null pointer. You may have produced a pointer that doesn't point where it "should", and which may even to a non-existent part of the address space, but that does not mean it must be a null pointer. 3) Other expressions: Under 14.4, "Explicit pointer conversions", it says Certain conversions involving pointer are permitted *but have implementation-dependent aspects.... ...An object of integeral type may be explicitly converted to a pointer. The mapping always carries an integer converted from a pointer back to the same pointer, but is otherwise machine dependent. This implies that if you convert a null pointer to an integer, the integer that results must convert back into a null pointer. The most natural and "unsurprising" conversion (see the previous paragraph in section 14.4 on conversions from pointer to integer) is just a bitwise copy. If converting a null pointer produces an integer with the value 0xff000000, so be it. If that's how a null pointer is represented internally, I'd find conversion of a null pointer into a zero integer more surprising than conversion of it into 0xff000000. Given that, converting an integer back into a pointer by a bitwise copy would be the natural way to do it; this would convert an integer value of 0, other than a constant 0 (which is *not* an integer converted from a pointer), into a pointer with the value 0, not a null pointer, and would convert an integer with the value 0xff000000 into a null pointer. Yes, this implies that it's a pain to produce a pointer which points to location 0. It even implies that producing a pointer which points to location 0 can't be done the same way you produce a pointer which points to location 1; you'd have to say something *p; int i; p = (i - i); Worse things have happened. It may be a pain to produce such a pointer, but it's not impossible, and it's not *that* common an operation. So what sort of arithmetic expressions are left? I do rescind my earlier statement that 16-bit "int"s and 32-bit pointers are illegal. The statement that "(the integer-to-pointer mapping) always carries an integer converted from a pointer back into the same pointer" does not imply that an "int" must be big enough to hold a pointer. It merely implies that there must be an *integral type* big enough to hold a pointer; "int" is not the largest integral type, just the most "natural" type. "Natural" is not a precise specification; it implies that the choice of size of "int" is machine dependent. Of course, what is most "natural" given the data path width of the machine isn't necessarily the most "natural" given the size of objects you can put on the machine; try using "malloc" and "realloc" to grow a symbol table past 64K on a machine with 16-bit "int"s but 32-bit pointers. (It can't be done in a straightforward fashion. Believe me. We have such a machine, and we've *tried*. The standard UNIX "nm" uses that technique, and if your symbol table is bigger than 64K bytes, you lose.) So if you have 32-bit "int"s, you can't convert the pointer with the bit pattern 0x801234 into an "int" and back and get the same value back, but you can convert it to the integral type "long" and back; as it says in section 14.4, paragraph 2, A pointer may be converted to any of the integral types *large enough to hold it. Whether an "int" or "long" is required is machine dependent.* (italics mine) However, it does state specifically that the difference between two pointers is an "int", not just an integral value. (We don't do that. *Nostra culpa* - not "*mea culpa*"; it wasn't my idea. Our newer systems will bite the bullet and have 32-bit "int"s, mainly for compatibility with our 32-bit supermini, but also because they're 4.2BSD-based, and there's probably several *months* of work changing 4.2BSD to use "long" instead of "int" when it means "32-bit quantity". I assume the AT&T 68000 C compiler gets this right, when built for 16-bit "int"s.) Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
ron@brl-tgr.ARPA (Ron Natalie <ron>) (10/19/84)
> >A problem with expressions like > > > > &((foo *)0)->bar > > ... > > Perhaps more robust would be ((char *)&((foo *)0)->bar - (char *)(foo *)0) > > so that, regardless of the value of ((foo *)0), all that is expected of the > compiler is that &((foo *)0)->bar be produced by adding the offset to bar > to the value of ((foo *)0). There is still the presumption that any pointer > can be converted to (char *) without loss of information. But nowhere does the spec say that saying 0->anything or *(pointer_cast)0 is guaranteed to valid at all. Zero can be put into a pointer so that it can be checked for later, period. On certain architectures (like ones that have different pointer layouts for various types) trying to 0-> something may not be valid. Consider the HEP: The pointers work as follows: Char SHORT Medium int (and long) 0 1 2 0 1 2 3 3 4 5 6 5 6 7 7 8 8 In these cases something that begins with short or medium would have a zero pointer of 1 or 2 even though the compiler traps the special NULL (0) case and handles it.
gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/19/84)
Although I see no particular use for the following piece of code, I believe that it is supposed to be legal both now and in the ANSI standard: char *foo; foo -= (long)foo; /* or (int) perhaps */ /* foo now is NULL */ More importantly, char *malloc(), *where; where = malloc( (unsigned)40 ); subr( where ); /* check validity of `where' and does something */ Here a NULL pointer must be type-compatible with other pointers. Using a special "nil" pointer could easily get in the way here (subr() might end up with special-case "nil"-handling in-line code every time its parameter is used). What more does using special "nil" get you than using a 0, apart from hardware check against dereferencing NULL? In any case, the hardware check is unnecessary if you write your code correctly. Are we all hackers or are there some professional programmers out there?
gwyn@brl-tgr.UUCP (10/19/84)
Relay-Version: version B 2.10 5/3/83; site houxe.UUCP Posting-Version: version B 2.10.2 9/18/84; site brl-tgr.ARPA Message-ID: <5400@brl-tgr.ARPA> Date: Fri, 19-Oct-84 16:19:34 EDT <204@rlgvax.Fri, 19-Oct-84 13:19:34 PDT Organization: Ballistic Research Lab Lines: 26 Although I see no particular use for the following piece of code, I believe that it is supposed to be legal both now and in the ANSI standard: char *foo; foo -= (long)foo; /* or (int) perhaps */ /* foo now is NULL */ More importantly, char *malloc(), *where; where = malloc( (unsigned)40 ); subr( where ); /* check validity of `where' and does something */ Here a NULL pointer must be type-compatible with other pointers. Using a special "nil" pointer could easily get in the way here (subr() might end up with special-case "nil"-handling in-line code every time its parameter is used). What more does using special "nil" get you than using a 0, apart from hardware check against dereferencing NULL? In any case, the hardware check is unnecessary if you write your code correctly. Are we all hackers or are there some professional programmers out there?
guy@rlgvax.UUCP (Guy Harris) (10/20/84)
> What more does using special "nil" get you than using a 0, apart from > hardware check against dereferencing NULL? In any case, the hardware > check is unnecessary if you write your code correctly. Are we all > hackers or are there some professional programmers out there? Assuming you are handed an existing machine and an existing OS, libraries, and language tools with hardware and/or software conventions assigning a non-zero value to a null pointer, it gets you compatibility between a C implementation done for that machine and the other languages on that machine. If the hardware check is already there, one might as well use it. There's plenty of crap code out there - in standard versions of UNIX - that *does* dereference null pointers; hardware/software that catches it can help flush it out. We discovered this in our port to our 68000-based micro, which shoots down null pointer dereferences for reasons other than checking code. Also note that even the best programmers do not produce 100% perfect code every time; no amount of methodology or care can eliminate all errors, as the programmer could easily err in applying their checks as easily as they err in producing code. Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
henry@utzoo.UUCP (Henry Spencer) (10/22/84)
> Although I see no particular use for the following piece of code, > I believe that it is supposed to be legal both now and in the ANSI > standard: > > char *foo; > > foo -= (long)foo; /* or (int) perhaps */ > /* foo now is NULL */ This falls down if pointers are not byte indexes, e.g. if the byte info is up in some of the high-order bits. (I think the pdp10 does that.) Remember that the ptr--->long conversion is not required to produce a sensible number. There is also a more subtle assumption here, that memory is in fact organized as an array of bytes, so subtracting the "position" of a byte pointer from the pointer gives 0. (I can't immediately think of a counterexample, but it's not ruled out.) Finally, nowhere does the ANSI standard (or K&R) say that *any* computed pointer value, regardless of the computation, must compare equal to integer constant 0 (i.e. NULL). The only way to get a pointer that is guaranteed to compare equal to NULL is to derive it from an explicit NULL. > char *malloc(), *where; > > where = malloc( (unsigned)40 ); > > subr( where ); /* check validity of `where' and does something */ > > Here a NULL pointer must be type-compatible with other pointers. Using > a special "nil" pointer could easily get in the way here (subr() might > end up with special-case "nil"-handling in-line code every time its > parameter is used). Why? If subr() compares the pointer to an explicit NULL, then that comparison is done against the special "nil" value. If the comparison is against a pointer derived from an explicit NULL, same thing. If the comparison is against anything else, there are no promises made by the ANSI draft or K&R. The choice of bit pattern to represent NULL is entirely irrelevant. > What more does using special "nil" get you than using a 0, apart from > hardware check against dereferencing NULL? In any case, the hardware > check is unnecessary if you write your code correctly. Are we all > hackers or are there some professional programmers out there? Among other things, on some "smart" pieces of hardware, you cannot even load some bit patterns into a "pointer" register without getting a trap. On such hardware, you can't use such a bit pattern to represent NULL without all kinds of hassles when manipulating might-be-NULL pointers. On at least one such machine that I've heard of, all-zeros is one of the bad bit patterns. -- Henry Spencer @ U of Toronto Zoology {allegra,ihnp4,linus,decvax}!utzoo!henry