[net.lang.c] if

bet@ecsvax.UUCP (05/30/84)

As has been said, "if((c=getchar())!=EOF)" is perfectly valid IFF "c" is
declared "int"; NOT if c is a char. Whether char variables are signed or
unsigned, for purposes of comparison or promotion to integer, is explicitly
undefined in C [ref K&R p. 40, p. 183]. This is established and documented.
It is an instance in which the definition of the language is explicitly
ambiguous to allow whatever implementation is more efficient on a particular
machine.
					Bennett Todd
					...{decvax,ihnp4,akgua}!mcnc!ecsvax!bet

ado@elsie.UUCP (Arthur David Olson) (11/21/84)

It seems to me that the proposal to legitimize
	a ^^ b
is of the same ilk as the proposal to legitimize unary '+'.
Both are, strictly speaking, "unnecessary;"
both serve to "fill out" the family of operators supported by C,
which may make a programmer's job easier--
there being fewer "exceptions" to remember.

If I had my wish, the standards committee would accept both proposals or accept
neither proposal.  ("!(a^^b)", if you will.  :-))

I'd be interested in hearing from anyone who favors unary '+' and opposes "^^".
--
	..decvax!seismo!elsie!ado			(301) 496-5688
	DEC, VAX and Elsie are Digital Equipment and Borden trademarks

henry@utzoo.UUCP (Henry Spencer) (11/24/84)

> It seems to me that the proposal to legitimize
> 	a ^^ b
> is of the same ilk as the proposal to legitimize unary '+'.
> Both are, strictly speaking, "unnecessary;"
> both serve to "fill out" the family of operators supported by C,

As I understand it, one of the major things that moved the committee to
include unary plus was the observation that "+3.14159" is legal when
read from a string by atof, and when read from input by scanf, but not
when placed in C source.  No such inconsistency motivates "^^".
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

newman@mit-hector.UUCP (Ron Newman) (09/11/85)

With all this talk about NULL pointers not necessarily being equal to 0,
I'm no longer sure what is and isn't portable code.  An C idiom I see 
(and write) frequently is

	<some type> *ptr;
	...
	if (ptr)
	   <statement>

Will this work correctly on a machine where NULL is not 0?  Does it really
need to say

	if (ptr != NULL)

??

(Disclaimer:  I've only known C and Unix for nine months, and have used only
Berkeley 4.2 and only on a VAX.   Thus the discussion about NULL and 0
being non-equivalent came as a great shock.)

/Ron Newman, MIT Project Athena

ark@alice.UucP (Andrew Koenig) (09/11/85)

> With all this talk about NULL pointers not necessarily being equal to 0,
> I'm no longer sure what is and isn't portable code.  An C idiom I see 
> (and write) frequently is

>	<some type> *ptr;
>	...
>	if (ptr)
>	   <statement>

> Will this work correctly on a machine where NULL is not 0?  Does it really
> need to say

>	if (ptr != NULL)

No, you don't need to say NULL explicitly.  The relevant rules are:

	1. The only integer that is guaranteed to be converted
	meaningfully to a pointer is the constant 0: this is
	guaranteed to yield a value that is distinct from any
	valid pointer and is guaranteed to compare unequal to
	any valid pointer.

	2. Since 0 is the only distinctive pointer value, it is
	usually used by convention to mark the end of a list or
	to indicate a non-existent pointer value.  This convention
	is institutionalized by defining NULL as 0 in <stdio.h>.

	3. If e is an expression, if(e) and if((e)!=0) always
	mean exactly the same thing.

Thus, if NULL is defined as 0,

	if (pointer) ...

and

	if (pointer != NULL) ...

mean the same thing and the usage is portable.  If NULL is not defined
as 0, the very definition of NULL is non-portable.

guy@sun.uucp (Guy Harris) (09/12/85)

> An C idiom I see (and write) frequently is
> 
> 	if (ptr)
> 
> Will this work correctly on a machine where NULL is not 0?  Does it really
> need to say
> 
> 	if (ptr != NULL)

1) "if (X)" means exactly the same thing as "if (X == 0)", for all X
(assuming "if (X == 0)" has a meaning in the first place, i.e. if X is a
struct, forget it).

2) NULL is #defined to be 0, so "if (ptr == NULL)" is equivalent to "if (ptr
== 0)".

So both statements are equivalent.  The compiler is capable of realizing
that the LHS of the comparison is a pointer and the RHS is the constant 0,
and generates code to compare the pointer to a null pointer of the
appropriate type.  The only place where the compiler can't recognize that a
constant 0 is really a null pointer of the appropriate type is in a function
call (i.e., "setbuf(stream, 0)" and "setbuf(stream, NULL)" are wrong; you
have to say "setbuf(stream, (char *)0)" or "setbuf(stream, (char *)NULL)").

ANSI C will permit you to say something like

	void setbuf(FILE *, char *);

in <stdio.h>, in which case the compiler knows that the "0" in "setbuf(0)"
is to be converted to "(char *)0" and will do it for you.

	Guy Harris

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (09/13/85)

> 	if (ptr)
is perfectly okay

> 	if (ptr != NULL)
is also okay and may be better style

NULL is #defined in <stdio.h> and other places as 0.
Do not confuse NULL (or its macro expansion, 0) with
a null pointer.  NULL is an integer constant (after
macro expansion).

Are you confused yet?

stuart@rochester.UUCP (Stuart Friedberg) (09/13/85)

[S. Friedberg]
> [A. Koenig]
> > [forgot original poster, sorry]

> > With all this talk about NULL pointers not necessarily being equal to 0,
> > I'm no longer sure what is and isn't portable code.
> >	if (ptr)
> >	   <statement>
> > Will this work correctly on a machine where NULL is not 0?  Does it really
> > need to say
> >	if (ptr != NULL)
> No, you don't need to say NULL explicitly.  The relevant rules are:
[relevant rules elided]
> ... the usage is portable.  If NULL is not defined
> as 0, the very definition of NULL is non-portable.

I have to disagree with A. Koenig's reply.  The rules he cites are
accurate, but the conclusions are unsafe.

A constant zero IS safely converted into a "null" pointer of the
appropriate type.  IT IS NOT THE CASE that a "null" pointer of an
arbitrary type is converted to or represented by all zero bits.
  char *p;
  if (p)			/* NOT PORTABLE */

Moreover, saying that NULL must be defined as zero is putting the cart
before the horse.  NULL must be defined as an invalid (char *) pointer
and if your machine architecture wants that to be 0x555555, then that
is what NULL should be defined as.  It is coincidental and NONPORTABLE
that NULL is usually defined as 0.
  char *p;
  if (p != 0)			/* NOT PORTABLE */
  if (p != NULL)		/* PORTABLE */

NULL is NOT guaranteed to be canonical "null" pointer for any type
other than (char *).
   struct foo *f;
   if (f != NULL)		/* NOT PORTABLE */
   if (f != (struct foo *)0)	/* PORTABLE */

Moral:  It is an unfortunate accident of fate that a word or longword
with all zero bits can serve as a canonical "null" pointer for all
pointer types on most machines.  Yes, it works on most machines. No,
it is not portable and if you really want PORTABLE code, use this idiom:
   anytype *p;
   if (f != (anytype *)0)	/* PORTABLE */

NULL is defined as "(char *)0", not as "0" on any sensible system and
if your architecture is one where null pointers of all types are zeroed
integers, there will be NO OVERHEAD using the suggested idiom, because
even trivial compilers will generate identical code for
  if(p)			/* BAD */
  if(p != (anytype *)0)	/* GOOD */

Stu Friedberg  {seismo, allegra}!rochester!stuart  stuart@rochester

mjs@sfmag.UUCP (M.J.Shannon) (09/13/85)

> > An C idiom I see (and write) frequently is
> > 
> > 	if (ptr)
> > 
> > Will this work correctly on a machine where NULL is not 0?  Does it really
> > need to say
> > 
> > 	if (ptr != NULL)
> 
> 1) "if (X)" means exactly the same thing as "if (X == 0)", for all X
> (assuming "if (X == 0)" has a meaning in the first place, i.e. if X is a
> struct, forget it).

No!  "if (X)" means exactly the same thing as "if (X != 0)"!

> 2) NULL is #defined to be 0, so "if (ptr == NULL)" is equivalent to "if (ptr
> == 0)".

No again!  Null is defined to be "some flavor of 0", and "if (ptr == NULL)" is
almost always equivalent to "if (ptr == 0)".  I have seen NULL defined as an
integral constant and as "(char *) 0", with equivalent results in all available
implementations.

> So both statements are equivalent.  The compiler is capable of realizing
> that the LHS of the comparison is a pointer and the RHS is the constant 0,
> and generates code to compare the pointer to a null pointer of the
> appropriate type.  The only place where the compiler can't recognize that a
> constant 0 is really a null pointer of the appropriate type is in a function
> call (i.e., "setbuf(stream, 0)" and "setbuf(stream, NULL)" are wrong; you
> have to say "setbuf(stream, (char *)0)" or "setbuf(stream, (char *)NULL)").

It is for this reason that NULL is often defined as "(char *) 0" (often, there
is an enclosing pair of parentheses in the definition).

> ANSI C will permit you to say something like
> 
> 	void setbuf(FILE *, char *);
> 
> in <stdio.h>, in which case the compiler knows that the "0" in "setbuf(0)"
> is to be converted to "(char *)0" and will do it for you.

Yes, this and the related features of ANSI C are "*good* things".

> 	Guy Harris

Tiny flame: I am usually loathe to include an article in its entirety, but Guy
is one of the few posters who (often) constrains his articles to only the facts
of the matter at hand, without "religious" flames (at least in non-"religious"
newsgroups).  That he goofed here is most likely due to fingers running on
auto-pilot with a noisy signal....
-- 
	Marty Shannon
UUCP:	ihnp4!attunix!mjs
Phone:	+1 (201) 522 6063
Disclaimer: I speak for no one.

vishniac@wanginst.UUCP (Ephraim Vishniac) (09/13/85)

> > With all this talk about NULL pointers not necessarily being equal to 0,
> > I'm no longer sure what is and isn't portable code.  An C idiom I see 
> > (and write) frequently is
> >	<some type> *ptr;
> >	...
> >	if (ptr)
> >	   <statement>
> > Will this work correctly on a machine where NULL is not 0?
> 
> No, you don't need to say NULL explicitly.  The relevant rules are:

	1. You should only make explicit comparisons in your code *only* where
	   absolutely necessary.  Not only does this save at least 1% in
	   editing keystrokes, but it deters nosy people from reading (or,
	   at least, from understanding) your code.

The way I see it, portability is not the essential issue here: *clarity* is.
-- 
Ephraim Vishniac
  [apollo, bbncca, cadmus, decvax, harvard, linus, masscomp]!wanginst!vishniac
  vishniac%Wang-Inst@Csnet-Relay

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (09/13/85)

> I have to disagree with A. Koenig's reply.  The rules he cites are
> accurate, but the conclusions are unsafe.

Sorry, Stu, but Andy was completely correct and you are wrong.

> A constant zero IS safely converted into a "null" pointer of the
> appropriate type.  IT IS NOT THE CASE that a "null" pointer of an
> arbitrary type is converted to or represented by all zero bits.

No one said anything about representing a null pointer by "zero bits".
The integer constant 0, written in the source code, is guaranteed to
compare equal to a null pointer, and there are some other guarantees
(NOT including bit pattern for a null pointer).

>   char *p;
>   if (p)			/* NOT PORTABLE */

Rong.

> Moreover, saying that NULL must be defined as zero is putting the cart
> before the horse.  NULL must be defined as an invalid (char *) pointer
> and if your machine architecture wants that to be 0x555555, then that
> is what NULL should be defined as.  It is coincidental and NONPORTABLE
> that NULL is usually defined as 0.

Rong.  The only portable definition of NULL is as 0.

> NULL is NOT guaranteed to be canonical "null" pointer for any type
> other than (char *).

Where did you get this idea?

Please read the X3J11 information bulletin.

cg@myriasa.UUCP (Chris Gray) (09/13/85)

We're going though the usual arguments about how the type 'boolean'
should be fudged, and I came across this example:

typedef enum {FALSE, TRUE} bool;

f()
{
    bool flag;

    if (flag) {
	printf("Hello\n");
    }
    if (flag != 0) {
	printf("Good-bye\n");
    }
}

The BSD 4.2 C compiler is happy about the first 'if', but warns about
'enumeration type clash' on the second. This at least is one case where
'if (X)' isn't the same as 'if (X != 0)'.

				Chris Gray   {...,ihnp4}!alberta!myrias!cg

mjs@sfmag.UUCP (M.J.Shannon) (09/15/85)

> [S. Friedberg]
> > [A. Koenig]
> > > [forgot original poster, sorry]
> 
> > > With all this talk about NULL pointers not necessarily being equal to 0,
> > > I'm no longer sure what is and isn't portable code.
> > >	if (ptr)
> > >	   <statement>
> > > Will this work correctly on a machine where NULL is not 0?  Does it really
> > > need to say
> > >	if (ptr != NULL)
> > No, you don't need to say NULL explicitly.  The relevant rules are:
> [relevant rules elided]
> > ... the usage is portable.  If NULL is not defined
> > as 0, the very definition of NULL is non-portable.
> 
> I have to disagree with A. Koenig's reply.  The rules he cites are
> accurate, but the conclusions are unsafe.
> 
> A constant zero IS safely converted into a "null" pointer of the
> appropriate type.  IT IS NOT THE CASE that a "null" pointer of an
> arbitrary type is converted to or represented by all zero bits.

Koenig is correct both in the rules cited and the conclusions reached.  Your
statement immediately above is also correct, but your conclusion below
("if (p)" is not portable) is incorrect.

>   char *p;
>   if (p)			/* NOT PORTABLE */
> 
> Moreover, saying that NULL must be defined as zero is putting the cart
> before the horse.  NULL must be defined as an invalid (char *) pointer
> and if your machine architecture wants that to be 0x555555, then that
> is what NULL should be defined as.  It is coincidental and NONPORTABLE
> that NULL is usually defined as 0.

Incorrect.  It is the compiler's responsibility to interface with the host
architecture, not the programmer's.  The compiler is required to generate code
which determines whether the pointer "points to no object of the given type"
in the host architecture or not for the fragment "if (p)".

>   char *p;
>   if (p != 0)			/* NOT PORTABLE */
>   if (p != NULL)		/* PORTABLE */
> 
> NULL is NOT guaranteed to be canonical "null" pointer for any type
> other than (char *).

Comments above are all wrong.  Clearly specified in K&R (and, I believe, ANSI
C drafts) is the statement that the (integer) constant 0 may safely (and
correctly) compared to a pointer of any type to determin 0th order validity of
said pointer.  Your statement above is true, but only because different
implementations and/or environments define NULL as something other than "0".

>    struct foo *f;
>    if (f != NULL)		/* NOT PORTABLE */
>    if (f != (struct foo *)0)	/* PORTABLE */
> 
> Moral:  It is an unfortunate accident of fate that a word or longword
> with all zero bits can serve as a canonical "null" pointer for all
> pointer types on most machines.  Yes, it works on most machines. No,
> it is not portable and if you really want PORTABLE code, use this idiom:
>    anytype *p;
>    if (f != (anytype *)0)	/* PORTABLE */

While the preceding fragment is guaranteed to be correct in all (valid)
implementations of C, your moral above is incorrect.  At the level of C source
(in particular, not at the level of the hardware), it is a guarantee that the
integer constant 0 (independant of its representation in the actual hardware)
*IS* the canonical null pointer for all pointer types, *BY DEFINITION*.  This
is specified explicitly in all the specifications of the language I've ever
seen.

> NULL is defined as "(char *)0", not as "0" on any sensible system and

Wrong.  *Some* implementations provide some header file which may define NULL
as something other than "0".  Those that do so are misleading their users, or
intend its use only in comparisons of "char *" types.

> if your architecture is one where null pointers of all types are zeroed
> integers, there will be NO OVERHEAD using the suggested idiom, because
> even trivial compilers will generate identical code for
>   if(p)			/* BAD */

No, not bad.  Perfectly legitimate and proper *on implementations which
properly implement the C language*.

>   if(p != (anytype *)0)	/* GOOD */

Good only if the type of p is "anytype *".

> Stu Friedberg  {seismo, allegra}!rochester!stuart  stuart@rochester

Stu, if you'd like to carry this argument further, let's do so offline (via
mail).  No need to further inflame the other good folks on the net.
-- 
	Marty Shannon
UUCP:	ihnp4!attunix!mjs
Phone:	+1 (201) 522 6063
Disclaimer: I speak for no one.

guy@sun.uucp (Guy Harris) (09/16/85)

> No!  "if (X)" means exactly the same thing as "if (X != 0)"!

Sorry, I slipped - 1/2 typeo, 1/2 mindo.

> > 2) NULL is #defined to be 0, so "if (ptr == NULL)" is equivalent to "if
> > (ptr == 0)".

> No again!  Null is defined to be "some flavor of 0", and "if (ptr == NULL)"
> is almost always equivalent to "if (ptr == 0)".  I have seen NULL defined
> as an integral constant and as "(char *) 0", with equivalent results in all
> available implementations.

No!  If NULL is defined as 0, and "p" is an "int *",

	if (p == NULL)	/* or != NULL */

generates correct code and no messages.  If NULL is defined as "(char *)0",
it probably generates correct code (the compiler should know enough to
coerce the "(char *)0" into an "(int *)0", just as it should know enough to
coerce "0" by itself into "(int *)0", *but* it also generates the warning

	illegal pointer combination

at least on PCC-based compilers.

And in the case

	foo(p)
	int *p;
	{
		...
	}

	bar()
	{
		...
		foo(NULL);
		...
	}

unless "(char *)0" and "(int *)0", as function arguments, cause the exact
same bit pattern to be passed, #defining NULL as "(char *)0" will not cause
correct code to be generated (unless you have an ANSI C compiler and have
declared "foo" as taking a "int *" as an argument - but if you have such a
compiler and have so declared "foo", #defining NULL as 0 will also cause
correct code to be generated).

#defining NULL as *anything* other than 0 is incorrect!  The trouble with
that declaration is that it looks like it solves the problem when it really
doesn't.

	Guy Harris

guy@sun.uucp (Guy Harris) (09/16/85)

> A constant zero IS safely converted into a "null" pointer of the
> appropriate type.  IT IS NOT THE CASE that a "null" pointer of an
> arbitrary type is converted to or represented by all zero bits.
>   char *p;
>   if (p)			/* NOT PORTABLE */
> 

Wrong.  "if (p)" is simply shorthand for "if (p != 0)" (see, I got it right
this time :-)), so if

	if (p)

isn't portable, then

	if (p != 0)

isn't portable either.  (And it *is* portable - see next paragraph of reply.)

> Moreover, saying that NULL must be defined as zero is putting the cart
> before the horse.  NULL must be defined as an invalid (char *) pointer
> and if your machine architecture wants that to be 0x555555, then that
> is what NULL should be defined as.  It is coincidental and NONPORTABLE
> that NULL is usually defined as 0.
>   char *p;
>   if (p != 0)			/* NOT PORTABLE */
>   if (p != NULL)		/* PORTABLE */

Wrong, wrong, wrong, wrong, wrong, wrong, wrong, wrong*!  Go read K&R again,
It *very explicitly says* that "0", when it is used in an expression where a
pointer is expected (or generated, such as (char *)0), is converted to a
null pointer of the appropriate type.  If the bit pattern for such a pointer
happens to be 0x555555, then

	if (p != 0)

gets compiled as code to compare the bit pattern of "p" with the bit pattern
0x555555, regardless of the fact that a "0" happens to appear in the code.

> NULL is defined as "(char *)0", not as "0" on any sensible system

OK, you've just declared most UNIX implementations not to be sensible.  NULL
is defined a "(char *)0" on systems which haven't implemented C correctly.
(See my response to Marty Shannon for an explanation of why declaring NULL
as anything other than 0 is incorrect.)

> ...even trivial compilers will generate identical code for
>   if(p)			/* BAD */
>   if(p != (anytype *)0)	/* GOOD */

ALL C COMPILERS GENERATE IDENTICAL CODE FOR THOSE TWO CASES.  Anything that
does *not* generate identical code for them - even if that means comparing
the bits stored in "p" with the bit pattern of 0x555555 - is not a C
compiler.  It is a compiler for a language which resembles C in some ways
but isn't C.

Moral: novice C programmers who ask questions in net.lang.c are probably
better off asking somewhere else.  The chances are very good that the answer
they get will be totally wrong.  Go out and buy a copy of Harbison and
Steele's "C: A Reference Manual" instead - despite the title, it's not just
a dry reference text, but contains a fair bit of useful tutorial information
(explaining things like null pointers, the proper use of "short", and all
sorts of topics which seem to be ill-understood by a depressingly large
segment of the C programming community and of the readership *and*
writership of this newsgroup).

	Guy Harris

mjs@sfmag.UUCP (M.J.Shannon) (09/17/85)

> > > 2) NULL is #defined to be 0, so "if (ptr == NULL)" is equivalent to "if
> > > (ptr == 0)".
> 
> > No again!  Null is defined to be "some flavor of 0", and "if (ptr == NULL)"
> > is almost always equivalent to "if (ptr == 0)".  I have seen NULL defined
> > as an integral constant and as "(char *) 0", with equivalent results in all
> > available implementations.
> 
> No!  If NULL is defined as 0, and "p" is an "int *",
> 
> 	if (p == NULL)	/* or != NULL */
> 
> generates correct code and no messages.  If NULL is defined as "(char *)0",
> it probably generates correct code (the compiler should know enough to
> coerce the "(char *)0" into an "(int *)0", just as it should know enough to
> coerce "0" by itself into "(int *)0", *but* it also generates the warning
> 
> 	illegal pointer combination
> 
> at least on PCC-based compilers.

True and proper.  I overstated reality above.

> And in the case
> 
> 	foo(p)
> 	int *p;
> 	{
> 		...
> 	}
> 
> 	bar()
> 	{
> 		...
> 		foo(NULL);
> 		...
> 	}
> 
> unless "(char *)0" and "(int *)0", as function arguments, cause the exact
> same bit pattern to be passed, #defining NULL as "(char *)0" will not cause
> correct code to be generated (unless you have an ANSI C compiler and have
> declared "foo" as taking a "int *" as an argument - but if you have such a
> compiler and have so declared "foo", #defining NULL as 0 will also cause
> correct code to be generated).

Well, now we come to the can of worms which causes some implementations to
#define NULL (char *) 0: machines where pointers occupy more space than ints.
In these implementations, foo(NULL) is absolutely incorrect (duplicate paren'd
comment from previous paragraph) (and try porting "typical" code to such
machines; but I digress).

> #defining NULL as *anything* other than 0 is incorrect!  The trouble with
> that declaration is that it looks like it solves the problem when it really
> doesn't.

Same comment as above.  The solution may be that NULL should be 0L (but what
about the venerable old PDP-11?  In that case, foo(NULL) passes too much stuff
on the stack.  So, what is *the* answer?)....

> 	Guy Harris
-- 
	Marty Shannon
UUCP:	ihnp4!attunix!mjs
Phone:	+1 (201) 522 6063
Disclaimer: I speak for no one.

rcd@opus.UUCP (Dick Dunn) (09/18/85)

> We're going though the usual arguments about how the type 'boolean'
> should be fudged, and I came across this example:
> 
> typedef enum {FALSE, TRUE} bool;
> ...
>     if (flag) {
> ...
>     if (flag != 0) {
> ...
> The BSD 4.2 C compiler is happy about the first 'if', but warns about
> 'enumeration type clash' on the second. This at least is one case where
> 'if (X)' isn't the same as 'if (X != 0)'.

I remember Dennis Ritchie commenting about enums in C; the general idea was
that they were "ripp'd untimely from the womb" as it were, so there's woom
er, room, for improvement.  Some of the behavior of enums is for the dogs;
it's high time someone said, "Out, damn Spot!", got us off our MacDuffs, and
cleaned them up somehow.  I discovered, courtesy one of our local errant
programmers, that given a type:
	typedef enum {T0, T1, T2} ternary;
and a declaration:
	ternary t;
although it is not permitted, with the 4.x BSD compilers, to say:
	if (t!=0) ...
it IS permitted to say
	if (!t)
My reaction to the use of ! on a ternary value is, quid pro quo, !
If enums are ints, both ifs should be allowed; if they are distinct animals
(which I might hope had been the int-ention), neither should be allowed.
-- 
Dick Dunn	{hao,ucbvax,allegra}!nbires!rcd		(303)444-5710 x3086
   ...Lately it occurs to me what a long, strange trip it's been.

emike@riccb.UUCP (Mike Durbin ) (09/18/85)

> [S. Friedberg]
> > [A. Koenig]
> > No, you don't need to say NULL explicitly.  The relevant rules are:
> [relevant rules elided]
> > ... the usage is portable.  If NULL is not defined
> > as 0, the very definition of NULL is non-portable.
> 
> I have to disagree with A. Koenig's reply.  The rules he cites are
> accurate, but the conclusions are unsafe.

> Moreover, saying that NULL must be defined as zero is putting the cart
> before the horse.  NULL must be defined as an invalid (char *) pointer
> and if your machine architecture wants that to be 0x555555, then that
> is what NULL should be defined as.  It is coincidental and NONPORTABLE
> that NULL is usually defined as 0.
>   char *p;
>   if (p != 0)			/* NOT PORTABLE */
>   if (p != NULL)		/* PORTABLE */
 
> NULL is NOT guaranteed to be canonical "null" pointer for any type
> other than (char *).
>    struct foo *f;
>    if (f != NULL)		/* NOT PORTABLE */
>    if (f != (struct foo *)0)	/* PORTABLE */
 
>    anytype *p;
>    if (f != (anytype *)0)	/* PORTABLE */
> 
> NULL is defined as "(char *)0", not as "0" on any sensible system and
> if your architecture is one where null pointers of all types are zeroed
> integers, there will be NO OVERHEAD using the suggested idiom, because
> even trivial compilers will generate identical code for
>   if(p)			/* BAD */
>   if(p != (anytype *)0)	/* GOOD */
> 
> Stu Friedberg  {seismo, allegra}!rochester!stuart  stuart@rochester


I agree with Stu Friedberg (mostly).

Just because a machine architecture allows 0 to point at a valid
address, dosn't mean that the compiler designer should allow valid data
to exist at that address.

Because all static and external variables are gaurenteed to be initialized
to 0, lets try to maintain that a pointer with a value of zero points to
nothing!

But back to agreeing with Stu.

NULL *IS* defined as (char *)0 and SHOULD only be used in comparisons
with character pointers!

	if ((fp = fopen(file, "r")) == NULL)		/* NOT QUITE CORRECT */

	if ((fp = fopen(file, "r")) == (FILE *)0)	/* BETTER */


	etc.

-- 
unixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixuni
n									      x
i	E. Mike Durbin			...!ihnp4!			      u
x	ROCKWELL TELECOMMUNICATIONS	...!ihopa!riccb!emike		      n
u	Downers Grove IL		...!cuuxb!			      i
n	(312) 960-8658							      x
i									      u
xunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixun

henry@utzoo.UUCP (Henry Spencer) (09/18/85)

> Well, now we come to the can of worms which causes some implementations to
> #define NULL (char *) 0: machines where pointers occupy more space than ints.
> In these implementations, foo(NULL) is absolutely incorrect (duplicate paren'd
> comment from previous paragraph) (and try porting "typical" code to such
> machines; but I digress).
> 
> ...  The solution may be that NULL should be 0L (but what
> about the venerable old PDP-11?  In that case, foo(NULL) passes too much stuff
> on the stack.  So, what is *the* answer?)....

*The* answer is that foo(NULL) is absolutely incorrect on any machine.
The proper way to write this is foo((char *)NULL).  The only portable way
to get something that looks like a NULL pointer of the proper type onto
the stack, is to put a NULL pointer of the proper type onto the stack
explicitly.  There are no shortcuts on this one.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

guy@sun.uucp (Guy Harris) (09/19/85)

> Well, now we come to the can of worms which causes some implementations to
> #define NULL (char *) 0: machines where pointers occupy more space than
> ints.

Unfortunately, #defining NULL as (char *)0 will provoke more "lint"
messages, which is not what is wanted here.  If you're going to make
"typical" code work on those machines (I spent a fair bit of time doing
exactly that at CCI, along with some code - like "sdb" - which, if it's
typical, means we're all in deep trouble; but I digress) you have to run it
through "lint" to catch all the pointer-valued functions which aren't so
declared.

> Same comment as above.  The solution may be that NULL should be 0L (but what
> about the venerable old PDP-11?  In that case, foo(NULL) passes too much
> stuff on the stack.  So, what is *the* answer?)....

In that case, you could have different definitions of NULL for different
machines.  But what about a hypothetical machine (which is, I suspect, not
so hypothetical) with 64K 16-bit words in a process' address space and a
special two-word format for byte pointers?  In that case, (int *)NULL would
be 16 bits but (char *)NULL would be 32 bits.

*The* answer is to cast each NULL/0 yourself, or wait for ANSI C and have it
do the coercions for you (which doesn't help with existing code).

In retrospect, perhaps C should have had a keyword like "nil" to represent
null pointers.

1) Assignments of the sort

	pointer_variable = <integer_expression>;

would no longer have different meanings depending on whether the
<integer_expression> was a constant 0 or not.

2) The confusion that has sprung up between null pointers and pointers
filled with 0 bits - and the subsequent confusion indicated by people
thinking NULL should *really* be defined as a constant with the same bit
pattern as a null pointer on a particular machine - might have been avoided.

3) If the rules for "nil" were that a) a naked "nil" is illegal but b) as
the operand of an operator whose other operand or result were of a
particular pointer type, a "nil" becomes a null pointer of that type, code
like

	setbuf(stdout, nil);

would be illegal (no such operator nearby) and the "nil" would have to be
properly cast.  "int *p; p = nil;" would compile properly, as the "nil" is
an operand of the "=" whose other operand is a "pointer to int".

However, hindsight is 20/20; if C had everything in it that it "should" have
had, it might be a better language but its compiler might not have fit on
the PDP-11...

	Guy Harris

emike@riccb.UUCP (Mike Durbin ) (09/19/85)

-----------

I wrote:
> NULL *IS* defined as (char *)0 and SHOULD only be used in comparisons
> with character pointers!

and I was wrong.  

Thanks for the (kind) replies that pointed out my error.
"... comparisons of pointers with a plain 0 are well-defined by the language."
-- 
unixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixuni
n									      x
i	E. Mike Durbin			...!ihnp4!			      u
x	ROCKWELL TELECOMMUNICATIONS	...!ihopa!riccb!emike		      n
u	Downers Grove IL		...!cuuxb!			      i
n	(312) 960-8658							      x
i									      u
xunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixunixun

henry@utzoo.UUCP (Henry Spencer) (09/20/85)

> NULL *IS* defined as (char *)0 and SHOULD only be used in comparisons
> with character pointers!

Groan.  Wrong.  K&R page 97:

	#define NULL 0
	...
	We write NULL instead of zero... to indicate more clearly that
	this is a special value for a pointer...

Harbison & Steele, page 94:

	Standard header files usually define the preprocessor macro
	name NULL to be 0.

ANSI X3J11 draft of 30 April 1985, page 67:

	The macros are... NULL... which expands to a constant expression
	whose value compares equal to that of an integral constant
	expression with the value 0, which can be assigned to a pointer
	or used as an argument to represent the null pointer...

(Note that although the ANSI definition uses more complex weasel-wording
than the earlier ones, it still requires that NULL work for pointer types
in general.)
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

rb@ccivax.UUCP (rex ballard) (09/20/85)

> An C idiom I see (and write) frequently is
> 
> 	if (ptr)
> 
> Will this work correctly on a machine where NULL is not 0?  Does it really
> need to say
> 
> 	if (ptr != NULL)
 
If you have the K&R book page 97 says:
	We write NULL instead of zero however, to indicate that this is
	a special value for a pointer.  In general, integers cannot
	meaningfully be assigned to pointers; zero is a special case.

If you have the use the "C referrence manual", section 7.14 states:

	The compilers currently allow a pointer to be assigned to
	an integer, an integer to a pointer, and a pointer to a
	pointer to a pointer of another type.  The assignment is a
	pure copy operation, with no conversion.  This usage is
	nonportable, and may produce pointers which cause addressing
	exceptions when used.  However, it is guarenteed that
	assignment of the constant 0 to a pointer will produce a
	null pointer distinguishable from a pointer to any object.

In other words, if NULL is defined as anything other than 0, you
are going to blow your banana's if you try to compile someone elses
code.

If you are really insistant, you can even access address location 0
in memory on some machines since many segmented systems like to
have the first byte of executable code.  The important thing here
is that you are not explicitly required to CAST a NULL for fear of
loss of significant bits, sign extension, or similar problems associated
with cross-type comparisons (like comparing 0126 < '\240') which
require explicit casting.

A popular convention is to use the "(char *)0" definition.  This can
get confusing if your source code contains (int *)NULL.  Some (the
bad ones) versions of lint give interesting results.

Always be sure however of the exact definition of the returned pointers such as
EOF is not as consistant.

I recently experienced a nasty "gotcha" in an interface with two routines
each of which returned 'OK' but the problem was:
--------------------------
(file 1)
#define OK 0	/* actually include's */

foo()
{
	return(OK);
}
-----------------------------
(file 2)
#define OK -1	/* actually include's */

fie()
{
	return(OK);
}
-----------------------------
(file 3)

foobar()
{
	if(!foo()||!fie())
	{
	  errmsg("oops");
	  return;
	}  /* I normally use "indent -bl" but not on mail */
}

Of course foo and fie were very frequently used routines used in
separate products so there was no way to fix the various users
(many of which used "if(~fie())").

The first example is good it you are certain the function returns NULL,
otherwise, forget it.


By the way, has anybody figured out how to get lint to shut up about
the return codes for { ,s,f}printf().  I have yet to see a standard definition
of the return code, but lint keeps giving me "value returned but not used".
Isn't this just a tramp from doprntf?  I've had one version hand back the
value of write (and blew away my stack) even though the return code was
supposed to return a char* (sprintf),  (Seems it didn't like writing a
4k message).  This appears to be a good case for using if(sprintf(...)).

guy@sun.uucp (Guy Harris) (09/20/85)

> NULL *IS* defined as (char *)0

The meaning of the above statement, presumably, is "there is no place where
NULL is defined as anything other than (char *)0.

Well:

4.2BSD's /usr/include/stdio.h:
 ...
#define	NULL	0

System V Release 2's "/usr/include/stdio.h":
 ...
#define NULL		0

Sun UNIX 2.0's "/usr/include/stdio.h":
 ...
#define	NULL	0

Need I go on?  One counterexample is sufficient to show that the above
statement is false.

> and SHOULD only be used in comparisons with character pointers!

> 	if ((fp = fopen(file, "r")) == NULL)		/* NOT QUITE CORRECT */

> 	if ((fp = fopen(file, "r")) == (FILE *)0)	/* BETTER */

If NULL were defined as (char *)0, the first comparison would be incorrect.
However, it isn't, so it is correct.  Furthermore,

	if ((fp = fopen(file, "r")) == 0)

is just as good.  The C compiler is quite capable of realizing that since
the LHS of the "==" has type "FILE *", the RHS has to be coerced to the same
type.

There are few things more tiresome than reading debates over whether 2+2 is
5 or 7.  If anybody still thinks that

	1) NULL is always #defined as (char *)0

	2) NULL should be defined as an integer with the same bit pattern
	   as a null pointer on the machine in question

	3) 0's have to be cast to an appropriate pointer type in
	   contexts where the compiler can automatically do the
	   coercion

would they please go back and read K&R or Harbison and Steele until they no
longer think so?

	Guy Harris

rb@ccivax.UUCP (rex ballard) (09/21/85)

> > An C idiom I see (and write) frequently is
> > 
> > 	if (ptr)
> > 
> > Will this work correctly on a machine where NULL is not 0?  Does it really
> > need to say
> > 
> > 	if (ptr != NULL)
>  
> If you have the K&R book page 97 says:
> 	We write NULL instead of zero however, to indicate that this is
> 	a special value for a pointer.  In general, integers cannot
> 	meaningfully be assigned to pointers; zero is a special case.

NULL should be defined as '0', a good example of why?

-------------------------------
/* file 1 */
struct clx {
    int a;	/* a number >0 */
    char b[2];
};

static struct clx x[10];

newclx(i)
{
	if((i<0)||(i>9))
		return(NULL);
	/* NOSTRICT */	/* turn off lint's type checker */
	return((x[i]=malloc(sizeof(struct clx));
}

getclxa(i)
{
	if((i<0)||(i>9)) return(NULL);
	return(x[i].a)
}
char *getclxb(i)
{
	if((i<0)||(i>9)) return(NULL);
	return(x[i].b)
}
-------------------------------
/* file 2 */

doit()
{
	int a,i;
	char b;
	char *x;
	....
	if(newclx(i))
		return;
	if(getclxa(i),&a)
}
----------------------------

This is a LOUSY example, but was required to illustrate.
Are you going to tell doit(), about the structure of clx?  What about all
the possible name and type clashes?  The object oriented approach is such
that only "file 1" will ever know about clx.  Suppose there were a different
structure in doit() that also used member names 'a' and 'b'? Some compilers
will use the offset of the last structure member (they shouldn't, but they
do!).  The alternative is #include every structure in every module.

In experimenting with object oriented design, I produced a very powerful
report generator that was very flexable and very maintainable (once you
got to the first "primitive").  We just change one module for any report
you like (very much like tbl, with input for binary data).

The most important purpose of the return value is to indicate the
success or failure of the function, anything else can be handled
just as well by using &value. (Make sure someone explains the
use of:
fun(a) **a; {...}

before flaming me about use of return code to return pointers.

ART@ACC.ARPA (Art Berggreen) (09/23/85)

I've been watching the discussion about "if(pointer)" and have not
seen the following observation:

[I'll probably raise flames requarding booleans under C, but please
treat this as an abstract view.]

From an abstract language viewpoint, an "if" statement conditionally
executes a block of statements based on whether the control statement
evaluates to a condition of *TRUE*.  Pointers by themself do not
have attributes of TRUE vs FALSE.  Thus, "if(pointer)" makes less semantic
sense than "if(pointer == SOME_VALID_POINTER_VALUE)".  What to test against
has been discussed in previous messages.

    				"Art Berggreen"<Art@ACC.ARPA>

Please return any flames to <Pacific_Ocean@Santa_Barbara.California>.

------

tmb@talcott.UUCP (Thomas M. Breuel) (09/24/85)

In article <1671@brl-tgr.ARPA>, ART@ACC.ARPA (Art Berggreen) writes:
> From an abstract language viewpoint, an "if" statement conditionally
> executes a block of statements based on whether the control statement
> evaluates to a condition of *TRUE*.  Pointers by themself do not
> have attributes of TRUE vs FALSE.  Thus, "if(pointer)" makes less semantic

Where do you take that bit of wisdom from? Obviously, 'C' works differently,
from an abstract and practical point of view. If you like abstractions,
why don't you consider 'if' a message that is sent to the object following
it in parentheses and that then decides to execute the statement
following it :-).

					Thomas.

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (09/25/85)

> By the way, has anybody figured out how to get lint to shut up about
> the return codes for { ,s,f}printf().  I have yet to see a standard definition
> of the return code, but lint keeps giving me "value returned but not used".
> Isn't this just a tramp from doprntf?  I've had one version hand back the
> value of write (and blew away my stack) even though the return code was
> supposed to return a char* (sprintf),  (Seems it didn't like writing a
> 4k message).  This appears to be a good case for using if(sprintf(...)).

This subject has been discussed to death before.  In X3J11 and UNIX
System V, the *printf() functions return a value (# chars transmitted,
or negative on output error).  If you don't wish to test this value
(shame on you), then cast it to (void).

cmc@rlvd.UUCP (Chris Crampton) (09/25/85)

In article <57@opus.UUCP> rcd@opus.UUCP (Dick Dunn) writes:
>> typedef enum {FALSE, TRUE} bool;
>> ...
>>     if (flag) {
>> ...
>>     if (flag != 0) {
>> ...
>> The BSD 4.2 C compiler is happy about the first 'if', but warns about
>> 'enumeration type clash' on the second. This at least is one case where
>> 'if (X)' isn't the same as 'if (X != 0)'.
>
>I remember Dennis Ritchie commenting about enums in C; the general idea was
>that they were "ripp'd untimely from the womb" as it were, so there's woom
>er, room, for improvement.  Some of the behavior of enums is for the dogs;
>it's high time someone said, "Out, damn Spot!", got us off our MacDuffs, and
>cleaned them up somehow.  I discovered, courtesy one of our local errant
>programmers, that given a type:
>	typedef enum {T0, T1, T2} ternary;
>and a declaration:
>	ternary t;
>although it is not permitted, with the 4.x BSD compilers, to say:
>	if (t!=0) ...
>it IS permitted to say
>	if (!t)
>My reaction to the use of ! on a ternary value is, quid pro quo, !
>If enums are ints, both ifs should be allowed; if they are distinct animals
>(which I might hope had been the int-ention), neither should be allowed.
>-- 
>Dick Dunn	{hao,ucbvax,allegra}!nbires!rcd		(303)444-5710 x3086

At the recent EUUG conference, Mike Banahan of The Instuction Set gave
a summary of ANSI C. One thing I remember well was the statement "enum IS
int". This should hopefully clear things up in the future when we all have
our ANSI C compilers, but until then we will have to put up with the
various ways compilers handle enums. The compilers on the machines we
run here (Perq PNX, 4.2 ...) all give the previously described
"type clash" warnings and do not allow enums to index an array. But, I am
assured that there are many compilers which treat enums as ints (already)
and that there are indeed good reasons for doing so (run time checking of
proper enumerated type would be too expensive...etc - there were apparently
moves on the ANSI commitee to scrap enums altogether)

I am of the inclination that enums should be handled pretty much as they
are on our machines in that mixing enums and ints should only be allowed with an
appropriate cast, as this indicates that the programmer is prepared to take
the risk or has checked the value herself.

-- 
Chris M Crampton,	..!mcvax!ukc!rlvd!cmc
			Rutherford Appleton Labs, Didcot, OXON, U.K.
			+44 235 21900   ext. 6756
		

mike@rlvd.UUCP (Mike Woods) (09/26/85)

Followup-To:
Xpath: warwick ubu

In article <1671@brl-tgr.ARPA> ART@ACC.ARPA (Art Berggreen) writes:
>From an abstract language viewpoint, an "if" statement conditionally
>executes a block of statements based on whether the control statement
>evaluates to a condition of *TRUE*.  Pointers by themself do not
>have attributes of TRUE vs FALSE.  Thus, "if(pointer)" makes less semantic
>sense than "if(pointer == SOME_VALID_POINTER_VALUE)".  What to test against
>has been discussed in previous messages.

But from an abstract viewpoint, pointers can be considered to have
two states: valid or invalid. Therefore, "if (pointer)" can be read
as "if (pointer is valid)". At least this is how I look upon such
things (though the first time I saw it my mind stopped functioning
for five minutes (the legacy of Fortran!)).

Mike (I don't think logically; I think C) Woods.
-- 

UK JANET:	mike@uk.ac.rl.vd
UUCP:		..!mcvax!ukc!rlvd!mike

david@ukma.UUCP (David Herron, NPR Lover) (09/27/85)

In article <516@talcott.UUCP> tmb@talcott.UUCP (Thomas M. Breuel) quotes:
>In article <1671@brl-tgr.ARPA>, ART@ACC.ARPA (Art Berggreen) writes:
>> From an abstract language viewpoint, an "if" statement conditionally
>> executes a block of statements based on whether the control statement
>> evaluates to a condition of *TRUE*.  Pointers by themself do not
>> have attributes of TRUE vs FALSE.  Thus, "if(pointer)" makes less semantic

I disagree...  Pointers have VALID and INVALID values.  So when you
say if(p) you're asking if it has a VALID or INVALID value.  It's
just that conveniently most machines have 0 as the INVALID value.
-- 
--- David Herron
--- ARPA-> ukma!david@ANL-MCS.ARPA
--- UUCP-> {ucbvax,unmvax,boulder,oddjob}!anlams!ukma!david
---        {ihnp4,decvax,ucbvax}!cbosgd!ukma!david

Hackin's in me blood.  My mother was known as Miss Hacker before she married!

brooks@lll-crg.UUCP (Eugene D. Brooks III) (09/28/85)

If you are using enums in such a way that changing the values that the various
items {apples, oranges, pears} have then you deserve the SERIOUS trouble that
you will eventually get into.

Just because the compiler allows it, even an ANSI standard compiler, is not
justification for doing it!

guy@sun.uucp (Guy Harris) (10/03/85)

> So when you say if(p) you're asking if it has a VALID or INVALID value.
> It's just that conveniently most machines have 0 as the INVALID value.

If you had a machine on which the bit pattern stored in an invalid pointer
was 0x12344321, the way to test whether a pointer is valid or not is

	if (p)	/* or if (p != 0) or if (p != NULL) */
		printf("valid\n");
	else
		printf("invalid\n");

The fact that "0" is converted to the appropriate pattern for an invalid
pointer (of the type in question) does not imply that those pointers have to
have a bit pattern of all zeroes.

	Guy Harris

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/04/85)

> I disagree...  Pointers have VALID and INVALID values.  So when you
> say if(p) you're asking if it has a VALID or INVALID value.  It's
> just that conveniently most machines have 0 as the INVALID value.

So if p is non-NULL, it is valid, eh?  Chuckle, chuckle.

brooks@lll-crg.ARpA (Eugene D. Brooks III) (10/05/85)

Yep, if(p) tests to see if p is a valid pointer, valid in the sense that
it point somewhere.  Of course it may point to a location that will dump core.
If it very unfortunate that on a VAX a NULL pointer does not dump core.

guy@sun.uucp (Guy Harris) (10/05/85)

> If you are really insistant, you can even access address location 0
> in memory on some machines since many segmented systems like to
> have the first byte of executable code.

You may consider this a bug, or you may consider it a feature, but

	char *p = 0;

is not guaranteed to assign a value to "p" which will point at address
location 0.

	char *p = 4;

is more likely to assign a value to "p" that will point at address location
4 than the original construct is to assign a value pointing at 0.  Assigning
a constant 0 to a pointer is treated differently from assigning any other
value to a pointer.

> The important thing here is that you are not explicitly required to CAST
> a NULL for fear of loss of significant bits, sign extension, or similar
> problems associated with cross-type comparisons (like comparing 0126 <
> '\240') which require explicit casting.

You are not required to cast a NULL in comparisons because the language
specifies that a 0 (or NULL) is to be converted to a null pointer of the
appropriate type in a comparison.  It does not, however, specify that such
conversions are done in function calls, so you *are* required to cast NULL
when it is used as an argument to a function.

> A popular convention is to use the "(char *)0" definition.  This can
> get confusing if your source code contains (int *)NULL.  Some (the
> bad ones) versions of lint give interesting results.

All versions of "lint" that I know of complain when you say something like

	(int *)(char *)0;

since you're casting a pointer of one type to a pointer of another type
which is, in general, dangerous.  In this particular case it's probably
safe; however, there's no reason to have code of that type in the first
place (see many articles explaining why defining NULL as "(char *)0" is
totally wrong).

	Guy Harris

mikel@codas.UUCP (Mikel Manitius) (10/06/85)

> > By the way, has anybody figured out how to get lint to shut up about
> > the return codes for { ,s,f}printf().  I have yet to see a standard definition
> > of the return code, but lint keeps giving me "value returned but not used".
> > Isn't this just a tramp from doprntf?  I've had one version hand back the
> > value of write (and blew away my stack) even though the return code was
> > supposed to return a char* (sprintf),  (Seems it didn't like writing a
> > 4k message).  This appears to be a good case for using if(sprintf(...)).
> 
> This subject has been discussed to death before.  In X3J11 and UNIX
> System V, the *printf() functions return a value (# chars transmitted,
> or negative on output error).  If you don't wish to test this value
> (shame on you), then cast it to (void).

I don't have any trouble with lint,
It never complains,
Then again, I never use then damn beast!
-- 
                                        =======
     Mikel Manitius                   ==----=====    AT&T
     (305) 869-2462 RNX: 755         ==------=====   Information Systems 
     ...{akguc|ihnp4}!codas!mikel    ===----======   SDSS Regional Support
     ...attmail!mmanitius             ===========    Altamonte Springs, FL
     My opinions are my own.            =======

roger@ll-sst (Roger Hale) (10/08/85)

There's something I've been missing in the if(p) discussion:
I see where K&R declare that comparing pointers with 0 is special (sec. 7.7),
where ``condition ? pointer : 0'' is special (sec. 7.13),
and where assigning 0 to pointers is special (sec. 7.14);
but I don't see where if(p) invokes the same mechanism.

    Section 7.7 Equality operators:
		expression == expression
		expression != expression
	A pointer may be compared to ... the constant 0 [machine-
    independently].  A pointer to which 0 has been assigned ... will appear
    to be equal to 0[.]

    Section 9.3 Conditional statement:
	if ( expression ) statement
	if ( expression ) statement else statement
    In both cases the expression is evaluated and if it is non-zero,
    the first substatement is executed.  In the second case the second
    substatement is executed if the expression is 0.

    Section 9.4 While statement:
	... remains non-zero ...

    Section 9.5 Do statement:
	... becomes zero ...

    Section 9.6 For statement:
	... becomes 0 ...

In 9.3 ``the expression is 0'' cannot mean the constant 0, but rather
that the expression evaluates to zero.  There are plenty of uses of
``0'', ``zero'', and ``non-zero'' referring to what an expression
evaluates to, and only three references that I've found (7.7, 7.13 and 7.14)
to the special meaning of the constant 0.  Initialization, section 8.6,
refers to assignment's conversions and so incorporates them; but for
the various conditional constructs I don't see how ``evaluates to zero''
can be taken equivalent to ``compares equal to the constant 0''.

In Sum:  it seems to me that ``if (p != NULL)'' is portable (NULL being 0)
			 but ``if (p)'' is not.

Please correct me if I be wrong, before I mislead countless others.

		Roger Hale	<roger@ll-sst.arpa>

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/08/85)

>     Section 9.3 Conditional statement:
> 	if ( expression ) statement
> 	if ( expression ) statement else statement
>     In both cases the expression is evaluated and if it is non-zero,
                                                    ^^^^^^^^^^^^^^^^^
This implies a comparison against 0.  Therefore if "expression" is a
pointer, this is comparison of a pointer with 0, which is the special
case previously discussed.  There is no concept of "evaluates to zero"
independently of "comparison with zero".

>     the first substatement is executed.  In the second case the second
>     substatement is executed if the expression is 0.

Don't take K&R too much as gospel.  It is an excellent book,
but it really did not try to specify everything to the level
that a language lawyer would want.  (X3J11 is trying to do that.)
K&R has to be read with the attitude of extracting the "spirit"
of the language from what is implied as well as from what is said.

chris@umcp-cs.UUCP (Chris Torek) (10/08/85)

> In [K&R, section] 9.3 ``the expression is 0'' cannot mean the
> constant 0, but rather that the expression evaluates to zero.
> There are plenty of uses of ``0'', ``zero'', and ``non-zero''
> referring to what an expression evaluates to, and only three
> references that I've found (7.7, 7.13 and 7.14) to the special
> meaning of the constant 0. [...]
> 
> In Sum:  it seems to me that ``if (p != NULL)'' is portable (NULL
> being 0) but ``if (p)'' is not.
> 
> Please correct me if I be wrong, before I mislead countless others.

Consider yourself corrected.  This rather loose definition in K&R
is one of the reasons there is an ANSI standard in the works.

In general, if K&R says `<x> is so', then you can be pretty darn
sure that <x> is so; but if they do not say `<y> is not', you will
just have to make your best guess.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

savage@ssc-vax.UUCP (Lowell Savage) (10/08/85)

In article <1671@brl-tgr.ARPA> ukma!david@ANL-MCS.ARPA (David Herron) writes:
> In article <516@talcott.UUCP> tmb@talcott.UUCP (Thomas M. Breuel) quotes:
> >In article <1671@brl-tgr.ARPA>, ART@ACC.ARPA (Art Berggreen) writes:
> >> From an abstract language viewpoint, an "if" statement conditionally
> >> executes a block of statements based on whether the control statement
> >> evaluates to a condition of *TRUE*.  Pointers by themself do not
> >> have attributes of TRUE vs FALSE.  Thus, "if(pointer)" makes less semantic
> 
> I disagree...  Pointers have VALID and INVALID values.  So when you
> say if(p) you're asking if it has a VALID or INVALID value.  It's
> just that conveniently most machines have 0 as the INVALID value.

No, C *GUARANTEES* that 0 is an invalid pointer value.  Page 192 of
my copy of K&R (this is the end of section 7.14 of appendix A) says:

	"...  However, it is guaranteed that assignment of the
	constant 0 to a pointer will produce a null pointer dis-
	tinguishable from a pointer to any object."

And page 97 (the middle of section 5.4 Address Arithmetic) says:

	"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, ...."

So I agree with David, except that this is not a machine-dependant thing,
it is defined into the C language!!  If the machine doesn't support it
directly, the compiler must generate whatever code it needs to support
"zero pointer = invalid pointer".  So in other words, the compiler has
builtin support for "VALID-pointer = TRUE" and "INVALID-pointer = FALSE",
and I find this concept very useful and helpful--as well as quicker.

				There's more than one way to be savage,

				Lowell Savage

marv@ISM780.UUCP (10/08/85)

>/* Written  7:21 pm  Oct  7, 1985 by gwyn@brl-tgr in ISM780:net.lang.c */
>>     Section 9.3 Conditional statement:
>>       if ( expression ) statement
>>       if ( expression ) statement else statement
>>     In both cases the expression is evaluated and if it is non-zero,
                                                    ^^^^^^^^^^^^^^^^^
>This implies a comparison against 0.  Therefore if "expression" is a
>pointer, this is comparison of a pointer with 0, which is the special
>case previously discussed.  There is no concept of "evaluates to zero"
>independently of "comparison with zero".

Just to add more flames to this theological discussion, from my reading of
section 9.3, I draw the inference that if p is a pointer then:

      if(p) statement

is not a valid construct in the C language and therefore questions of
portability are rendered moot.

My reasoning is as follows. The phrase, "In both cases the expression is
evaluated and if it is non-zero" does not imply comparison to zero. Instead
it implies that the expression must be of a numeric type since only numeric
data have the property of 'zeroness'. The expression consting only of a
pointer variable evaluates either to a pointer to some object or to a pointer
to no object. It does not evaluate to zero or not zero.

On the other hand, the expression p==0 is an expression of type int. In fact
it evaluates to the integer 0 or 1 (K&R section 7.6 Relational operators).

Parenthetically, the symbol '0' is overloaded in the C language. In the
context of a pointer it *represents* the constant that points to no object.
in the context of a number it *represents* the constant with the value zero.
i.e., the symbol '0' is not the value of either of the the constants, it is
mearly the *name* used to identify either of the constants in the *source*
from of a program.

I also suggest that if we take the stance that "evaluates to zero" implies
comparison to zero we could also claim that:
    i = 0;
    if (p == i)
is equivalent to:
   if (p == 0)
and I believe that most C theologians would reject the above equivalence.

The fact the K&R is very fuzzy about the type of an expression (as opposed to
the types of primitive operands) leads to many a theological discussion.

George Bool sure simplified my life,
	  Marv Rubinstein  -- Interactive Systems

peter@graffiti.UUCP (Peter da Silva) (10/13/85)

Re: Marv Rubenstein's message.

int i;
foo *p;

	i=0;
	if(p==i)...

What's wrong with this? It's identical to "if(p==(foo *)i)" according to the
default expression evaluation rules. Any special meaning of 0 should be handled
in the evaluation of (foo *)i.

harged%ti-eg.csnet@CSNET-RELAY.ARPA (Richard_Hargrove) (10/13/85)

It seems to me that all the discussion about what
         .
     PTR p;              /* pointer to something... */
         .
         .
     if (p) {
         .
     }

means, whether it is good style, &c... is overlooking something.
Nowhere in K & R is the meaning of the distinguished constant
NULL defined. I thought its definition was implementation
dependent. It just so happens that all implementations I know of
use 0 (or 0L). But nothing *requires* this. That implies to me
that the above coding practice is implementation dependent,
whereas

     if (p != NULL) ...

or

     if (not_null (p)) ...

(given some magic boolean function (or macro) that returns TRUE
if p != NULL else FALSE) are implementation independent. A trivial
macro

#define not_null(p) ((p))

will suffice if your implementation defines NULL as 0.

I won't address the stylistic problems associated with using
non-boolean expressions in a boolean context, regardless of what
the language "allows", but the above macro should solve any
problems in this area also.

regards,
Richard Hargrove    harged%ti-eg
================    ============

ross@smeagol.UUCP (Gary_Ross) (10/14/85)

> 
> section 9.3, I draw the inference that if p is a pointer then:
> 
>       if(p) statement
> 
> is not a valid construct in the C language and therefore questions of
> portability are rendered moot.
> 
> My reasoning is as follows. The phrase, "In both cases the expression is
> evaluated and if it is non-zero" does not imply comparison to zero. Instead
> it implies that the expression must be of a numeric type since only numeric
> data have the property of 'zeroness'. The expression consting only of a
> pointer variable evaluates either to a pointer to some object or to a pointer
> to no object. It does not evaluate to zero or not zero.

	As I understand it, the C compiler interprets pointers as integers in terms
of evaluating an if statement. This means that if the value of a pointer is 
null, the result of:

			if (p) statement1
			else statement2
	
will execute statement2 because a null pointer has value 0. It is conceptually
very nice to think of numbers having numeric properties and pointers having
properties of either pointing to an object or not, but when you get right down
to it pointers are nothing more than numeric variables containing memory
addresses. Therefore the concept of using the value of a pointer in an if
statement makes perfect sense especially since the C language is in general
not very strict on typing of variables.

> Parenthetically, the symbol '0' is overloaded in the C language. In the
> context of a pointer it *represents* the constant that points to no object.
> in the context of a number it *represents* the constant with the value zero.
> i.e., the symbol '0' is not the value of either of the the constants, it is
> mearly the *name* used to identify either of the constants in the *source*
> from of a program.

	I disagree. I think it is very concise to have the value '0' represent a
false result of a logical operation and the value of an invalid pointer. It
seems to me that this makes statements like if(p) neat and simple, as well
as obviously requesting the validity of a pointer.

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/14/85)

NULL has to be defined as 0 since that is a special value for
pointers (guaranteed by the C language spec), which is just
what NULL is meant to represent.

roger@celtics.UUCP (Roger Klorese) (10/17/85)

In article <2098@brl-tgr.ARPA> harged%ti-eg.csnet@CSNET-RELAY.ARPA (Richard_Hargrove) writes:
>
>means, whether it is good style, &c... is overlooking something.
>Nowhere in K & R is the meaning of the distinguished constant
>NULL defined. I thought its definition was implementation
>dependent. It just so happens that all implementations I know of
>use 0 (or 0L). But nothing *requires* this.

In fact, I've worked in an environment (PRIMOS and other Multics-style systems)
with segmented addressing schemes (and demand-paged virtual memory) wherein 
the definition of a NULL pointer was any address in the designated-highest-
possible segment (defined as octal 7777).  Thus, for C to see a pointer as 
null required conversion from 7777/0 to 0 (or seg 0 loc 0, which is a valid
address) and back again!  

In summary:

YEAH, a portable test is necessary that does not depend on NULL being ZERO.

-- 
--------------------------------------------------------------------------------
| ... "What were you expecting, rock'n'roll?"                                  |
|Roger B.A. Klorese                                                            |
|Celerity Computing, 40 Speen St., Framingham, MA 01701, (617) 872-1772        |
|UUCP:                                 ARPA:                                   |
|decvax-\    bang-\                        celerity!celtics!roger@sdcsvax.ARPA |
|ucbvax--\   akgua-\                                                           |
|ihnp4----\-sdcsvax-\-celerity!celtics!roger                - or -             |
|- or -                                      celtics!roger@bu-cs.ARPA          |
|seismo----\harvard---\bu-cs!celtics!roger                                     |
--------------------------------------------------------------------------------

guy@sun.uucp (Guy Harris) (10/17/85)

> What's wrong with this? It's identical to "if(p==(foo *)i)" according to the
> default expression evaluation rules. Any special meaning of 0 should be
> handled in the evaluation of (foo *)i.

No, it shouldn't.  According to K&R, the rules for conversion between
pointers and integers are machine-dependent but are supposed to be
"unsurprising".  I'd be surprised as hell if, assuming a null pointer had
some value other than all zero bits, this conversion produced a null pointer
if i is zero.  K&R takes great pains to insist that the interpretation of 0
as a null pointer is only to be taken if the 0 is a *constant* 0.

	Guy Harris

chris@umcp-cs.UUCP (Chris Torek) (10/18/85)

> From: ross@smeagol.UUCP (Gary_Ross)

> As I understand it, the C compiler interprets pointers as integers
> in terms of evaluating an if statement.

Your C compiler does, and mine does; but `the' C compiler does not.

> This means that if the value of a pointer is null, the result of:
>			if (p) statement1
>			else statement2
> will execute statement2

Correct.

> because a null pointer has value 0.

Wrong.  Not because a null pointer has the integer value zero, but
because the C language requires this.  On a sufficiently bizarre
machine, the null pointer might be represented by the double-precision
floating-point value 1.179e+7001; yet on such a machine, if the
compiler is correct, and the value of a pointer `p' is nil---compares
equal to zero, so that `(p == 0)' is true---then `if (p) s1; else
s2;' will execute s2.

Of course, most if not all current architectures do indeed represent
a null pointer with 0 or 0L; this makes code generation simpler.
But it is not required.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

jak@adelie.UUCP (Jeff Kresch) (10/18/85)

> Re: Marv Rubenstein's message.
> 
> int i;
> foo *p;
> 
> 	i=0;
> 	if(p==i)...
> 
> What's wrong with this? It's identical to "if(p==(foo *)i)" according to the
> default expression evaluation rules. Any special meaning of 0 should be
> handled in the evaluation of (foo *)i.

We seem to be going around in circles with this one.

First, it is stated clearly in K&R that the only integer that may be
compared with a pointer is the *constant* zero.  (foo *)i is not a
constant.  Any other comparisons between integers and pointers is
machine dependent, regardless of whether the integer is casted correctly
or not.  

The reasons for this rule are not hard to understand from the point of
view of implementing a compiler producing efficient code.  Checking for
the constant zero can be done at compile-time.  The compiler just has to
substitute a different value if one is required.  Checking for a
variable containing the value zero would require that the compiler put
in extra code to do this at run time.  This *code* (not the compiler)
would have to check the value of the variable, and if it was zero,
substitute the proper null value in the comparison, if need be.

Looking at it from this perspective, it is easy to see why 

        if (p)

is reasonable.  This is always taken to mean

        if (p != 0)

and can be detected at compile-time.  The compiler can substitute the
appropriate value if it is required.



                                                They call me
                                                JAK

guy@sun.uucp (Guy Harris) (10/21/85)

> In fact, I've worked in an environment (PRIMOS and other Multics-style
> systems) with segmented addressing schemes (and demand-paged virtual
> memory) wherein  the definition of a NULL pointer was any address in
> the designated-highest-possible segment (defined as octal 7777).  Thus,
> for C to see a pointer as null required conversion from 7777/0 to 0 (or
> seg 0 loc 0, which is a valid address) and back again!

No, it didn't.  The author of the C compiler may have thought so, but if
he/she did then somebody else should have been chosen to write the compiler.
On such a machine,

1) NULL should have been #defined as 0 (just like everywhere else)

and

2) the statement

	if (p)
		foo();

should have compiled into something like

	compare_immediate	p, #07777/0	# seg 7777, loc 0
	beq			around
	subroutine_call		foo
around:

> YEAH, a portable test is necessary that does not depend on NULL being ZERO.

The definition of the C language requires that NULL be ZERO (how many times
do people have to be told this?).  The test

	if (p)

is already portable enough, as is the test

	if (p != 0)

and the test

	if (p != NULL)

In the aforementioned class of machines, all three test should compile into
code like the code listed above.

	Guy Harris

peter@graffiti.UUCP (Peter da Silva) (10/21/85)

Question: isn't this all rather moot? I mean, what sort of architecture could
you actually run 'C' on that doesn't allow the use of zero as a null pointer?
If nothing else you can always add a shim at 0000:0000 (or whatever weird
method you use for indicating segments). I mean: there's nothing in the PDP-11
architecture that says that 0 is a natural value for NULL. In fact 1 or FFFF
would probably make more sense (seeing as they're odd addresses).

peter@graffiti.UUCP (Peter da Silva) (10/23/85)

> > If nothing else you can always add a shim at 0000:0000 (or whatever weird
> > method you use for indicating segments)...
> 
> Shame on you Peter, for not reading the entire net.lang.c archives before
> asking this question :-).

You're right. I'll run right out & read them this very minute.

>                            Some while ago this very issue was raised:  some
> poor people were faced with implementing C on an architecture which did
> interesting things with the high-order bits of pointers, such that any
> attempt to treat an all-0 value as a pointer caused a trap.

Well, what was it doing with these high bits? Anything meaningful in terms
of where it's pointing? If not, you could always mask them out when doing
compares with a pointer.

>                                                              Not just any
> attempt to follow the pointer, mind you, but any attempt to do *anything*
> with it.  Trapping *NULL is one thing; trapping foo=NULL is unacceptable.
> So they could not use all-0 as NULL.

Um, treating an all-0 value as a pointer? But what operation do you do on
pointers apart from dereferencing that could be construed as doing something
with a *pointer* as opposed to doing something with a *variable*? Sounds like
some net.bizarre sort of tagged architecture, in which case you can throw
portability out the window anyway. I mean, what would this do to implicit type
conversion, just for starters? OK, let's rephrase that. What sort of
architecture could you actually implement 'C' on, and reasonably easily port
programs to and from, that wouldn't let you use 0 as a pointer?
-- 
Name: Peter da Silva
Graphic: `-_-'
UUCP: ...!shell!{graffiti,baylor}!peter
IAEF: ...!kitty!baylor!peter

henry@utzoo.UUCP (Henry Spencer) (10/23/85)

> Question: isn't this all rather moot? I mean, what sort of architecture could
> you actually run 'C' on that doesn't allow the use of zero as a null pointer?
> If nothing else you can always add a shim at 0000:0000 (or whatever weird
> method you use for indicating segments)...

Shame on you Peter, for not reading the entire net.lang.c archives before
asking this question :-).  Some while ago this very issue was raised:  some
poor people were faced with implementing C on an architecture which did
interesting things with the high-order bits of pointers, such that any
attempt to treat an all-0 value as a pointer caused a trap.  Not just any
attempt to follow the pointer, mind you, but any attempt to do *anything*
with it.  Trapping *NULL is one thing; trapping foo=NULL is unacceptable.
So they could not use all-0 as NULL.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

jdb@mordor.UUCP (John Bruner) (11/02/85)

In article <366@graffiti.UUCP> peter@graffiti.UUCP (Peter da Silva) writes:
>Um, treating an all-0 value as a pointer? But what operation do you do on
>pointers apart from dereferencing that could be construed as doing something
>with a *pointer* as opposed to doing something with a *variable*? Sounds like
>some net.bizarre sort of tagged architecture, in which case you can throw
>portability out the window anyway. I mean, what would this do to implicit type
>conversion, just for starters? OK, let's rephrase that. What sort of
>architecture could you actually implement 'C' on, and reasonably easily port
>programs to and from, that wouldn't let you use 0 as a pointer?

A few months ago I described the problems that the incorrect assumption
that (foo *)0 has an all-zero-bits representation caused when we were
attempting to transport C programs to our machine, the S-1 Mark IIA.

The Mark IIA has a 36-bit word.  Pointers are partitioned into a
5-bit tag and a 31-bit address.  The different tag values specify
different protection rings (ring 0 == kernel, ring 3 == user) and
special pointer types (nil, self-relative).  [We did not use
self-relative pointers in C.]  The tag values 0 and 31 are reserved
as invalid.  Any attempt to manipulate a pointer with an invalid
tag causes a trap.  Since most integers will be either small
negative or positive numbers, this helps to detect the use of
uninitialized pointers.

Just as floating-point numbers are manipulated differently than
integers, there are instructions to copy and manipulate pointers.
An attempt to copy an all-zero word with a pointer instruction
causes a trap.

Pointers with the "nil" tag can be copied, but they cannot be used
in address expressions.  Of course, "nil" cannot be de-referenced.

Pointer to integer coercions are possible (just as conversions
between floating-point and integers are possible).  However,
K&R does not guarantee that all integers can be copied into a
pointer.  It only guarantees that (given sufficiently large
integers, conversion from pointer to integer to pointer will
return the original pointer.  In particular, it is *not*
guaranteed that you can always do something like this:

	p = (char *)10;

After fighting the tide of sloppy coding practices, we finally
gave in and changed the meaning of tag 0 (in microcode) and
adopted an all-0 bit pattern for NULL.  It was wrong, it was
ugly, but it was the only expedient thing to do.

If C is to survive as a language it must be allowed to escape
its PDP-11 origins (and I include the VAX [and to some degree
the 68000] in this category).  (foo *)0 is not an all-zero
bit pattern; it may not even be a memory address.  Although
this may be the implementation on some (or even most) machines,
the abstract idea should not be confused with the most common
implementation.
-- 
  John Bruner (S-1 Project, Lawrence Livermore National Laboratory)
  MILNET: jdb@mordor [jdb@s1-c.ARPA]	(415) 422-0758
  UUCP: ...!ucbvax!dual!mordor!jdb 	...!seismo!mordor!jdb

jsdy@hadron.UUCP (Joseph S. D. Yao) (11/03/85)

In article <2910@sun.uucp> guy@sun.uucp (Guy Harris) writes:
>The definition of the C language requires that NULL be ZERO (how many times
>do people have to be told this?).  ...

I really hate contradicting Guy, especially when I don't have my
references at hand.  But my memory tells me that 0 should convert
to the NULL pointer, not necessarily be it.  Guy's hand compilation
a few lines earlier shows how:

>2) the statement
>	if (p) foo();
>should have compiled into something like
>	compare_immediate	p, #07777/0	# seg 7777, loc 0
>	beq			around
>	subroutine_call		foo
>around:

This is recent in my accessible memory because this issue has been
DISCUSSED TO DEATH n times already.  Please do not respond.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

henry@utzoo.UUCP (Henry Spencer) (11/05/85)

> Um, treating an all-0 value as a pointer? But what operation do you do on
> pointers apart from dereferencing that could be construed as doing something
> with a *pointer* as opposed to doing something with a *variable*? ...

The distinction you are drawing is not portable.  You are assuming that
one can treat something as a variable without implying a type.  This is
not necessarily true.  It is conceivable, for example, that pointers are
not the same size as any of the integer types, in which case manipulating
them using integer instructions isn't possible.	 Or, as in the case John
Bruner explained (which is the one I was thinking of), there are good
reasons for *wanting* to use the instructions which know that the data
item is a pointer.  For example, I think on his machine it's not possible
to load something into an address register without getting it checked for
validity as a pointer, and doing things like pointer assignments while
never using the address registers is awkward.

No, you don't need an architecture radically unsuited to C to get conditions
where all-0 is unsuitable as the NULL pointer.  A slightly odd architecture
with a bit more knowledge of datatypes than the usual ibm360/pdp11 clone
is all it takes.  Yes, software portability to and from such machines is
meaningful and useful.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

dg@sal.UUCP (Dag Gruneau) (11/05/85)

In article <4173@mordor.UUCP> jdb@mordor.UUCP (John Bruner) writes:
>Pointer to integer coercions are possible (just as conversions
>between floating-point and integers are possible).  However,
>K&R does not guarantee that all integers can be copied into a
>pointer.  It only guarantees that (given sufficiently large
>integers, conversion from pointer to integer to pointer will
>return the original pointer.


K&R does not guarantee anything. See p. 102, "on most machines
a pointer can be assigned to an integer and back without
changing it" that's far from a guarantee!

-- 

	Dag Gruneau
	Stockholm, Sweden

UUCP:  {seismo,decvax,philabs}!mcvax!enea!sal!dg
ARPA:  decvax!mcvax!enea!sal!dg@berkeley.arpa
       mcvax!enea!sal!dg@seismo.arpa

guy@sun.uucp (Guy Harris) (11/11/85)

> >The definition of the C language requires that NULL be ZERO (how many times
> >do people have to be told this?).  ...
> 
> ...my memory tells me that 0 should convert to the NULL pointer, not
> necessarily be it.

True, but NULL isn't the null pointer any more than 0 is; NULL is just an
alternate way of saying "0".  Think of it as a comment; it doesn't tell the
compiler anything, but it tells the human reader of the code something.
NULL must be #defined to be 0 in a correct C implementation.  Null pointers
need not have the same bit pattern as 0 or 0L.
 
> ...this issue has been DISCUSSED TO DEATH n times already.  Please do
> not respond.
 
AMEN.  There is only *ONE* correct response to the question "does the
definition of the C language require that a null pointer have the same bit
pattern as the integral value 0"; the answer is NO.  If anybody disagrees,
go back and read K&R and/or Harbison and Steele until you see why there is
no reason for the answer to be YES; please don't waste your time, our time,
and all the readership's time arguing the point.

        Guy Harris

guy@sun.UUCP (09/08/86)

(This discussion has ceased to be related to UNIX; it is now a discussion of
C, so it is being moved to net.lang.c.)

> > It DOESN'T???  Gee, then maybe you have a busted malloc().  If malloc()
> > fails, it should return the null pointer.  That is what is being tested in
> > the clause 'if(p=malloc(strlen(s)+1))'.  As so many people have expounded
> > in net.lang.c:  a null pointer is semantically the same as zero in the C
> > language (except for sizeof(), should pointers exist of sizes other than
> > sizeof(int)).

> But if a pointer is 4 bits and an int is 2 bits (some 68000
> implementations), you'll get spurious failures if the pointer returned by
> malloc is a multiple of 0x10000!

Not if you've declared "malloc" to return a "char *", you won't.  All C
compilers will generate equivalent code for

	extern char *malloc();
	char *p;
	unsigned int i;

	if (p = malloc(i))
		foo();

and

	extern char *malloc();
	char *p;
	unsigned int i;

	if ((p = malloc(i)) != 0)
		foo();

Any compiler that does *not* generate equivalent code in these two cases is
not a C compiler; it compiles some other language that may look like C in
some ways, but is not C.  If it was intended to be a C compiler, it has a
bug.  (Any compiler that does not generate equally efficient code for these
two constructs is *also* broken, since they both mean the same thing in C.)

If you haven't declared "malloc" to return a "char *", this code will rarely
if ever work on a machine where "char *" and "int" have different sizes,
regardless of whether "malloc" succeeds or fails.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)