Re: Null pointers
From: Keith Thompson (kst-u_at_mib.org)
Date: 08/07/04
- Previous message: RCollins: "Re: Null pointers"
- In reply to: Christian Bau: "Re: Null pointers"
- Next in thread: Richard Bos: "Re: Null pointers"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sat, 07 Aug 2004 01:27:38 GMT
Christian Bau <christian.bau@cbau.freeserve.co.uk> writes:
> In article <ln3c30twl2.fsf@nuthaus.mib.org>,
> Keith Thompson <kst-u@mib.org> wrote:
> > Christian Bau <christian.bau@cbau.freeserve.co.uk> writes:
> > [...]
> > > Keep in mind that cast from int to char* is "implementation defined" and
> > > doesn't necessarily give a useful result, so if you have a variable "int
> > > x" then "(char *) x == NULL" could be true for many different values of
> > > x.
> > >
> > > When you use a cast to convert say from int to float, the bits don't
> > > stay unchanged: int i = 1; float f = (float) i; will produce very
> > > different representations in i and f even though both are equal to one.
> > > Same with a cast to convert from int to char*. Now typically compilers
> > > where a null pointer has an all-bits-zero representation will leave the
> > > representation unchanged, but if a null pointer has the same
> > > representation as 0x12345678 then that would be no good.
> >
> > I'm going to use the notation $12345678$ to refer to a pointer whose
> > internal representation is the same as the integer 0x12345678.
> >
> > Even an implementation with the null pointer represented as
> > $12345678$, integer to pointer conversions could still leave the bits
> > unchanged, except in the case of converting a null pointer constant to
> > a pointer type. Remember that a null pointer constant is a source
> > construct, and the conversion of a null pointer constant to a pointer
> > value takes place during compilation.
>
> No, it does not, at least not in C99.
You're right that the language doesn't require the conversion to be
done during compilation; I was sloppy there. (But of course it can
be, and it typically is.)
> For example, in the definition of "equality operator" it says
>
> Constraints: (three other possibilities and)
> One operand is a pointer and the other is a null pointer constant.
>
> Semantics: (two other possibilities and)
> Otherwise, at least one operand is a pointer. If one operand is a
> null pointer constant, it is converted to the type of the other operand.
>
> Conversion is done according to exactly the same rules as all other
> conversions; since a null pointer constant is either of an integral type
> or of type void*, that conversion is either a conversion from an
> integral type to a pointer type or from void* to another pointer type.
> But there is no difference between this conversion and any other
> conversion. It is conceptually not a compile time construct. (Of course
> an optimiser will determine the result of the conversion at compile
> time, just as 2+3 will be determined at compile time by practically
> every compiler).
Conversion of a null pointer constant to a pointer type is explicitly
a special case, at least in C99. (I think that was the intent in C90
as well, but C99 expresses it better; this is speculation on my part.)
> > There's no requirement to
> > duplicate that conversion at run time.
> >
> > So we could have:
> >
> > (char*)0 --> $12345678$ (null pointer)
> > int zero = 0;
> > (char*)zero --> $00000000$ (non-null pointer)
> > (char*)0x12345678 --> $12345678$ (happens to be a null pointer)
>
> Quite possible in C90, but most definitely not in C99. In C90, the
> wording was such that in an assignment, or within an equality operator,
> and probably some cases that I forgot, a null pointer constant was
> replaced with a null pointer. (char*)0 was _not_ one if these cases and
> in C90 not guaranteed to be a null pointer; in C99 they added that
> _every_ conversion of a null pointer constant to a pointer produces a
> null pointer.
Here's the C90 wording, with underscores denoting italics:
An integral constant expression with the value 0, or such an
expression cast to type void *, is called a _null pointer
constant_. If a null pointer constant is assigned to or compared
for equality to a pointer, the constant is converted to a pointer
of that type. Such a pointer, called a _null pointer_, is
guaranteed to compare unequal to a pointer to any object or
function.
Two null pointers. converted through possibly different sequences
of casts to pointer types, shall compare equal.
C99 says:
An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant.
If a null pointer constant is converted to a pointer type, the
resulting pointer, called a null pointer, is guaranteed to compare
unequal to a pointer to any object or function.
Conversion of a null pointer to another pointer type yields a null
pointer of that type. Any two null pointers shall compare equal.
In my opinion, C99's statement that a null pointer constant yields a
null pointer when converted to a pointer type does not imply that all
expressions of type int with value 0 yield a null pointer when
converted to a pointer type.
In my example above, I presume the part you disagree with is my
assertion that, given "int zero = 0;", the expression "(char*)zero"
needn't yield a null pointer. Since "zero" is not a null pointer
constant
Your assumption, I think, is that conversion of a given value from a
given type to another given type (in this case, respectively, the
value 0, the type int, and the type char*) must always yield the same
result. That's not a reasonable expectation, but I think it's
overridden by the fact that conversion of a "null pointer constant" is
explicitly a special case.
More concretely:
#include <stdio.h>
#include <string.h>
static int equal(char *x, char *y)
{
return memcmp(&x, &y, sizeof(char*)) == 0;
}
int main(void)
{
char *null_pointer = 0;
char *all_bits_zero_pointer;
int zero = 0;
char *maybe_null_pointer = (char*)zero;
memset(&all_bits_zero_pointer, 0, sizeof all_bits_zero_pointer);
if (equal(null_pointer, all_bits_zero_pointer)) {
printf("A null pointer is all-bits-zero\n");
}
else {
printf("A null pointer is not all-bits-zero\n");
}
if (equal(null_pointer, maybe_null_pointer)) {
printf("Conversion of int zero yields a null pointer\n");
}
else {
printf("Conversion of int zero does not yield a null pointer\n");
}
return 0;
}
I'm using memcmp rather than direct pointer comparison to avoid any
possibility of undefined behavior on attempts to access the value of
an invalid pointer.
I think we all agree that the first line of output from this program
may be either
A null pointer is all-bits-zero
or
A null pointer is not all-bits-zero
I assert that, regardless of the first line of output, the second line
can be either
Conversion of int zero yields a null pointer
or
Conversion of int zero does not yield a null pointer
under a conforming implementation -- though an implementation would
have to be particularly perverse to produce
A null pointer is not all-bits-zero
Conversion of int zero does not yield a null pointer
(The output happens to be
A null pointer is all-bits-zero
Conversion of int zero yields a null pointer
on all systems I'm familiar with; that's not very illuminating.)
To put it another way, I believe that choosing a value other than
all-bits-zero for the null pointer does not imply that
integer-to-pointer conversion has to do anything other than a bitwise
copy *except* in the case of a null pointer constant.
-- Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst> San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst> We must do something. This is something. Therefore, we must do this.
- Previous message: RCollins: "Re: Null pointers"
- In reply to: Christian Bau: "Re: Null pointers"
- Next in thread: Richard Bos: "Re: Null pointers"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|