[comp.lang.c] To comment 48: Xenix

lopez@uiucdcsp.cs.uiuc.edu (04/25/87)

Reply to Xenix Comments by Dave.

Being a User of Microport's Unix System V I don't have to deal with
the madness of Microsoft.

The problem of having pointers that are not the same length as integers
cost me a day and alot of steam. But I think that declaring:

#define NULL (char *)0

is expensive but will always do the trick for all models (small,large,huge)

On UNIX System V AT I had done the typical tricks in C of:

   if (!charptr)

   Even though I new it was crypt, but efficient.

   But all of that worked while I was in the small memory model, when
   I went to large I core dumped my brain.

   For now pointers were 32 bits instead of 16 bits, 
   but integers are 16 bits.

   So after alot of frustration I figured out that NULL was defined
   as 0L. Which makes sense since we need a 32 bit zero value, which
   is not an integer.

   But As for people saying that if (charptr == 0) should work,
   "for any good C programmer's" I think thats hogwash.

   On every system I have ever used if (!charptr) worked,
   but I am happy that System V/AT has finally forced me to
   write code that is less crypt.

   if (charptr == NULL)

   is easier to read by everyone, and less likely to be a source of 
   unexpected bugs.

   As for Microport's product, I think it is the real McCoy. Xenix
   is for people who have no class.

   Frank Lopez
   University of Illinois

lopez@uiucdcsp.cs.uiuc.edu (04/25/87)

A biased reply of course.

chris@mimsy.UUCP (Chris Torek) (04/26/87)

In article <77200001@uiucdcsp> lopez@uiucdcsp.cs.uiuc.edu writes:
>The problem of having pointers that are not the same length as integers
>cost me a day and alot of steam. But I think that declaring:
>#define NULL (char *)0
>is expensive but will always do the trick for all models (small,large,huge)

This is wrong:  It will not always work.  In fact, it will not work
on at least two existing machines, both of which have C compilers.
(DG MV series and PR1ME series have 48 bit char pointers, but 32
bit word (integer, structure, &c) pointers.  Passing one (char *)0
and one int to a function that expects one (int *)0 followed by one
int will give you a much-deserved surprise.)

>On UNIX System V AT I had done the typical tricks in C of:
>    if (!charptr)
>Even though I new [sic] it was crypt [sic], but efficient.

There is really nothing illegal about this, although it is considered
bad style by many.  It is also no more efficient than

	if (charptr == NULL)

or

	if (charptr == 0)

---both of which MUST mean the same thing, or the compiler or the
environment is broken.

>... after alot of frustration I figured out that NULL was defined
>as 0L. Which makes sense since we need a 32 bit zero value, which
>is not an integer.

	double pow();
	extern double no;

	printf("%g\n", pow(no, 1000.0));

You do not need a 32 bit zero value.  You need a null pointer to
char.  A null pointer to char is not a 32 bit zero value.  It is
not a 16 bit zero value.  It is not an 18 bit zero value.  It is
not a 36 bit zero value.  It is not a 48 bit zero value.  It is
all of these, and none.

It is a null pointer to char.

A null pointer to char is a null pointer to char.

A null pointer to char is a null pointer to char.

(What I tell you three times is true.)

>But As for people saying that if (charptr == 0) should work,
>"for any good C programmer's" I think thats hogwash.

It has nothing to do with programmers.  Any correct C *compiler*
will compare `charptr' with an appropriately typed null pointer,
not with a 16 bit integer zero, nor with a 32 bit integer zero,
nor with an 18 bit integer zero, nor a 36 bit zero nor a 48 bit
zero.  All of these happen to correspond to certain machines' ideas
of null pointers, but a null pointer is a null pointer, not any of
these odd objects.

In an assignment or comparison context when the other operand is
a pointer, the integer constant value zero is converted to a null
pointer of the appropriate type.  In other contexts, no conversion
is performed, and the programmer must provide an appropriate cast.
It is legal (though unnecessary) to write

	if (charptr == (char *) 0)

or

	if (charptr == (char *) NULL)

It is *not* legal (with an exception mentioned below) to write

	n = scandir(dir, &names, NULL, NULL)

because scandir wants two pointers to function returning integer.
It is legal and necessary to write

	n = scandir(dir, &names, (int (*)()) NULL, (int (*)()) NULL)

or something equivalent.

If you run lint, it will properly complain about incorrect function
calls.

Exception:  If you use a dpANS-conformant compiler with function
prototypes, the function prototype can supply assignment context
for function parameters, making the first of the two `scandir'
calls legal.  Of course, there must be a scandir prototype in
scope at the time of the call.

>On every system I have ever used if (!charptr) worked,

(because it must)

>    if (charptr == NULL)
>    is easier to read by everyone, and less likely to be a source of 
>    unexpected bugs.

(I agree.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP:	seismo!mimsy!chris	ARPA/CSNet:	chris@mimsy.umd.edu

guy@gorodish.UUCP (04/27/87)

>But I think that declaring:
>
>#define NULL (char *)0
>
>is expensive but will always do the trick for all models (small,large,huge)

And will also tick "lint" (and PCC, in some cases, I suspect) with
some justification.

>On UNIX System V AT I had done the typical tricks in C of:
>
>   if (!charptr)
>
>   Even though I new it was crypt, but efficient.

If

	if (!charptr)

is any more efficient than

	if (charptr == 0)

you have some justification for flaming the author(s) of your
compiler.  The two constructs are equivalent, and any good compiler
will produce equally efficient code for them.

>   So after alot of frustration I figured out that NULL was defined
>   as 0L. Which makes sense since we need a 32 bit zero value, which
>   is not an integer.

No, it makes no sense whatsoever because:

	1) a 32-bit zero value *is* an integer; it just doesn't
	happen to be an "int";

and

	2) "we" don't need a 32-bit zero value, we need a null
	pointer of the appropriate type, and null pointers are not
	integers.

>   But As for people saying that if (charptr == 0) should work,
>   "for any good C programmer's" I think thats hogwash.

Anyone who knows the C language well enough to make their comments on
this newsgroup worth believing knows that

	if (charptr == 0)

should work.  Anyone who knows the C language well enough that they
should feel competent to implement it knows that it should work.

lopez@uiucdcsp.cs.uiuc.edu (04/29/87)

>/* Written  1:45 pm  Apr 27, 1987 by guy%gorodish@Sun.COM in uiucdcsp:comp.lang.c */
>you have some justification for flaming the author(s) of your
>compiler.  The two constructs are equivalent, and any good compiler
>will produce equally efficient code for them.

The above is irrelevant since I believed that
the null-pointer was equivalent to (int)zero. "apparently not true"
Plus I assumed a "dumb" compiler "in this day and age is very common."

>
>	1) a 32-bit zero value *is* an integer; it just doesn't
>	happen to be an "int";

True, but irrelevant:
(integer was used in terms of the latter, that doesn't need explanation).

>
>	2) "we" don't need a 32-bit zero value, we need a null
>	pointer of the appropriate type, and null pointers are not
>	integers.

paraphase: (null pointers internally are not integers)

When you enter (charptr == 0) in your "C" source code,
when the compiler takes its first pass and sees the zero, it will first
identify it as a zero and then see that it is a special case and make
the transformation.  If I am wrong, tell me.

for,

page  97 "K&R" 
"In general, integers cannot meaningfully be assigned to pointers;
zero is a special case."
::Quote.

By definition zero is an integer.
::Math.

Good argument for not having NULL being "0".
::Common Sense.

If NULL is not (int)zero, then C should not be using the symbol "0"
to represent it and (int)0.
::Statement.
:-> but by page 97 it appears that NULL is (int)zero and that it 
:-> is up to the compiler to take care of the special case.

Lastly,
   p. 192 says:

      "the assignment of the constant 0 to a pointer will produce a 
       null pointer".

       Meaning? the pointer now has the value of zero.
       (or can this be seen as compiler dependent).

       Can each compiler writer have their own way of representing 
       a NULL pointer (internally).

       We have two different types (pointers and integers)
       I seemed to have forgotten that; been writing too many
       lines of "C" lately.
>
>/* End of text from uiucdcsp:comp.lang.c */

guy%gorodish@Sun.COM (Guy Harris) (05/01/87)

> >	1) a 32-bit zero value *is* an integer; it just doesn't
> >	happen to be an "int";
> 
> True, but irrelevant:
> (integer was used in terms of the latter, that doesn't need explanation).

Oh, yes it does.  The term "integral type" is used both in K&R and in the
ANSI C draft, and does NOT just refer to "int".  0L is an integer in
C, even though it is not (necessarily) an "int".

> If NULL is not (int)zero, then C should not be using the symbol "0"
> to represent it and (int)0.

I agree, given all the confusion overloading the symbol "0" causes,
but it's too late to fix things now.

>       "the assignment of the constant 0 to a pointer will produce a 
>        null pointer".
> 
>        Meaning? the pointer now has the value of zero.
>        (or can this be seen as compiler dependent).

Nope.  The pointer now has the value of a null pointer of the
appropriate type.  On some implementations the pointer in question
may have all its bits zero; on other implementations, it may not.

>        Can each compiler writer have their own way of representing 
>        a NULL pointer (internally).

I presume by "internally" you mean "internally to the generated
code".  (How the compiler works internally is irrelevant; it is a
black box.)  A compiler writer can represent NULL pointers with any
bit pattern they choose, as long as NO pointer to any object will
have that same bit pattern (I include tag bits, etc. here - if you
have tag bits, you may represent a null pointer with the same
non-tag-bits that represent a pointer to an object, as long as the
tag bits are different and as long as this difference will cause a
null pointer not to compare equal to said pointer value).

>        We have two different types (pointers and integers)

We have a lot more types than that.  We have several different
integer types, two floating-point types (three in ANSI C), a
theoretically infinite set of structure types, a theoretically
infinite set of union types, a theoretically infinite set of
enumerated data types (yes, I know they are considered "funny"
integral types in ANSI C), and as many types of pointers, arrays, and
functions as we have data types plus "pointer to void" and "function
returning "void" (the set of function types is "bigger" in ANSI C in
that a function's type includes the types of its arguments).

(The astute reader will note that this implies that the cardinality
of the set of data types is infinite; in theory, it sure is.  In
practice, compilers will probably impose a limit on how many levels
of pointer indirection they will support, as well as limits on how
many different structure, union, and enumerated types they will
support.)

jimp@cognos.uucp (Jim Patterson) (05/02/87)

In article <6464@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>>But I think that declaring: 
>>#define NULL (char *)0
>>is expensive but will always do the trick for all models (small,large,huge)
>
>This is wrong:  It will not always work.  In fact, it will not work
>on at least two existing machines, both of which have C compilers.
>(DG MV series and PR1ME series have 48 bit char pointers, but 32
>bit word (integer, structure, &c) pointers.)

I've no experience on PRIME, but I do know that the DG MV series
uses 32 bit pointers exclusively (for both char and word pointers).
They are represented differently (a char pointer is the corresponding
word pointer shifted one bit left, with the low-order bit
addressing one char of the corresponding word).  However, the
representation of NULL is the same, 0 in both cases.  In fact, because
an int is also 32 bits with DG's C compiler, there are very few reasonable
ways of defining NULL that won't work over function calls (I think that
all of the alternatives that have flown by this discussion would
in fact work).
-- 
Jim Patterson
Cognos Inc.

roger@celtics.UUCP (Roger Klorese) (05/06/87)

In article <641@cognos.UUCP> jimp@cognos.UUCP (Jim Patterson) writes:
>In article <6464@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>>>But I think that declaring: #define NULL (char *)0
>>>is expensive but will always do the trick for all models (small,large,huge)
>>
>>This is wrong:  It will not always work.  In fact, it will not work
>>on at least two existing machines, both of which have C compilers.
>
>I've no experience on PRIME...

Well, I DO... let's talk about it.

The Prime 50 series uses a segmented architecture.  The null pointer is
defined as a pointer to segment 7777(octal), location 0.  NULL must be
defined as 0, NOT (char *)0, for the compiler and runime environment to
test this properly.

By the way, character pointers are indeed a 48-bit quantity, containing 
segment number, protection ring, location (word), and byte-offset.
-- 
 ///==\\   (No disclaimer - nobody's listening anyway.)
///        Roger B.A. Klorese, CELERITY (Northeast Area)
\\\        40 Speen St., Framingham, MA 01701  +1 617 872-1552
 \\\==//   celtics!roger@seismo.CSS.GOV - seismo!celtics!roger