NULL with representation other then all bits 0

Y

yossi.kreinin

Hi!

There is a system where 0x0 is a valid address, but 0xffffffff isn't.
How can null pointers be treated by a compiler (besides the typical
"solution" of still using 0x0 for "null")?

- AFAIK C allows "null pointers" to be represented differently then
"all bits 0". Is this correct?
- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?
- AFAIK I can identify contexts where `0' is used as a pointer and use
the numeric value 0xffffffff rather then 0x0. Is this correct? In
particular, should `void* p;' initialize p to "null pointer" rather
then "zero" (so it has to be placed in ".data" rather then ".bss" in
terms of typical implementations if "null pointer" is not represented
as all bits 0)? Worse, should `memset(&p, 0, sizeof(void*))' set p to
the "null pointer" rather then "zero"? Should casts from int to void*
convert (int)0 (bits: 0x0) to (void*)0 (bits: 0xffffffff)?

I know that this topic has been discussed a lot. That's even one of the
reasons I'm not sure what the real answers are - I remember too many of
them and can't tell the right ones from the wrong ones...

Thanks in advance!
-- Yossi
 
V

Vladimir S. Oka

Hi!

There is a system where 0x0 is a valid address, but 0xffffffff isn't.
How can null pointers be treated by a compiler (besides the typical
"solution" of still using 0x0 for "null")?

Standard does not prescribe actual bit representation of a NULL pointer,
that is implementation dependent. However, the Standard does prescribe
that 0 used in a pointer context is a valid representation of a NULL
pointer (even if it's not all-bits-zero).
- AFAIK C allows "null pointers" to be represented differently then
"all bits 0". Is this correct?

Yes (see above).
- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?

Yes and no. Yes, if you're compiler writer, and on your implementation
NULL pointer representation is 0x10000, so you defined it thus in the
standard header files. No, if you're trying to re-define NULL in your
own C program. The part of your sentence after `since` is irrelevant in
this context (but true).
- AFAIK I can identify contexts where `0' is used as a pointer and use
the numeric value 0xffffffff rather then 0x0. Is this correct? In

Yes, but you'd have to cast it an appropriate pointer type. However,
this will make your program utterly non-portable, as the next
implementation you compile it on may have defined NULL as something
entirely different (bit-wise).
particular, should `void* p;' initialize p to "null pointer" rather
then "zero" (so it has to be placed in ".data" rather then ".bss" in
terms of typical implementations if "null pointer" is not represented
as all bits 0)? Worse, should `memset(&p, 0, sizeof(void*))' set p to
the "null pointer" rather then "zero"? Should casts from int to void*
convert (int)0 (bits: 0x0) to (void*)0 (bits: 0xffffffff)?

I know that this topic has been discussed a lot. That's even one of
the reasons I'm not sure what the real answers are - I remember too
many of them and can't tell the right ones from the wrong ones...

I suggest you carefully read the C FAQ at http://www.c-faq.com/,
especially Section 5 which is in its entirety devoted to NULL pointers,
and I believe answers all your questions (direct link is:
http://www.c-faq.com/null/index.html)

Cheers

Vladimir
 
F

Flash Gordon

Vladimir said:
(e-mail address removed) wrote:


Yes, but you'd have to cast it an appropriate pointer type. However,
this will make your program utterly non-portable, as the next
implementation you compile it on may have defined NULL as something
entirely different (bit-wise).

<snip>

Actually, even if that is the correct representation of a null pointer
the conversion might defined so that converting 0xffffffff actually
converts to address 0 since otherwise you would not be able to produce
an address 0.
 
G

Gordon Burditt

There is a system where 0x0 is a valid address, but 0xffffffff isn't.
How can null pointers be treated by a compiler (besides the typical
"solution" of still using 0x0 for "null")?

- AFAIK C allows "null pointers" to be represented differently then
"all bits 0". Is this correct?
Yes.

- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?

You, as programmer, are not allowed to do this.
You, as compiler implementor, are allowed to do this.

There is no prohibition of an implementation where "all pointers with
a prime-valued bit pattern are considered null pointers. This might
make the code generated for pointer comparison messy, since two null
pointers should compare equal to each other even if they don't have
the same bit representation.
- AFAIK I can identify contexts where `0' is used as a pointer and use
the numeric value 0xffffffff rather then 0x0. Is this correct? In

You can identify null pointer constants and use the correct bit
pattern (which is 0xdeadbeef, not 0xffffffff on 32-bit machines)
at compile time.
particular, should `void* p;' initialize p to "null pointer" rather
then "zero" (so it has to be placed in ".data" rather then ".bss" in
terms of typical implementations if "null pointer" is not represented
as all bits 0)?

You could place it in a section which is initialized to null pointers,
not all-bits-zero.
Worse, should `memset(&p, 0, sizeof(void*))' set p to
the "null pointer" rather then "zero"?

No.
memset(&p, 0, sizeof(void*));
p; /* smegmentation fault allowed here */


Should casts from int to void*
convert (int)0 (bits: 0x0) to (void*)0 (bits: 0xffffffff)?

Casts from non-constant ints need not do such a conversion.
I know that this topic has been discussed a lot. That's even one of the
reasons I'm not sure what the real answers are - I remember too many of
them and can't tell the right ones from the wrong ones...

Gordon L. Burditt
 
K

Keith Thompson

(e-mail address removed) writes: [...]
- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?

You, as programmer, are not allowed to do this.
You, as compiler implementor, are allowed to do this.

The NULL macro must expand to a null pointer constant. 0x10000 is not
a null pointer constant as the term is defined by the standard, so
it's not immediately obvious that

#define NULL 0x10000

is legal for an implementation, even if converting that value to a
pointer always yields a null pointer value.

On the other hand, C99 6.6p10 says:

An implementation may accept other forms of constant expressions.

It's not clear whether that means an implementation may accept other
forms of null pointer constant (0x10000 is already a constant
expression). And C99 4p6 says:

A conforming implementation may have extensions (including
additional library functions), provided they do not alter the
behavior of any strictly conforming program.

so treating 0x10000 as a null pointer constant is (I think) allowed on
that basis, as long as the implementation documents it.

On the other other hand, since 0 *must* be a null pointer constant,
there's little point in defining NULL as anything other than 0 or
((void*)0). Giving null pointers a representation other than
all-bits-zero doesn't require any extensions to make everything work
properly. And accepting 0x10000 as a null pointer constant would
encourage programmers to write non-portable code that depends on this
assumption.

Even if all-bits-zero is a valid address, you might still consider
representing null pointers as all-bits-zero. Whatever is at that
location, no portable program can access it anyway. If it's something
important, there's some risk of buggy programs clobbering it by
writing through a null pointer -- but then there's going to be some
risk of buggy programs clobbering it by writing through address zero.

BTW, Gordon, please don't snip attribution lines.
 
J

Jack Klein

Yes and no. Yes, if you're compiler writer, and on your implementation
NULL pointer representation is 0x10000, so you defined it thus in the
standard header files. No, if you're trying to re-define NULL in your
own C program. The part of your sentence after `since` is irrelevant in
this context (but true).

No, even if you are a compiler writer, you cannot define the macro
NULL as something like (void *)0x10000. Not if you want your compiler
to conform to the C language standard.

The macro NULL is required by the standard to be an
implementation-defined null pointer constant, and the meaning of "null
pointer constant" is precisely defined by the standard:

"An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant."

The expression (void *)0x10000 is neither of the two allowed forms.
 
J

Jordan Abel

No, even if you are a compiler writer, you cannot define the macro
NULL as something like (void *)0x10000. Not if you want your compiler
to conform to the C language standard.

The macro NULL is required by the standard to be an
implementation-defined null pointer constant, and the meaning of "null
pointer constant" is precisely defined by the standard:

"An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant."

The expression (void *)0x10000 is neither of the two allowed forms.

I think you have a funny definition of "implementation-defined".
 
J

Joe Wright

Keith said:
(e-mail address removed) writes:
[...]
- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?

You, as programmer, are not allowed to do this.
You, as compiler implementor, are allowed to do this.


The NULL macro must expand to a null pointer constant. 0x10000 is not
a null pointer constant as the term is defined by the standard, so
it's not immediately obvious that

#define NULL 0x10000

is legal for an implementation, even if converting that value to a
pointer always yields a null pointer value.

On the other hand, C99 6.6p10 says:

An implementation may accept other forms of constant expressions.

It's not clear whether that means an implementation may accept other
forms of null pointer constant (0x10000 is already a constant
expression). And C99 4p6 says:

A conforming implementation may have extensions (including
additional library functions), provided they do not alter the
behavior of any strictly conforming program.

so treating 0x10000 as a null pointer constant is (I think) allowed on
that basis, as long as the implementation documents it.

On the other other hand, since 0 *must* be a null pointer constant,
there's little point in defining NULL as anything other than 0 or
((void*)0). Giving null pointers a representation other than
all-bits-zero doesn't require any extensions to make everything work
properly. And accepting 0x10000 as a null pointer constant would
encourage programmers to write non-portable code that depends on this
assumption.

Even if all-bits-zero is a valid address, you might still consider
representing null pointers as all-bits-zero. Whatever is at that
location, no portable program can access it anyway. If it's something
important, there's some risk of buggy programs clobbering it by
writing through a null pointer -- but then there's going to be some
risk of buggy programs clobbering it by writing through address zero.

BTW, Gordon, please don't snip attribution lines.

I am not a guru. The only pointer value defined by the C standard is
NULL. It is defined variously as 0 or (void*)0. The zero value is chosen
specifically because it is within the range of all possible pointer
values. No pointer value other than NULL can be tested for validity.

A C program can safely assume NULL as zero. If it is really not it is
the implementation's job to take care of it and lie to us.

The conditional (NULL == 0) will yield 1 everywhere. Or not?
 
P

Peter Nilsson

Jordan said:
I think you have a funny definition of "implementation-defined".

You mean the funny one in the standard...

implementation-defined behavior
unspecified behavior where each implementation documents how the
choice is made

unspecified behavior
behavior where this International Standard provides two or more
possibilities and imposes no further requirements on which is
chosen in any instance

This allows an implementation to do...

#define NULL __null

....where __null is an integer constant expression with the value 0, but
it doesn't allow...

#define NULL ((void *)0x10000)

....since 0x10000 is clearly not an integer constant expression with the
value 0.

Of course, such an implementation could carefully check for stringising
of the NULL macro, under the 'as if' rule, thereby preventing a
strictly conforming program from confirming that it is not an
ordinary null pointer constant, but why would an implementaion
bother?
 
K

Keith Thompson

Jordan Abel said:
I think you have a funny definition of "implementation-defined".

There was a lengthy discussion of this not too long ago; I think it
was in comp.std.c.

In my opinion, the phrase "implementation-defined null pointer
constant" in C99 7.17p3 (which defines the NULL macro) does *not* give
permission to extend the standard's definition of "null pointer
constant" in 6.3.2.3p3; it merely allows an implementation to chose
something that's a null pointer constant and requires it to document
its choice.

On the other hand, 4p6 says:

A conforming implementation may have extensions (including
additional library functions), provided they do not alter the
behavior of any strictly conforming program.

which does mean that an implementation may define additional null
pointer constants (such as (void*)0x10000), as long as it documents
them and as long as no strictly conforming programs are affected.

But since 0 is already a perfectly portable null pointer constant,
there's not much point in doing so. (It might be useful to define a
special form of null pointer constant for error checking, so a
diagnostic is produced when NULL is used in a non-pointer context, but
it's possible to do that anyway.)
 
K

Keith Thompson

Joe Wright said:
I am not a guru. The only pointer value defined by the C standard is
NULL.

I'm not sure what you mean by that. NULL is a macro that expands to a
null pointer constant; a null pointer constant yields a null pointer
value when converted to a pointer type. So NULL is two steps removed
from an actual pointer value. (A null pointer constant is a syntactic
entity that occurs only in source code; a null pointer value occurs
only during the execution of a program.)

Yes, I'm being picky; it's not entirely unreasonable to use NULL as a
shorthand for a null pointer value. But the address of any object or
function is a pointer value.
It is defined variously as 0 or (void*)0.

Basically yes. I'm going to go into pedantic mode; feel free to
ignore the next few paragraphs.

(void*)0 is not a valid definition for NULL because of C99 7.1.2p5:

Any definition of an object-like macro described in this clause
shall expand to code that is fully protected by parentheses where
necessary, so that it groups in an arbitrary expression as if it
were a single identifier.

If you have
#define NULL (void*)0
then the expression
sizeof NULL
becomes a syntax error.

On the other hand, it's not clear that ((void*)0) is a valid
definition for NULL either. NULL is required to be a null pointer
constant. The standard's definition of a null pointer constant is:

An integer constant expression with the value 0, or such an
expression cast to type void *

6.5.1p5 says that:

A parenthesized expression is a primary expression. Its type and
value are identical to those of the unparenthesized expression. It
is an lvalue, a function designator, or a void nexpression if the
unparenthesized expression is, respectively, an lvalue, a function
designator, or a void expression.

We cannot directly conclude from this that a parenthesized null
pointer constant is a null pointer constant.

However, just as a matter of common sense, it seems obvious that
((void*)0) *should* be a null pointer constant, and therefore a valid
definition of NULL. Some implementations do define NULL this way.
The wording of the standard should be corrected.

End of pedantry (for now).
The zero value is
chosen specifically because it is within the range of all possible
pointer values.

I'm not sure what this means. Pointers are not numbers; they don't
have ranges.
No pointer value other than NULL can be tested for
validity.

Again, the address of any object or function is a pointer value. What
do you mean by "can be tested for validity"?
A C program can safely assume NULL as zero. If it is really not it is
the implementation's job to take care of it and lie to us.

A null pointer value is a particular value of a pointer type, just as
0 is a particular value of an integer type and 0.0 is a particular
value of a floating-point type. It just happens that the language
uses a very strange way to represent a null pointer literal.

It's best to think of a null pointer value as a null pointer value,
not as "zero". The fact that 0 can be used *in source* to represent a
run-time null pointer value is just an oddity that's hidden behind the
NULL macro.
The conditional (NULL == 0) will yield 1 everywhere. Or not?

Yes, because both will be converted to a common type. If NULL is 0,
it's just (0 == 0), which is an integer comparison. If NULL is
((void*)0), it's a pointer comparison.
 
J

Jordan Abel

Keith said:
(e-mail address removed) writes:
[...]

- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?

You, as programmer, are not allowed to do this.
You, as compiler implementor, are allowed to do this.


The NULL macro must expand to a null pointer constant. 0x10000 is not
a null pointer constant as the term is defined by the standard, so
it's not immediately obvious that

#define NULL 0x10000

is legal for an implementation, even if converting that value to a
pointer always yields a null pointer value.

On the other hand, C99 6.6p10 says:

An implementation may accept other forms of constant expressions.

It's not clear whether that means an implementation may accept other
forms of null pointer constant (0x10000 is already a constant
expression). And C99 4p6 says:

A conforming implementation may have extensions (including
additional library functions), provided they do not alter the
behavior of any strictly conforming program.

so treating 0x10000 as a null pointer constant is (I think) allowed on
that basis, as long as the implementation documents it.

On the other other hand, since 0 *must* be a null pointer constant,
there's little point in defining NULL as anything other than 0 or
((void*)0). Giving null pointers a representation other than
all-bits-zero doesn't require any extensions to make everything work
properly. And accepting 0x10000 as a null pointer constant would
encourage programmers to write non-portable code that depends on this
assumption.

Even if all-bits-zero is a valid address, you might still consider
representing null pointers as all-bits-zero. Whatever is at that
location, no portable program can access it anyway. If it's something
important, there's some risk of buggy programs clobbering it by
writing through a null pointer -- but then there's going to be some
risk of buggy programs clobbering it by writing through address zero.

BTW, Gordon, please don't snip attribution lines.

I am not a guru. The only pointer value defined by the C standard is
NULL. It is defined variously as 0 or (void*)0. The zero value is chosen
specifically because it is within the range of all possible pointer
values. No pointer value other than NULL can be tested for validity.

A C program can safely assume NULL as zero. If it is really not it is
the implementation's job to take care of it and lie to us.

The conditional (NULL == 0) will yield 1 everywhere. Or not?

#define NULL ((void *)0xFFFFFFFF), assuming that that is in fact a null
pointer, will guarantee that.
 
C

Chris Torek

Hi!

There is a system where 0x0 is a valid address, but 0xffffffff isn't.
How can null pointers be treated by a compiler (besides the typical
"solution" of still using 0x0 for "null")?

Ignoring all the debate that has been triggered by your list
(which I have snipped), here is the answer I think you may be
looking for.

Suppose you are the compiler-writer for this machine. Suppose
further that you have decide to use 0xffffffff (all-one-bits)
as your internal representation for the null pointer, so that:

char *p = 0;
use(*p);

will trap at runtime, even though 0 is a valid address. How will
you, as the compiler-writer, achieve this?

The answer lies in your code generator. At any point in dealing
with the conversion of C source code to machine-level instructions,
you *always* know the type(s) of all the operand(s) of every
operator. This is of course absolutely necessary on most machines.
Consider, for instance, something like:

sum = a + b;

If a and b are ordinary "int"s, you probably need to generate an
integer-add instruction with integer operands:

ld r1, ... # load variable "a" into integer register
ld r2, ... # load variable "b" into integer register
add r0,r1,r2 # compute integer sum, reg+reg -> reg
st r0, ... # store sum back to memory

while if "a" and "b" are ordinary "double"s, you probably need to
generate a double-add instruction with double operands:

ldd f2, ... # load double "a" into f2/f3 register pair
ldd f4, ... # load double "b" into f4/f5 register pair
addd f0,f2,f4 # compute double-precision sum
std f0, ... # store sum back into memory

If one operand is an "int" and one is a "double", you have to
convert the int to a double and do the addition as two doubles,
and so on. The only way to know which instructions to generate is
to keep track of the types of all the operands.

So, now you have a chunk of C source level code that includes
the line:

p = 0;

where "p" has type "char *", i.e., a pointer type. The operand on
the right-hand-side of the assignment is an integer *and* is a
constant (you must keep track of this, too, but of course you will,
in order to do constant-folding). So you have an assignment that
has "integer constant zero" as the value to be assigned. Inside
the compiler, you check, and you GENERATE DIFFERENT CODE!

if (is_integer_constant(rhs) && value(rhs) == 0)
generate_store(lhs, 0xffffffff);
else
...

and thus, what comes out in the machine code is:

mov r0, -1 # set r0 to 0xffffffff
st r0, ... # store to pointer "p"

Likewise, in places where you have a comparision or test that
might be comparing a pointer to integer-constant-zero, you check
for this in the compiler, and generate the appropriate code:

is_null = false;
if (is_pointer(lhs) && is_integer_constant(rhs) && value(rhs) == 0) {
is_null = true;
ptr_operand = lhs;
} else if (is_pointer(rhs) &&
is_integer_constant(lhs) && value(lhs) == 0) {
is_null = true;
ptr_operand = rhs;
}
if (is_null)
generate_compare(ptr_operand, 0xffffffff);
else
...

There is only one place this goes wrong, and that is:

extern void somefunc(int firstparam, ...);
...
somefunc(3, ptr1, 0, ptr3); /* where "0" is meant to be a null pointer */

Here, inside your compiler, you see that the second parameter is
an integer constant zero, so you check the function prototype to
see if you need a pointer in this position. All you have is the
literal "..." part, so you must assume that this is really an
integer here, not a pointer. You pass zero (0x00000000) instead
of 0xffffffff. But this source code call is wrong! The programmer
*should* have used a cast:

somefunc(3, ptr1, (char *)0, ptr3);

In this version, you have a cast of an integer constant zero to a
pointer type, which produces 0xffffffff as appropriate, and only
then looks at the prototype. As before, there is no extra information
given by the prototype, but now you pass 0xffffffff as desired.

Now, given that I believe you have indeed correctly identified how
to do this inside the compiler:
- AFAIK I can identify contexts where `0' is used as a pointer and use
the numeric value 0xffffffff rather then 0x0. Is this correct?
Yes.

In particular, should `void* p;' initialize p to "null pointer" rather
then "zero" (so it has to be placed in ".data" rather then ".bss" in
terms of typical implementations if "null pointer" is not represented
as all bits 0)?

If "p" has static duration, yes. If "p" has automatic duration it
does not have to have any useful value upon creation. Note that
this also applies to structures containing pointers:

struct S { int a; long *b; double c; void *d; };
static struct S x;

will have to put x in a data segment in order to set x.b and x.d
to 0xffffffff internally. Unions may also contain pointers, but
in C89 only the first element is initialized, so only if the first
element is a pointer will you have to do this. (C99 offers designated
initializers, but those just fall out naturally.)

(You may, depending on implementation, want to have an "all one
bits" segment that you place either before or after your "bss"
segment. This will handle pointers that are not members of
structures.)
Worse, should `memset(&p, 0, sizeof(void*))' set p to the
"null pointer" rather then "zero"?
No.

Should casts from int to void* convert (int)0 (bits: 0x0) to (void*)0
(bits: 0xffffffff)?

If the (int)0 is an integer *constant*, yes (because semantically,
a cast is just an assignment to an unnamed temporary, except that
an actual temporary would be an lvalue and a cast produces an rvalue).

If the int that happens to contain zero is *not* a constant, this
is up to you -- but I would not. This allows programmers to write:

int zero = 0;
char *p = (char *)zero;
... now use *p to access hardware location 0 ...
I know that this topic has been discussed a lot. That's even one of the
reasons I'm not sure what the real answers are - I remember too many of
them and can't tell the right ones from the wrong ones...

The usual answer is to skip all of this and simply make sure that
hardware-location-zero is occupied, so that no *C* object or function
actually has address zero. Of course, this does not trap erroneous
null-pointer dereferences, but C implementations are rarely kind
to programmers that way. We seem to prefer to drive our race cars
without seatbelts. :)
 
A

Alex Fraser

Keith Thompson said:
On the other hand, it's not clear that ((void*)0) is a valid
definition for NULL either. NULL is required to be a null pointer
constant. The standard's definition of a null pointer constant is:

An integer constant expression with the value 0, or such an
expression cast to type void *

6.5.1p5 says that:

A parenthesized expression is a primary expression. Its type and
value are identical to those of the unparenthesized expression. It
is an lvalue, a function designator, or a void nexpression if the
unparenthesized expression is, respectively, an lvalue, a function
designator, or a void expression.

We cannot directly conclude from this that a parenthesized null
pointer constant is a null pointer constant.

(In N869,) 6.6 says that a constant expression is (grammatically) a
conditional expression - with some constraints, of course.

Grammatically, a primary expression is a conditional expression.

Alex
 
C

Christian Bau

Hi!

There is a system where 0x0 is a valid address, but 0xffffffff isn't.
How can null pointers be treated by a compiler (besides the typical
"solution" of still using 0x0 for "null")?

- AFAIK C allows "null pointers" to be represented differently then
"all bits 0". Is this correct?
- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?
- AFAIK I can identify contexts where `0' is used as a pointer and use
the numeric value 0xffffffff rather then 0x0. Is this correct? In
particular, should `void* p;' initialize p to "null pointer" rather
then "zero" (so it has to be placed in ".data" rather then ".bss" in
terms of typical implementations if "null pointer" is not represented
as all bits 0)? Worse, should `memset(&p, 0, sizeof(void*))' set p to
the "null pointer" rather then "zero"? Should casts from int to void*
convert (int)0 (bits: 0x0) to (void*)0 (bits: 0xffffffff)?

I know that this topic has been discussed a lot. That's even one of the
reasons I'm not sure what the real answers are - I remember too many of
them and can't tell the right ones from the wrong ones...

All your compiler has to do is to make sure that a cast from an integer
zero to a pointer type produces a null pointer, and a cast from a null
pointer to an integer type produces an integer zero.

If for example sizeof (int) == sizeof (void *), and a null pointer of
type (void *) has exactly the same representation as an int with a value
of 0x10000, then the cast from int to void* might translate to an "add"
instruction which adds 0x10000, and a cast from void* to int might
translate to a "subtract" instruction which subtracts 0x10000, or both
might translate to an "exclusive or" instruction which does an
exclusive-or with a value of 0x10000.

One other bit where the compiler must be careful: All static and extern
pointer variables without an explicit initialisation must be initialised
to a null pointer. Some compilers just produce code that sets everything
to zeroes and then fills in bits that are explicitely initialised; that
will not be enough if null pointers or floating point zeroes are not all
bits zeroes.
 
J

Joe Wright

Keith said:
I'm not sure what you mean by that. NULL is a macro that expands to a
null pointer constant; a null pointer constant yields a null pointer
value when converted to a pointer type. So NULL is two steps removed
from an actual pointer value. (A null pointer constant is a syntactic
entity that occurs only in source code; a null pointer value occurs
only during the execution of a program.)

Yes, I'm being picky; it's not entirely unreasonable to use NULL as a
shorthand for a null pointer value. But the address of any object or
function is a pointer value.




Basically yes. I'm going to go into pedantic mode; feel free to
ignore the next few paragraphs.

(void*)0 is not a valid definition for NULL because of C99 7.1.2p5:

Any definition of an object-like macro described in this clause
shall expand to code that is fully protected by parentheses where
necessary, so that it groups in an arbitrary expression as if it
were a single identifier.

If you have
#define NULL (void*)0
then the expression
sizeof NULL
becomes a syntax error.

On the other hand, it's not clear that ((void*)0) is a valid
definition for NULL either. NULL is required to be a null pointer
constant. The standard's definition of a null pointer constant is:

An integer constant expression with the value 0, or such an
expression cast to type void *

6.5.1p5 says that:

A parenthesized expression is a primary expression. Its type and
value are identical to those of the unparenthesized expression. It
is an lvalue, a function designator, or a void nexpression if the
unparenthesized expression is, respectively, an lvalue, a function
designator, or a void expression.

We cannot directly conclude from this that a parenthesized null
pointer constant is a null pointer constant.

However, just as a matter of common sense, it seems obvious that
((void*)0) *should* be a null pointer constant, and therefore a valid
definition of NULL. Some implementations do define NULL this way.
The wording of the standard should be corrected.

End of pedantry (for now).




I'm not sure what this means. Pointers are not numbers; they don't
have ranges.
Pointer values share some characteristics of numbers. You can add to
them, subtract from them and subtract one from another. Pointers have a
range from 0 to the maximum allowed memory address.

As the C programmer doesn't know the memory model of the target, the
natural choice for a 'pointer to nothing' would be 0 or (void*)0.
Again, the address of any object or function is a pointer value. What
do you mean by "can be tested for validity"?
Consider..
int *ptr;
ptr = malloc(100 * sizeof *ptr);
if (ptr == NULL) {/* do something about the failure */}
.. use ptr with careless abandon ..
free(ptr);
.. use ptr at your peril ..
The value of ptr probably hasn't changed but the call to free(ptr) has
made it indeterminate. You can't examine ptr to determine its validity.
 
C

Christian Bau

Keith Thompson said:
(e-mail address removed) writes: [...]
- AFAIK I can't `#define NULL 0x10000' since `void* p=0;' should work
just like `void* p=NULL'. Is this correct?

You, as programmer, are not allowed to do this.
You, as compiler implementor, are allowed to do this.

The NULL macro must expand to a null pointer constant. 0x10000 is not
a null pointer constant as the term is defined by the standard, so
it's not immediately obvious that

#define NULL 0x10000

is legal for an implementation, even if converting that value to a
pointer always yields a null pointer value.

On the other hand, C99 6.6p10 says:

An implementation may accept other forms of constant expressions.

I think the C Standard defines "constant expressions" a bit before null
pointer constants. A null pointer constant is then defined as a
"constant expression" which has some additional properties, for example
either being an integer expression of value 0, or such an expression
cast to void*. 0x10000 cannot be a null pointer constant, because it
doesn't have a value of zero.

I think an implementation might for example define strlen ("") as a
constant which would have a value of zero and might therefore become a
null pointer constant (but I think there will other restrictions in the
definition of "integer constant expression" and "null pointer constant"
that prevent it from being a null pointer constant).
 
C

Christian Bau

A C program can safely assume NULL as zero. If it is really not it is
the implementation's job to take care of it and lie to us.

No. Saying "NULL is zero" is nonsense. NULL can either be an integer
constant with a value of 0, or it is such a constant cast to void*. In
that case is a pointer. Saying that a pointer is zero is pure nonsense.
A pointer can point to an object, or it can point past the last byte of
an object, or it can be a null pointer which points to no object at all,
or it can be some indeterminate value, but it cannot be zero. It cannot
be pi, or e, or sqrt (2), or one, or zero, or any other number. It
cannot be green, yellow, red or blue either. These are all things that
don't make any sense for pointers.

In a comparison (p == 0), where p is a pointer, the integer constant 0
is converted to a null pointer because there is a special rule in the C
language that in this kind of situation, integer constants of value 0
are automatically converted to pointers, while any other integer
constants, for example those with a value of 1, are not converted. The
pointer p is _never_ compared with a zero. It is always compared with
another pointer value.
 
C

Christian Bau

Jordan Abel said:
#define NULL ((void *)0xFFFFFFFF), assuming that that is in fact a null
pointer, will guarantee that.

But it is not a null pointer constant, because 0xFFFFFFFF doesn't have a
value of zero.
 
V

Vladimir S. Oka

Joe said:
Pointer values share some characteristics of numbers. You can add to
them, subtract from them and subtract one from another. Pointers have
a range from 0 to the maximum allowed memory address.

I think associating pointers with numbers, despite the `similarities`
quoted above is not a good idea. The `addition` and `subtraction` work
in (not so) subtly different ways than expected of `ordinary` numbers
(think pointers to a structure with size of 17 bytes). Also, ranges are
not necessarily contiguous in the sense the ranges of real world
numbers are (an architecture may have no memory mapped in the byte
address range 0x1000 to 0x2000, as it's reserved for memory-mapped
I/O).
As the C programmer doesn't know the memory model of the target, the
natural choice for a 'pointer to nothing' would be 0 or (void*)0.

This may be the `natural` assumption, but it suffers the same problems
as outlined above.

IMHO, It might have been better if C went the Pascal way and had just
NULL, and didn't allow numbers to be mixed with pointers, unless as a
non-standard extension.

My tuppence, anyway...

Cheers

Vladimir
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
DewittMill
Top