guy@gorodish.Sun.COM (Guy Harris) (06/08/88)
(The preceding article's contents represent a misunderstanding of the C language, rather than a valid statement of what an architecture should or should not have; I've therefore redirected followups to "comp.lang.c", although many readers there are probably tired of seeing these same misunderstandings....) > This presents a sequential search through a linear list more positively than > > #include <stdio.h> /*stupid place for NULL*/ > ... > Foobar *foobar; > for (foobar = first; foobar != (Foobar*)NULL; foobar = foobar->next) > { > } > > (For code to be truly portable, NULL must be cast to the appropriate pointer > type every time it is used since it is nothing more than an integer bit > pattern :-) If the smiley really means "I don't believe the statement that precedes the smiley", OK; however, the statement in question is completely false. Any valid C compiler will turn foobar != 0 into a comparison of "foobar" with the appropriate representation for a null pointer of type "pointer to Foobar". > Why not just adopt a pattern of all zeroes as NULL instead? Just ensure > that this convention is observed by your malloc. Then we can write > > if (func) > result = (*func)(); > > rather than using double negatives: > > if (func != (int(*)(void))NULL) > result = (*func)(); "if (func != (int(*)(void))NULL)" is equivalent to "if (func != NULL)", because the compiler will perform the type conversion. "if (func != NULL)" is equivalent to "if (func != 0)", because "NULL" is supposed to be defined as "0" (or "(void *)0", but the latter isn't really necessary). "if (func != 0)" is equivalent to "if (func)". Therefore, you can write "if (func)" instead of "if (func != (int(*)(void))NULL)" *regardless* of the representation of a null pointer. The confusion on this point probably results because C did not have function prototypes until some compiler vendors put them in in anticipation of ANSI C. Thus, while the compiler has enough information to know that NULL (or 0, it's the same thing) in "foobar = NULL" or "if (func != NULL)" should be converted to the appropriate pointer type, the compiler lacks enough information to do the type conversion in "setbuf(stdout, NULL)" - it doesn't know that the second argument to "setbuf" is "char *". Therefore, you have to tell the compiler to perform this conversion by explicitly casting the pointer: "setbuf(stdout, (char *)NULL)". If you have function prototypes in your C implementation, and have included the proper include files or have otherwise arranged that prototypes are in scope for all functions to which you refer, the compiler can do the conversion: void setbuf(FILE *stream, char *buf); ... setbuf(stdout, NULL); Please don't post a followup to this article unless you have good evidence that the C *language* doesn't specify that the appropriate conversions be done in the examples given. Thank you.
jeffa@hpmwtla.HP.COM (Jeff Aguilera) (06/18/88)
ANSI C is not C. Prototypes do not exist in C. Please show me where in K&R that it states that "0" refers to the NULL pointer irrespective of the underlying implementation. Mr. Wells, if "0" does refer to the null pointer, why do some systems have #define NULL (-1) in stdio? My statement regarding casting is correct, since not all pointers need be of the same size. Prototypes eliminate this annoyance, but I live with a compiler void of prototypes :-( Jeff "still wondering when C will be as clean as Algol-68" Aguilera
jgk@speech2.cs.cmu.edu (Joe Keane) (06/20/88)
In article <3100003@hpmwtla.HP.COM> jeffa@hpmwtla.HP.COM (Jeff Aguilera) writes: >ANSI C is not C. Prototypes do not exist in C. Please show me where in >K&R that it states that "0" refers to the NULL pointer irrespective of the >underlying implementation. Mr. Wells, if "0" does refer to the null >pointer, why do some systems have #define NULL (-1) in stdio? My >statement regarding casting is correct, since not all pointers need be >of the same size. Prototypes eliminate this annoyance, but I live with >a compiler void of prototypes :-( Please, not this again! Any system with `#define NULL (-1)' is so broken it's pitiful. Code in my previous posting must work right. >Jeff "still wondering when C will be as clean as Algol-68" Aguilera --Joe "enough about NULL" Keane
darrylo@hpsrli.HP.COM (Darryl Okahata) (06/20/88)
In comp.arch, jeffa@hpmwtla.HP.COM (Jeff Aguilera) writes: > ANSI C is not C. Prototypes do not exist in C. Please show me where in > K&R that it states that "0" refers to the NULL pointer irrespective of the Pages 97-98 of K&R, first edition: "We write NULL instead of zero, however, to indicate more clearly that this is a special value for a pointer. In general, integers cannot meaningfully be assigned to pointer; zero is a special case." For the second edition of K&R ("ANSI C"), a passage similar to the above appears somewhere around page 102. > underlying implementation. Mr. Wells, if "0" does refer to the null > pointer, why do some systems have #define NULL (-1) in stdio? My Personally, I think that ANY "C" compiler that does not assign 0 (or even 0L - yuk) to NULL is busted. Using a compiler that defines NULL as (-1), try porting any program that appears in comp.sources.*. You'll find that it's a nightmare, as almost ALL C programs make the assumption that NULL is 0. [ ... ] > Jeff "still wondering when C will be as clean as Algol-68" Aguilera OK, this topic has been brought up for the N-th to the i-th time. Time to kill it. -- Darryl Okahata {hplabs!hpccc!, hpfcla!} hpsrla!darrylo CompuServe: 75206,3074 Disclaimer: the above is the author's personal opinion and is not the opinion or policy of his employer or of the little green men that have been following him all day.
hoefling@uicsrd.csrd.uiuc.edu (06/21/88)
"The C Programming Language", Kernighan and Ritchie, 1978, page 192: Appendix A, section 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."
guy@gorodish.Sun.COM (Guy Harris) (06/21/88)
> ANSI C is not C. Prototypes do not exist in C. Please show me where in > K&R that it states that "0" refers to the NULL pointer irrespective of the > underlying implementation. 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. A pointer to which 0 has been assigned is guaranteed not to point to any object, and will appear to be 0; in conventional usage, such a pointer is considered to be null. 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. Next question. > ...if "0" does refer to the null pointer, why do some systems have #define > NULL (-1) in stdio? Because the implementors were ignorant. Any language implementation that defines NULL as -1, and purports to be an implementation of C, is broken. Next question. > My statement regarding casting is correct, since not all pointers need be > of the same size. Prototypes eliminate this annoyance, but I live with > a compiler void of prototypes :-( Your statement regarding casting was: > #include <stdio.h> /*stupid place for NULL*/ > ... > Foobar *foobar; > for (foobar = first; foobar != (Foobar*)NULL; foobar = foobar->next) > { > } > (For code to be truly portable, NULL must be cast to the appropriate pointer > type every time it is used since it is nothing more than an integer bit > pattern :-) which is absolutely, positively, INcorrect. The cast is totally unnecessary in the "for" loop in question; the compiler is quite capable of figuring out that 0 or NULL must be converted to a null pointer of type "Foobar *" before comparing it, and all valid C compilers will do so (if you know of a compiler that does not, it is invalid - period). Furthermore, as there are no function calls whatsoever in your example, prototypes have no effect. The ONLY place where you are required to cast NULL properly is when passing arguments to a procedure; prototypes eliminate that unless you have a function (such as "execl") that takes a variable number of arguments or otherwise cannot have its calling sequence fully described by a prototype. Any valid compiler will perform the required conversion automatically in all other cases (assignments and comparisons, as described above).
scott@award.UUCP (Scott Smith) (06/22/88)
In article <3100003@hpmwtla.HP.COM> jeffa@hpmwtla.HP.COM (Jeff Aguilera) writes: >ANSI C is not C. Prototypes do not exist in C. Please show me where in >K&R that it states that "0" refers to the NULL pointer irrespective of the >pointer, why do some systems have #define NULL (-1) in stdio? Actually K&R does, p97: "C guarantees that no pointer that validly points at data will contain zero, so a return value of zero can be used to signal an abnormal event... We write NULL instead of zero, however, to indicate more clearly that this is a special value for a pointer." The reason some systems have #define NULL (-1) in stdio is becuase zero *can* be a pointer to valid data (as is the case in my micro). In this case, NULL is simply changed to be a value that can't be a valid pointer on that particular system. S. Smith
chris@mimsy.UUCP (Chris Torek) (07/01/88)
>In article <3100003@hpmwtla.HP.COM> jeffa@hpmwtla.HP.COM (Jeff Aguilera) asks: >>why do some systems have #define NULL (-1) in stdio? In article <743@award.UUCP> scott@award.UUCP (Scott Smith) answers: >K&R p97: "C guarantees that no pointer that validly points >at data will contain zero" ... (A better place is the appendix, which is more explicit.) >The reason some systems have #define NULL (-1) in stdio is becuase zero >*can* be a pointer to valid data (as is the case in my micro). In this >case, NULL is simply changed to be a value that can't be a valid pointer >on that particular system. Or, to simplify it: Some systems have `#define NULL (-1)' in <stdio.h> because some systems are broken. If location zero is a valid data address, the compiler must take care to ensure that either nil pointers are not the all-zero bit pattern, or that something which is never accessed from C is stored in location zero. Given the C code main() { char *p = 0; if (p) *p = 0; } the following pseudo-assembly is legal and correct: main_:: create_frame move #0xff00,-4(stack) | p = (char *)0 move -4(stack),r0 cmp r0,#0xff00 | if (p) branch notequal,$1 move #0,0(r0) | *p = 0 $1: destroy_frame return Notice how pointer assignments and comparisons with `0' turn into assignments and comparisons with a magic nil pointer. Whether that nil pointer's value is in fact zero is not determined, but IN THE SOURCE LANGUAGE THAT NIL POINTER IS WRITTEN AS IF IT WERE THE INTEGER ZERO. (The dpANS also allows (void *)0.) ----------------------------------------------------------------------- In C, NULL may or may not be in fact zero, but it is *written* as zero. ----------------------------------------------------------------------- (Nil pointers also have types, and cannot be correctly discussed without also mentioning their types, but this is the important part.) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
guy@gorodish.Sun.COM (Guy Harris) (07/01/88)
> The reason some systems have #define NULL (-1) in stdio is becuase zero > *can* be a pointer to valid data (as is the case in my micro). In this > case, NULL is simply changed to be a value that can't be a valid pointer > on that particular system. Only if the implementor was ignorant, in which case they shouldn't be trying to implement C without having somebody around who *does* know the language. You don't have to guarantee that that location is inaccessible, you merely have to guarantee that no object that a C program can refer to is at that address. If "malloc" will never return a pointer to an object at location 0, and if no static or global data or code defined in a C program will ever be put at location 0, and if no data object on the stack will be at location 0, this is sufficient. If it is truly impossible to arrange for this to happen, what you can do is: 1) define the format of a null pointer to be, say, 0xFFFF; 2) have the C compiler properly handle null pointers. Thus, the C code char *p; p = 0; would generate machine code something like move.i #ffff,p (i.e., store the bit pattern 0xFFFF into "p"), and the C code char *p; if (p == 0) or char *p; if (!p) would generate machine code something like cmp.i #ffff,p bne 1f (code inside "if") 1: (i.e., compare "p" against the bit pattern 0xFFFF; if it's equal, the pointer is "equal to zero", or null). Given that, NULL should be defined as 0, not -1.
bsy@PLAY.MACH.CS.CMU.EDU (Bennet Yee) (09/28/88)
.... blithering about universal intermediate languages and cost for checking every pointer reference to enforce *NULL == NULL .... Of course, if your machine core dumps on dereferencing NULL, just trap the fault (SIGSEGV), check the instruction that caused the trap, and patch up and return if it was due to dereferencing NULL; otherwise dump core. If we presume that most pointers dereferenced are not NULL, then the run-time costs are small. It's a very machine dependent solution, but certainly can be done on any Unix machine. -bsy -- Internet: bsy@cs.cmu.edu Bitnet: bsy%cs.cmu.edu%smtp@interbit CSnet: bsy%cs.cmu.edu@relay.cs.net Uucp: ...!seismo!cs.cmu.edu!bsy USPS: Bennet Yee, CS Dept, CMU, Pittsburgh, PA 15213-3890 Voice: (412) 268-7571
blarson@skat.usc.edu (Bob Larson) (09/28/88)
In article <3112@pt.cs.cmu.edu> bsy@PLAY.MACH.CS.CMU.EDU (Bennet Yee) writes: >.... >blithering about universal intermediate languages and cost for checking >every pointer reference to enforce *NULL == NULL >.... > >Of course, if your machine core dumps on dereferencing NULL, just trap the >fault (SIGSEGV), check the instruction that caused the trap, and patch up >and return if it was due to dereferencing NULL; otherwise dump core. This is a possible solition to running software that assumes NULL can be dereferenced on a machine that doesn't allow this, but it still leaves the problem of getting software that assumes that a NULL pointer dereference WILL cause an exception to run on machine that does allow NULL to be dereferenced. (Obviously, with much overhead, it can be checked at every pointer dereference.) -- Bob Larson Arpa: Blarson@Ecla.Usc.Edu blarson@skat.usc.edu Uucp: {sdcrdcf,cit-vax}!oberon!skat!blarson Prime mailing list: info-prime-request%ais1@ecla.usc.edu oberon!ais1!info-prime-request
chip@ateng.ateng.com (Chip Salzenberg) (09/30/88)
According to bsy@PLAY.MACH.CS.CMU.EDU (Bennet Yee): >Of course, if your machine core dumps on dereferencing NULL, just trap the >fault (SIGSEGV), check the instruction that caused the trap, and patch up >and return if it was due to dereferencing NULL; otherwise dump core. It is not not always possible to restart faulted instructions. The 68000, for example, is not capable of restarting an instruction after a bus fault. This problem has been fixed in the 68010 and all its successors; but if an intermediate language doesn't work on the 68000, it can hardly be called "universal". -- Chip Salzenberg <chip@ateng.uu.net> or <uunet!ateng!chip> A T Engineering My employer may or may not agree with me. The urgent leaves no time for the important.
thomas@uplog.se (Thomas Hameenaho) (10/03/88)
In article <3112@pt.cs.cmu.edu> bsy@PLAY.MACH.CS.CMU.EDU (Bennet Yee) writes: >Of course, if your machine core dumps on dereferencing NULL, just trap the >fault (SIGSEGV), check the instruction that caused the trap, and patch up >and return if it was due to dereferencing NULL; otherwise dump core. If we >presume that most pointers dereferenced are not NULL, then the run-time >costs are small. It's a very machine dependent solution, but certainly can >be done on any Unix machine. This problem should be easy to solve hadn't it been the case that many Unix kernels are defunct when it comes to using the a.out header. Our Pyramid (hint hint!) dumps core if the text segment doesn't start on virtual address 0! The sysV 68020 machines we have can correctly use the header. You can for instance have the entry point at address 4 and have a 0 at address 0 if you just want to be able to run the code as is. Or if you prefer you can skip page 0 and trap any references to 0 and once and for all fix the offending code. -- Real life: Thomas Hameenaho Email: thomas@uplog.{se,uucp} Snail mail: TeleLOGIC Uppsala AB Phone: +46 18 189406 Box 1218 Fax: +46 18 132039 S - 751 42 Uppsala, Sweden
peter@ficc.uu.net (Peter da Silva) (10/03/88)
In article <1988Sep29.141726.14882@ateng.ateng.com>, chip@ateng.ateng.com (Chip Salzenberg) writes: > According to bsy@PLAY.MACH.CS.CMU.EDU (Bennet Yee): > >Of course, if your machine core dumps on dereferencing NULL, just trap the > >fault (SIGSEGV), check the instruction that caused the trap, and patch up > >and return if it was due to dereferencing NULL; otherwise dump core. No no, no. If the program traps on dereferencing NULL you pack it up and send it back to the vendor. This is a *bug*. Even if it's safe on some machines it's still a bug. And for the other case, I can't imagine anyone actually writing code that depended on *NULL being illegal... -- Peter da Silva `-_-' Ferranti International Controls Corporation. "Have you hugged U your wolf today?" peter@ficc.uu.net
radford@.ucalgary.ca (Radford Neal) (10/04/88)
In article <3112@pt.cs.cmu.edu>, bsy@PLAY.MACH.CS.CMU.EDU (Bennet Yee) writes: > ..... > blithering about universal intermediate languages and cost for checking > every pointer reference to enforce *NULL == NULL > ..... > > ... if your machine core dumps on dereferencing NULL, just trap the > fault ... patch up ... return. It's a very machine dependent solution, > but certainly can be done on any Unix machine. Really? It can't be done on several models of the PDP-11. Models that supported UNIX. I suspect it can't be done on many UNIX systems that don't support demand paging. Maybe not even on some that do. Radford Neal