snl@cs.cmu.edu (Sean Levy) (06/26/88)
Here's part of what I do: #define NIL(t) ((t *)0) /* might change to some strange */ /* number for the 48-bit datapath HP's :-) */ #define NULL NIL(char) /* for backwards compatibility */ Then, f'rinstance struct hostent *hp; hp = gethostbyname(argv[1]); if (hp == NIL(struct hostent)) /* call lost */ ... I find this much easier to read. I don't write NULL in code anymore, I use NIL(char). Being a combo Lisp/C programmer, NIL makes more sense to me as a name for the pointer that points at nothing than NULL does. I type fast enough that I don't really make an issue of having to type an extra 5 characters to get what everyone seems to call a "NULL pointer" on this list. I would add that distinctions between 0 and a pointer become VERY important at a place like CMU, where code may be compiled on RT's, Suns, uVaxen and god knows what else. The above #define's go in a header file called "basic.h" on my personal include path. I only include <stdio.h> in modules that, well, do I/O. I'm also going to switch completely to C++ verrrry soon... Sean Levy Engineering Desingn Research Center (EDRC), CMU Internet: snl @ cs.cmu.edu BITNET: snl%cs.cmu.edu@cmccvma UUCP: beast me. here's a couple that seem to work west coast: ...!{ucsdhub|sdcsvax}!snl@cs.cmu.edu east cost: ...!harvard!snl@cs.cmu.edu
gwyn@brl-smoke.ARPA (Doug Gwyn ) (06/27/88)
In article <MWlAyzy00hIE82Q1Qc@cs.cmu.edu> snl@cs.cmu.edu (Sean Levy) writes: > #define NIL(t) ((t *)0) > #define NULL NIL(char) First of all, NULL must be either "0" or "((void *)0)" (the latter only if your compiler understands void *s), so your definition is nonportable. > struct hostent *hp; > hp = gethostbyname(argv[1]); > if (hp == NIL(struct hostent)) > ... You might as well write that condition as if (hp == 0) which is correct portable C. >I would add that distinctions between 0 and a pointer become VERY >important at a place like CMU, where code may be compiled on RT's, >Suns, uVaxen and god knows what else. I assume you mean the difference between an INTEGER 0 and a NULL POINTER of a particular type (null pointers do come in types). In many cases what looks like an integer 0 in source code actually does represent a null pointer of the appropriate type, as in my suggested replacement condition. The main case where special care is needed is when passing a pointer argument to a function. Unless a prototype is in scope (possible with newer C compilers only), an argument written as just "0" will be passed as an integer, which is a type mismatch. Casting the 0 to the appropriate pointer type is appropriate in such cases.
Paul_L_Schauble@cup.portal.com (06/29/88)
I don't mean to extend the continuing discussion about the value of NULL, but i have to ask: is #define NULL (char *)0 really portable?? I'm assuming a somewhat odd machine for sake of argument. Consider the common sequence 1: long *ptr_to_long; 2: ptr_to_long = 0; 3: if (ptr_to_long == NULL) ..... I can visualize a machine where pointer to long (word aligned) and pointer to char have different formats and for the null pointers in each type to have different non-zero values. Clearly the compiler is obligated to convert to the appropriate null pointer value in line 2. Expanding the macro and the cast in line 3 this becomes 1a: long *ptr_to_long; 2a: char *temp; 3a: ptr_to_long = 0; 4a: temp = 0; 5b: if (ptr_to_long == temp) xxxx Again, the compiler is obligated to convert to the appropriate null value in 3a and 4a. But not in 5b. So they may well compare unequal. (I think.) According to K&R, the comparison in 5b is undefined. Pointer comparisons are only defined between pointer of the same type that point at the same array. This becomes a very interesting question since I have seen postings that state that the value on NULL for ANSI C is (void *)0. Seems to me that this has all of the same problems. Is this really what the standard says? If so, where have I gone wrong above? Paul implemented, although the space station looks like it's going to be another bare-bones minimum budget thing with all the emphasis on keeping the up-front cost low, regardless of operational cost. Sigh. But that's the province of the politicians and beaurocrats. It's also hard to believe just how conservative the actual spacecraft manufacturers are. Nobody wants to fly anything that hasn't already been demonstrated in space already. This is very frustrating to those people who want to advance the technology--but nobody wants to risk a few million dollars on something new that might work a tiny bit better if there's any chance at all that something unfor
gwyn@brl-smoke.ARPA (Doug Gwyn ) (06/30/88)
In article <6966@cup.portal.com> Paul_L_Schauble@cup.portal.com writes: > is #define NULL (char *)0 really portable?? No. It never has been, and in Standard C it will cause diagnostics. >This becomes a very interesting question since I have seen postings that >state that the value on NULL for ANSI C is (void *)0. Seems to me that this >has all of the same problems. Is this really what the standard says? If so, >where have I gone wrong above? Where you have gone wrong is in not reading carefully (or not being sufficiently particular about whom you believe). Standard-conforming implementations shall define NULL (in the headers where it is defined) as either 0 or ((void *)0), implementor's choice. The latter definition can be used to catch more programming errors (for example, using NULL as a case in a switch or as an array index). void *s have special properties (not surprising, since there are no void objects). The relevant property here is that any valid pointer may be converted to void * and back without loss of significant information. This, combined with the requirement that null pointers of all types compare equal, implies that ((void *)0) is just as good as 0 for use as a "generic" null pointer. ("Generic" is in quotes because NULL isn't really generic, it just acts that way in most contexts.)
jfh@rpp386.UUCP (John F. Haugh II) (07/02/88)
In article <6966@cup.portal.com> Paul_L_Schauble@cup.portal.com writes: >I don't mean to extend the continuing discussion about the value of NULL, but >i have to ask: > > is #define NULL (char *)0 really portable?? YES, YES, YES. by DEFINITION it is portable. this is why this entire discussion is so damned pointless. as Doug Gywn is fond of saying, if it doesn't work, then you aren't using C. Chris - here is another topic for your posting. - john. -- John F. Haugh II +--------- Cute Chocolate Quote --------- HASA, "S" Division | "USENET should not be confused with UUCP: killer!rpp386!jfh | something that matters, like CHOCOLATE" DOMAIN: jfh@rpp386.uucp | -- with my apologizes
chris@mimsy.UUCP (Chris Torek) (07/03/88)
>In article <6966@cup.portal.com> Paul_L_Schauble@cup.portal.com asks: >>is #define NULL (char *)0 really portable?? In article <3458@rpp386.UUCP> jfh@rpp386.UUCP (John F. Haugh II) answers: >YES, YES, YES. by DEFINITION it is portable. ... >Chris - here is another topic for your posting. I suppose so, because the answer is `no', or at least, not without more context. C's untyped nil pointer, which MUST be given a type before it can be used correctly, is written as `0' (and `0L', and possibly using constant integer expressions, depending on whose definition you use; but `0' suffices and must work). After it has been given a type (`(char *)0') it becomes a nil pointer of that type. Once it has a type (if we ignore some fine points in the dpANS, many of which are unlikely to be implemented in current C compilers) it may not be used as a nil pointer of another type. Hence (char *)0 is a nil pointer to char, and as such may not be used as a nil pointer to int, or a nil pointer to struct tcurts, or indeed as anything other than a pointer to char. It may work---indeed, it is more likely to work than to fail---but it is incorrect and unportable, and should (and does in PCC) draw at least a warning from the compiler. There are only two ways that the untyped nil pointer can acquire a type, namely assignment and comparison. Casts are a special case of assignment, as are arguments to functions that have prototypes in scope. Where this causes the most trouble is in arguments to functions that do not have prototypes in scope, or for which the prototype does not specify a type for that argument: e.g., execl(): f() { void execl(char *, ...); execl("prog", "prog", "arg1", "arg2", ___); } The only correct way to fill in the blank is with (char *)0 (or possibly (char *)0L and similar tricks; outside of obfuscated C contests, these tricks are not worth considering). The dpANS has at present one more instance of an `untyped' nil pointer, namely `(void *)0'. This is an anomaly in the type system, and, while it has some beneficial properties, I believe that overall it makes the situation worse, not better. The differences between using `0' and `(void *)0' as a `generic nil' are, first, that while 0 is also an integer constant, (void *)0 is not, and second, that (void *)0 is also a typed nil pointer (ouch!---more below). Suppose that NULL is defined as either `0' or `(void *)0'---one of the two untyped nil pointers---but that we do not know which one. Which of the following calls are correct? /* defintions before the fragments (note lack of prototypes) */ void f1(cp) char *cp; { <code> } void f2(ip) int *ip; { <code> } void f3(vp) void *vp; { <code> } ... f1(NULL); /* call 1 */ f1((char *)NULL); /* call 2 */ f2(NULL); /* call 3 */ f2((int *)NULL); /* call 4 */ f3(NULL); /* call 5 */ f3((void *)NULL); /* call 6 */ It is easy to see that calls 2, 4, and 6 (which cast their arguments and hence provide types) are correct. The surprise is that while calls 1, 3, and 5 are all wrong if NULL is defined as `0', calls 1 and 5 are both correct, or at least will both work, if NULL is defined as `(void *)0'. Call 3 is wrong in any case. We can get away with `f1((void *)0)' only because of a technicality: the dpANS says that (void *) and (char *) must have the same representation (which more or less means `must be the same type'), and because (void *) is a valid pointer type, (void *)0 must be a valid nil pointer of type `void *', and thus must also be a valid nil pointer of type `char *'. (Actually, this argument glosses over a subsidiary technicality, in that there is no guarantee that there is only ONE valid nil pointer of any given type, but that way lies madness. There are more arguments about whether `same representation' implies `indistinguishable'; these, too, are best left untouched.) There are no ANSI-conformant C compilers, for there is as yet no ANSI C standard. One should therefore assume that code may have to run under a compiler where NULL is defined as `0', not as `(void *)0', and should therefore avoid calls like 1, 3, and 5 above. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
maart@cs.vu.nl (Maarten Litmaath) (07/05/88)
In article <12290@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
\ void execl(char *, ...);
Doesn't execl() return int anymore? With the cause of the failure in errno?
--
I'd rather live in Russia |Maarten Litmaath @ Free U Amsterdam:
than in South-Africa... |maart@cs.vu.nl, mcvax!botter!ark!maart
chris@mimsy.UUCP (Chris Torek) (07/06/88)
>In article <12290@mimsy.UUCP> I wrote >> void execl(char *, ...); In article <1315@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) asks: >Doesn't execl() return int anymore? With the cause of the failure in errno? Well, actually, yes; but it only ever returns -1, so declaring it as `int' is somewhat pointless---the value is even less useful than that from, e.g., strcpy(). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
nevin1@ihlpf.ATT.COM (00704a-Liber) (07/06/88)
In article <1315@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes: >In article <12290@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >\ void execl(char *, ...); >Doesn't execl() return int anymore? With the cause of the failure in errno? It still does (at least according to my System V manual); as a matter of fact, the ONLY value it can return is -1. More to the point: why would you want to check the return value of exec()? The only way it can return to the calling process is if an error occurs, so why check the return value? It seems like a waste of code. -- _ __ NEVIN J. LIBER ..!ihnp4!ihlpf!nevin1 (312) 510-6194 ' ) ) You are in a twisty maze of little / / _ , __o ____ email paths, all different. / (_</_\/ <__/ / <_ These are solely MY opinions, not AT&T's, blah blah blah