Well, you obviously know more about it than I do.

And yes, foo = 0 might set foo to 0x7fffffff is certainly astonishing.

But I get your point. (And yes, I programmed on the Intel 8085, and 8086, and 
Zylog Z80 (the first Z processor!) and I remember that goofy 20-bit segment + 
offset.)

What about char *foo = 0; if ( foo ) ... ; Would that then evaluate as true or 
false? Must be false; otherwise all those if ( ! f = fopen() ) would fail. But 
false is a little counterintuitive also.

Could not the Z (our Z!) implementation of C used something like 7FFFF000 for 
NULL and thereby trapped the dereferencing of NULL? (And yes, some provision 
would have had to be made for the PSA.)

Charles


-----Original Message-----
From: IBM Mainframe Assembler List [mailto:[email protected]] On 
Behalf Of Bob Raicer
Sent: Friday, September 11, 2020 4:19 PM
To: [email protected]
Subject: Re: Deep Cuts

On Wed, 9 Sep 2020 13:17:24 -0700 Charles Mills wrote:

 > Doesn't that say "0 is never a valid address"?  Or at least "zero
 > never compares equal to the address of any actual object"? "0 is
 > never equal to any valid address"?  Seems about the same to me.

No, it doesn't say "0 is never a valid address".  It says that the
constant expression value of zero when used as an assignment to a
pointer variable is semantically recognized as meaning assigning an
implementation defined value to that pointer.  This implementation
defined value can be thought of as a "special" address value which
satisfies the requirement that it "is guaranteed to compare unequal
to a pointer to any object or function."  That dereferencing it may
cause some form of an exception (absolutely machine and/or
implementation dependent) may be an added bonus; it is not required
by the standard and the standard simply states that the behavior is
undefined.

So, if some implementation defined this value as perhaps
0x7FFFFFFF, then the assignment:

char *somepointer = 0;

would set somepointer to 0x7FFFFFFF (assuming a pointer to char is
4-bytes in length).

Similarly, a statement like this:

if (0 == somepointer) ...

would cause a comparison to the value 0x7FFFFFFF, not zero.

The special treatment of the constant expression of zero in these
contexts is quirky and a source of great confusion.  This
comp.lang.c FAQ aims to clarify the confusion:

URL: http://c-faq.com/null/machnon0.html

   comp.lang.c FAQ list ยท Question 5.5

   Q:  How should NULL be defined on a machine which uses a nonzero
   bit pattern as the internal representation of a null pointer?

   A:  The same as on any other machine:  as 0 (or some version of
   0; see question 5.4).

   Whenever a programmer requests a null pointer, either by writing
   "0" or "NULL", it is the compiler's responsibility to
   generate whatever bit pattern the machine uses for that null
   pointer.  (Again, the compiler can tell that an unadorned 0
   requests a null pointer when the 0 is in a pointer context; see
   question 5.2.)  Therefore, #defining NULL as 0 on a machine for
   which internal null pointers are nonzero is as valid as on any
   other:  the compiler must always be able to generate the
   machine's correct null pointers in response to unadorned 0's seen
   in pointer contexts.  A constant 0 is a null pointer constant;
   NULL is just a convenient name for it (see also question 5.13).

   (Section 4.1.5 of the C Standard states that NULL "expands to an
   implementation-defined null pointer constant," which means that
   the implementation gets to choose which form of 0 to use and
   whether to use a void * cast; see questions 5.6 and 5.7.
   "Implementation-defined" here does not mean that NULL might be
   #defined to match some implementation-specific nonzero internal
   null pointer value.)

Going back to the old Intel 808x real mode memory addressing model,
a pointer in C could be represented as a 16-bit (unsigned) integer.
The effective address was arrived at by the processor by left
shifting by 4-bits the value contained in a segment register and
then adding the 16-bit "address" (i.e., an offset) value.  So it
was perfectly permissible to have some C pointer contain the value
zero if the item being pointed to was at offset zero from the value
contained in the segment register.

C.A.R. Hoare apologized for the invention of the null reference. The
quote that I found on Wikipedia is shown below:

"I call it my billion-dollar mistake.  It was the invention of the null
reference in 1965.  At that time, I was designing the first
comprehensive type system for references in an object oriented language
(ALGOL W).  My goal was to ensure that all use of references should be
absolutely safe, with checking performed automatically by the compiler.
But I couldn't resist the temptation to put in a null reference, simply
because it was so easy to implement.  This has led to innumerable
errors, vulnerabilities, and system crashes, which have probably caused
a billion dollars of pain and damage in the last forty years."

I have worked as a software developer on a hardware platform where
the hardware actually recognized a "special" address value (which
was not zero) as being always invalid and this value was the
implementation defined value of the null pointer.  A zero pointer
value, while a little awkward to generate, was totally valid.

In my view, C was developed as a somewhat high level programming
language which still allowed relatively easy and run-time efficient
access to lower level machine facilities.  Source code portability
was a goal, however, this goal was not intended to severely limit
the programmer; implementation and platform dependencies are
discouraged, but not disallowed.  That's not to say that
organizations might not have more stringent requirements in that
regard (which was one of the motivations for the creation of
programs like "lint").

Bob

Reply via email to