Topic: (Thing *)0" considered Bad?


Author: garry@ithaca.com (Garry Wiegand)
Date: Fri, 15 Oct 1993 21:47:42 GMT
Raw View
We had an interesting situation come up yesterday. On one of our
machines the compiler apparently feels free to load code/data at the
memory address corresponding to a bit pattern of all zeroes. This
implies of course that the representation for the "null" pointer on
that compiler can't also be a bit pattern of all zeroes. That's
fine; I'm not sure what they actually use, but no problem. Let's say
they use "0xFFFFFFFF".

The problem arises from a local coding style that we have. Rather than
comparing variables to and assigning variables from "0" we habitually
use a "typed null". I.e., rather than:

    void func (char * string) {
     if (string != 0) ...
    }
    void func2 () {
     func (0);
    }

we always make the "0" strongly-typed:

    void func (char * string) {
     if (string != (char *)0) ...
    }
    void func2 () {
     func ((char *)0);
    }


(This "good habit" is probably left over from the days before
function prototypes: "0" and "(char *)0" can behave very differently
when passed explicitly to an unprototyped function. And even
nowadays, in C++, "0" and "(char *)0" can be very different when
passed to an overloaded function.)

In the first case the compiler presumably converts the "== 0" into
"compare memory to (hypothetical) 0xFFFFFFFF".

In the second case is this compiler obliged also to treat "(char *)0"
as the (hypothetical) "0xFFFFFFFF"?

If not, and I can see a strong argument for why not (for example,
I'm on a PDP-11 and I want to twiddle the interrupt vector at actual
address zero), then "(Thing *)0" is a Bad Programming Practice. Because
"string != (char *)0" will be true at different times than "string != 0".

Oh dear.

The only strongly-typed alternative I can see is to have a header file:

    void * const null_void_pointer = 0;
    char * const null_char_pointer = 0;
    Thing * const null_Thing_pointer = 0;
    etc

and then compare/pass/assign using "null_char_pointer" instead of zero
in my example.


(Relevant ARM sections that I can find:

    4.6:
     A constant expression (5.19) that evaluates to zero is converted
     to a pointer, commonly called the null pointer. It is guaranteed
     that this value will produce a pointer distinguishable from a
     pointer to any object or function.

    5.4:
     A value of integral type may be explicitly converted to a pointer.

That's all.)

Enlightenment would be appreciated. Should we change our programming style?

garry

--
Garry Wiegand --- garry@ithaca.com --- Ithaca Software, Alameda, California




Author: karl@dme3.osf.org (Karl Heuer)
Date: 16 Oct 1993 04:31:03 GMT
Raw View
In article <CEyKJJ.4M9@ithaca.com> garry@ithaca.com (Garry Wiegand) writes:
>[On an implementation where the internal representation of a null pointer
>is something other than all-bits-zero, which one is (Thing *)0 ?]

It's a null pointer constant.  The implementation is required to do the
right thing.

>If not, and I can see a strong argument for why not (for example,
>I'm on a PDP-11 and I want to twiddle the interrupt vector at actual
>address zero),

On such an implementation, you would have to use some other method to get
the (non-null) zero address.  Likely candidates include:
 i=0; z=(char *)i;
 union { int asint; char *asptr; } u; u.asint=0; z=u.asptr;
Presumably anybody needing to do something so machine-dependent is
already sufficiently familiar with the local environment to know whether
one of these will work as intended.

>Enlightenment would be appreciated. Should we change our programming style?

No need.




Author: drs@netcom.com (Data Rentals and Sales)
Date: Sat, 16 Oct 1993 02:21:08 GMT
Raw View
In article <CEyKJJ.4M9@ithaca.com> garry@ithaca.com (Garry Wiegand) writes:
>    void func (char * string) {
>     if (string != (char *)0) ...
>    }
>    void func2 () {
>     func ((char *)0);
>    }
>In the second case is this compiler obliged also to treat "(char *)0"
>as the (hypothetical) "0xFFFFFFFF"?

The compiler will always treat any 0 integral or pointer value as the
internal representation of its NULL pointer.  For example, if you have
a char foo which happens to be zero, comparing a pointer to foo will
be true if the pointer is NULL.

Note that the only time this can fail is when the pointer is not
treated as a pointer - using memcmp for example.  At all other times
you are guaranteed that it will be properly handled.


--
#include <standard.disclaimer>
 _
Kevin D Quitt   96.37% of all statistics are made up




Author: jimp@cognos.COM (Jim Patterson)
Date: Sat, 16 Oct 1993 16:15:35 GMT
Raw View
Garry Wiegand (garry@ithaca.com) wrote:
: We had an interesting situation come up yesterday. On one of our
: machines the compiler apparently feels free to load code/data at the
: memory address corresponding to a bit pattern of all zeroes. This
: implies of course that the representation for the "null" pointer on
: that compiler can't also be a bit pattern of all zeroes.

It could imply that. A much simpler solution is to put something else
at address 0, e.g. a string literal "(null)". How to do this depends
of course on the details of the linker and runtime startup code, but
on most systems it should be quite feasible.

As long as the thing at address 0 isn't accessible via any C "object"
(that is, malloc doesn't return it and no declared C variable will
reside there), this should be consistent with the requirements of the
ANSI/ISO standard. It doesn't even prevent you from actually addressing
the thing at address 0 via a pointer with value 0, since dereferencing a
null pointer is "undefined" behaviour as I recall. On some systems,
this may very well cause your program to fall over, but it's not
required that a conforming system behave that way.

--
Jim Patterson                  Cognos Incorporated
Sr Consulting Engineer         P.O. BOX 9707
UUNET:jimp@cognos.COM          3755 Riverside Drive
PHONE:(613)738-1338 x3385      Ottawa, Ont  K1G 3Z4




Author: henry@zoo.toronto.edu (Henry Spencer)
Date: Sun, 17 Oct 1993 01:01:17 GMT
Raw View
In article <drsCEyx78.9Gv@netcom.com> drs@netcom.com (Data Rentals and Sales) writes:
>>In the second case is this compiler obliged also to treat "(char *)0"
>>as the (hypothetical) "0xFFFFFFFF"?
>
>The compiler will always treat any 0 integral or pointer value as the
>internal representation of its NULL pointer.  For example, if you have
>a char foo which happens to be zero, comparing a pointer to foo will
>be true if the pointer is NULL.

Sorry, this is wrong.  The magic transformation of zero to the value
appropriate for the null pointer occurs *only* at compile time.  The
zero has to be a compile-time constant for the transformation to occur.

You can't compare a pointer to a char variable foo; that is not a legal
combination of types for the comparison operators (some old compilers
did an implicit conversion, but ANSI C doesn't).  And when you convert
foo's value to a pointer, or the pointer variable to an integer type,
to do the comparison, you're in the never-never land of implementation-
dependent behavior.  In general, such conversions do *not* do the magic
transformation that makes a null pointer look like an integer zero.
--
One flight test is worth                | Henry Spencer @ U of Toronto Zoology
a thousand simulations.                 |  henry@zoo.toronto.edu  utzoo!henry




Author: mikes@Ingres.COM (Mike Schilling)
Date: 17 Oct 93 02:35:38 GMT
Raw View
The typeless null pointer in C is spelled '0'.  The integer value zero is
also spelled '0', but the two can be distinguished by their type.  They
have nothing else in common, although on many systems they represent the same
bit pattern.  In particular, given:

 void *vp = 0;
 int i = 0;

There is no guarantee that:

 ((int)vp == i || (void *)i == vp)

Much confusion could have been avoided over the years if the null pointer were
spelled 'NULL' (or 'nil').  At any rate, (void *)0 is a perfectly good way to
refer to the null pointer, regardless of its bit pattern.
----------------------------------------------------------------------------
mikes@ingres.com = Mike Schilling, INGRES, An ASK Group Company, Alameda, CA
Just machines that make big decisions,
Programmed by fellows with compassion and vision. -- Donald Fagen, "IGY"




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Sun, 17 Oct 1993 06:54:13 GMT
Raw View
garry@ithaca.com (Garry Wiegand) writes:

>We had an interesting situation come up yesterday. On one of our
>machines the compiler apparently feels free to load code/data at the
>memory address corresponding to a bit pattern of all zeroes. This
>implies of course that the representation for the "null" pointer on
>that compiler can't also be a bit pattern of all zeroes. That's
>fine; I'm not sure what they actually use, but no problem. Let's say
>they use "0xFFFFFFFF".
...
>    void func (char * string) {
>     if (string != 0) ...
>    }
...
>    void func (char * string) {
>     if (string != (char *)0) ...
>    }
...
>In the first case the compiler presumably converts the "== 0" into
>"compare memory to (hypothetical) 0xFFFFFFFF".
>
>In the second case is this compiler obliged also to treat "(char *)0"
>as the (hypothetical) "0xFFFFFFFF"?

Yes.

>If not, and I can see a strong argument for why not (for example,
>I'm on a PDP-11 and I want to twiddle the interrupt vector at actual
>address zero),

If you want a pointer at actual address zero, you need to use something
like the following:
 union {
  int val;
  char *ptr;
 } u;
 u.val = 0;
Now, assuming that `char *' and `int' are the same size, you can use u.ptr
as a pointer to actual address zero.

--
Fergus Henderson                     fjh@munta.cs.mu.OZ.AU




Author: garry@ithaca.com (Garry Wiegand)
Date: Sun, 17 Oct 1993 23:28:36 GMT
Raw View
In a recent article karl@dme3.osf.org (Karl Heuer) wrote:
>In a recent article I wrote:
>>[On an implementation where the internal representation of a null pointer
>>is something other than all-bits-zero, which one is (Thing *)0 ?]
>It's a null pointer constant.  The implementation is required to do the
>right thing.

Thanks, everyone. I really need to get a copy of the ANSI C spec,
rather than trying to figure these things out from the ARM.

garry

--
Garry Wiegand --- garry@ithaca.com --- Ithaca Software, Alameda, California