Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

NULL etc.

5 views
Skip to first unread message

Sean Levy

unread,
Jun 26, 1988, 6:19:43 AM6/26/88
to
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}!s...@cs.cmu.edu
east cost: ...!harvard!s...@cs.cmu.edu

Doug Gwyn

unread,
Jun 26, 1988, 3:50:07 PM6/26/88
to
In article <MWlAyzy00...@cs.cmu.edu> s...@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_...@cup.portal.com

unread,
Jun 29, 1988, 4:00:04 AM6/29/88
to
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

Doug Gwyn

unread,
Jun 30, 1988, 12:39:26 PM6/30/88
to
In article <69...@cup.portal.com> Paul_L_...@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.)

John F. Haugh II

unread,
Jul 1, 1988, 7:00:36 PM7/1/88
to
>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: j...@rpp386.uucp | -- with my apologizes

Chris Torek

unread,
Jul 2, 1988, 4:36:44 PM7/2/88
to
>In article <69...@cup.portal.com> Paul_L_...@cup.portal.com asks:

>>is #define NULL (char *)0 really portable??

In article <34...@rpp386.UUCP> j...@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: ch...@mimsy.umd.edu Path: uunet!mimsy!chris

Maarten Litmaath

unread,
Jul 4, 1988, 11:58:35 PM7/4/88
to
In article <12...@mimsy.UUCP> ch...@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... |ma...@cs.vu.nl, mcvax!botter!ark!maart

Chris Torek

unread,
Jul 5, 1988, 8:40:58 PM7/5/88
to
>In article <12...@mimsy.UUCP> I wrote
>> void execl(char *, ...);

In article <13...@ark.cs.vu.nl> ma...@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().

00704a-Liber

unread,
Jul 6, 1988, 12:23:58 AM7/6/88
to
In article <13...@ark.cs.vu.nl> ma...@cs.vu.nl (Maarten Litmaath) writes:
>In article <12...@mimsy.UUCP> ch...@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

Mark Brader

unread,
Jul 7, 1988, 2:10:58 PM7/7/88
to
Chris Torek (ch...@mimsy.UUCP), no less, writes:

> > > void execl(char *, ...);

> ... [if it returns at all] 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().

But "function returning int" and "function returning void" are different
types. A compiler might choose to implement calls to these functions
differently. For instance, it could require a function returning int
to push its return value on the stack, and leave the caller to pop it off.
Then if you declare the function type wrong, your stack is trashed.

There may be no such compilers at the moment, but they're certainly within
the rules. int execl() and (void)execl(...); is what you must say.

Incidentally, the (January 1988) ANSI draft's requirement that void *
have the same representation as char * contains a similar defect.
Not only the representation but also the function calling conventions
applicable to the type should have been required to be the same.
I mentioned this in my public comment letter but, judging from the
response I received, I seem to have been misunderstood.

Mark Brader "Strong typing isn't for weak minds; the argument
Toronto 'strong typing is for weak minds' is for weak minds."
utzoo!sq!msb, m...@sq.com -- Guy Harris

Chris Torek

unread,
Jul 8, 1988, 11:27:29 AM7/8/88
to
In some article somewhere, I wrote:
>>... [execl] only ever returns -1, so declaring it as `int' is
>>somewhat pointless ...

In article <1988Jul7.1...@sq.uucp> m...@sq.uucp (Mark Brader) remarks:


>But "function returning int" and "function returning void" are different

>types. ... int execl() and (void)execl(...); is what you must say.

True enough. What I had meant to imply, though, was that the
definition of execl() could well be changed. This is not something
that applies to ANSI X3J11, but rather to IEEE P1003.

0 new messages