Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What is a null pointer constant?

44 views
Skip to first unread message

Harald van Dijk

unread,
Nov 16, 2005, 2:20:09 AM11/16/05
to
Hi,

What is a null pointer constant? I'm aware of the definition, but I'm
not sure if this allows indirect conversions to (void *) as well.

Specifically, which lines of this simple program, if any, are invalid?

int main() {
int (*fp[])() = {
0,
(void *) 0,
(void *) (void *) 0,
(void *) (char *) 0,
(void *) (int) (void *) 0,
(int) (void *) 0
};
}

I'm pretty sure 0 and (void *) 0 should be fine, and for the last I
don't see at all how it might be valid, but for the other three I'm not
sure. gcc (4.0.2) and icc (9.0) accept all, but tendra (20051114) and
comeau (4.3.3 beta, online) accept only the first two.

Additionally, does any of this change if fp is declared static? I don't
see why, and with gcc, tendra and comeau nothing changes, but icc now
gives warnings ("warning #32: expression must have arithmetic type")
for the last two.

(I made sure to use the compiler options -ansi -pedantic with gcc,
-strict-ansi with icc, and nothing with tendra, and to select strict
C89/C90 mode for comeau. Also, in all of their C99 modes, nothing
changed.)

Message has been deleted

Jordan Abel

unread,
Nov 16, 2005, 2:45:53 AM11/16/05
to
On 2005-11-16, Harald van D?k <tru...@gmail.com> wrote:
> Hi,
>
> What is a null pointer constant? I'm aware of the definition, but I'm
> not sure if this allows indirect conversions to (void *) as well.
>
> Specifically, which lines of this simple program, if any, are invalid?
>
> int main() {
> int (*fp[])() = {
> 0,
> (void *) 0,
> (void *) (void *) 0,
> (void *) (char *) 0,
> (void *) (int) (void *) 0,
> (int) (void *) 0
> };
>}
>
> I'm pretty sure 0 and (void *) 0 should be fine, and for the last I
> don't see at all how it might be valid, but for the other three I'm not
> sure. gcc (4.0.2) and icc (9.0) accept all, but tendra (20051114) and
> comeau (4.3.3 beta, online) accept only the first two.

Everything but the two involving "int" are valid as far as I know. Even
gcc should give a warning for the last one - our good friend "Assignment
makes pointer from integer without cast".

Harald van Dijk

unread,
Nov 16, 2005, 2:57:23 AM11/16/05
to
Jordan Abel wrote:
> Everything but the two involving "int" are valid as far as I know. Even
> gcc should give a warning for the last one - our good friend "Assignment
> makes pointer from integer without cast".

It doesn't generate that warning, though; it seems to treat (int) (void
*) 0 as simply (int) 0, and because of that as initialising a pointer
with a constant 0, which would be okay.

Jordan Abel

unread,
Nov 16, 2005, 5:34:34 AM11/16/05
to
On 2005-11-16, Harald van D?k <tru...@gmail.com> wrote:

But there's no guarantee in the standard that (void *)0 becomes 0 when
converted back to an int, therefore the compiler must print a
diagnostic, right?

Skarmander

unread,
Nov 16, 2005, 5:38:38 AM11/16/05
to
Harald van Dijk wrote:
> What is a null pointer constant? I'm aware of the definition, but I'm
> not sure if this allows indirect conversions to (void *) as well.
>
It does not, but such conversions (where defined) still yield null
pointers, and null pointers are constant expressions.

> Specifically, which lines of this simple program, if any, are invalid?
>
> int main() {
> int (*fp[])() = {
> 0,

Allowed. '0' is a constant integral expression evaluating to zero.

> (void *) 0,
Allowed. '(void *) 0' is a constant integral expression evaluating to
zero, cast to void*.

> (void *) (void *) 0,

Allowed. '(void*) 0' a null pointer constant as per above; this
expression cast to void* is a null pointer, and a null pointer is an
address constant allowed to serve as an initializer.

> (void *) (char *) 0,

Allowed. '0' is a null pointer constant, '(char*) 0' a null pointer, and
null pointers may be converted to any pointer type, yielding another
null pointer.

> (void *) (int) (void *) 0,

Implementation-defined/undefined. A null pointer need not be convertible
to an int and back, and a pointer cast to an integer need not be
accepted as a constant expression. If a null pointer is not
representable as an int, this is undefined.

> (int) (void *) 0
Conversion as above, but additionally a constraint violation, since
initializers are converted per the rules of simple assignment, which
does not allow assigning integers to pointers.

> };
> }
>
> I'm pretty sure 0 and (void *) 0 should be fine, and for the last I
> don't see at all how it might be valid, but for the other three I'm not
> sure. gcc (4.0.2) and icc (9.0) accept all, but tendra (20051114) and
> comeau (4.3.3 beta, online) accept only the first two.
>

Compilers can accept all of these expressions, but are only
required to accept the first four, and must issue a diagnostic about the
last. I believe Comeau and Tendra are in error when they reject the
third and fourth expression: these expressions are not null pointer
constants, but they are constant expressions that evaluate to null
pointers, and should therefore be allowed in initializers.

The other compilers are in error if they do not issue a diagnostic about
the last expression. gcc seems to accept (int) (void*) 0 as a null
pointer constant and therefore allows the initialization without a
warning, but this is wrong.

> Additionally, does any of this change if fp is declared static?

Yes, since "all the expressions in an initializer for an object that has
static storage duration shall be constant expressions or string
literals". The last two expressions do not necessarily qualify.

> I don't see why, and with gcc, tendra and comeau nothing changes, but
> icc now gives warnings ("warning #32: expression must have arithmetic
> type") for the last two.
>

A peculiar warning; it seems to be talking about arithmetic constant
expressions, but that doesn't seem directly relevant. It could very well
be a result of the restriction I pointed out.

S.

Tim Rentsch

unread,
Nov 16, 2005, 5:40:43 AM11/16/05
to
"=?utf-8?B?SGFyYWxkIHZhbiBExLNr?=" <tru...@gmail.com> writes:

Of the expressions listed, only '0' and '(void*) 0' are
guaranteed by the Standard to be null pointer constants.

The other expressions presumably are implementation defined
null pointer constants (when accepted by their respective
implementations). There was discussion here recently about
whether the Standard allows some null pointer constants to
be implementation defined, and I don't mean to reactivate
that discussion; it does look like gcc and icc have
interpreted the Standard to mean that implementation defined
null pointer constants are allowed.

I haven't used icc, but I'm guessing their warning message
has to do with the more stringent requirements for static
initializers, which must be constant expressions. If you
read through section 6.6 (it's only two pages) I expect
you'll see what I mean. Perhaps the message isn't called
for when the initializer is a null pointer constant, but
it's easy to believe that it could have been left in anyway.
A good way to test this hypothesis is:

static int some_int_object;
int *x = (void*)(int )(void*) &some_int_object;
int *y = (void*)(int*)(void*) &some_int_object;

If the line with 'x' gets the same warning message and the
line with 'y' doesn't, the message probably indicates some
kind of problem meeting the requirements of 6.6 (regardless
of whether or not such an indication should result in a
diagnostic).

Tim Rentsch

unread,
Nov 16, 2005, 6:52:05 AM11/16/05
to
Skarmander <inv...@dontmailme.com> writes:

> Harald van Dijk wrote:
> > What is a null pointer constant? I'm aware of the definition, but I'm
> > not sure if this allows indirect conversions to (void *) as well.
> >
> It does not, but such conversions (where defined) still yield null
> pointers, and null pointers are constant expressions.

That's true.


> > Specifically, which lines of this simple program, if any, are invalid?
> >
> > int main() {
> > int (*fp[])() = {
> > 0,
> Allowed. '0' is a constant integral expression evaluating to zero.
>
> > (void *) 0,
> Allowed. '(void *) 0' is a constant integral expression evaluating to
> zero, cast to void*.

Right so far.


> > (void *) (void *) 0,
> Allowed. '(void*) 0' a null pointer constant as per above; this
> expression cast to void* is a null pointer, and a null pointer is an
> address constant allowed to serve as an initializer.

It's true that the expression is a constant expression yielding
a null pointer, but the expression has the wrong type. What's
needed is a function pointer type, and '(void*)(void*)0' doesn't
qualify in that regard. This expression should work only if
it's a null pointer constant.


> > (void *) (char *) 0,
> Allowed. '0' is a null pointer constant, '(char*) 0' a null pointer, and
> null pointers may be converted to any pointer type, yielding another
> null pointer.

Same statement as above - wrong type as a null pointer, should work
only if the expression is a null pointer constant.


[...]


> > I'm pretty sure 0 and (void *) 0 should be fine, and for the last I
> > don't see at all how it might be valid, but for the other three I'm not
> > sure. gcc (4.0.2) and icc (9.0) accept all, but tendra (20051114) and
> > comeau (4.3.3 beta, online) accept only the first two.
> >
> Compilers can accept all of these expressions, but are only
> required to accept the first four, and must issue a diagnostic about the
> last. I believe Comeau and Tendra are in error when they reject the
> third and fourth expression: these expressions are not null pointer
> constants, but they are constant expressions that evaluate to null
> pointers, and should therefore be allowed in initializers.

Only '0' and '(void*) 0' are required to work. All the rest may be
accepted, without diagnostic, if they are implementation defined
null pointer constants.


> The other compilers are in error if they do not issue a diagnostic about
> the last expression. gcc seems to accept (int) (void*) 0 as a null
> pointer constant and therefore allows the initialization without a
> warning, but this is wrong.

There was discussion here in comp.std.c recently about whether
the Standard allows implementation defined null pointer constants.
I believe there was a general consensus by the end that they are,
under a blanket provision that implementations may define any
additional extensions they wish as long as it doesn't change the
behavior of any strictly conforming program. Under that provision
(it was 4p6 IIRC), gcc is free to define '(int)(void*)0' as a null
pointer constant.

Jordan Abel

unread,
Nov 16, 2005, 7:05:18 AM11/16/05
to
On 2005-11-16, Tim Rentsch <t...@alumnus.caltech.edu> wrote:
> Skarmander <inv...@dontmailme.com> writes:
>
>> Harald van Dijk wrote:
>> > What is a null pointer constant? I'm aware of the definition, but I'm
>> > not sure if this allows indirect conversions to (void *) as well.
>> >
>> It does not, but such conversions (where defined) still yield null
>> pointers, and null pointers are constant expressions.
>
> That's true.
>
>> > Specifically, which lines of this simple program, if any, are invalid?
>> >
>> > int main() {
>> > int (*fp[])() = {
>> > 0,
>> Allowed. '0' is a constant integral expression evaluating to zero.
>>
>> > (void *) 0,
>> Allowed. '(void *) 0' is a constant integral expression evaluating to
>> zero, cast to void*.
>
> Right so far.

Is it? It's no more right than the next one,

>> > (void *) (void *) 0,
>> Allowed. '(void*) 0' a null pointer constant as per above; this
>> expression cast to void* is a null pointer, and a null pointer is an
>> address constant allowed to serve as an initializer.
>
> It's true that the expression is a constant expression yielding
> a null pointer, but the expression has the wrong type. What's
> needed is a function pointer type, and '(void*)(void*)0' doesn't
> qualify in that regard. This expression should work only if
> it's a null pointer constant.

Then the second would also be wrong.

>> > I'm pretty sure 0 and (void *) 0 should be fine, and for the last I
>> > don't see at all how it might be valid, but for the other three I'm not
>> > sure. gcc (4.0.2) and icc (9.0) accept all, but tendra (20051114) and
>> > comeau (4.3.3 beta, online) accept only the first two.
>> >
>> Compilers can accept all of these expressions, but are only
>> required to accept the first four, and must issue a diagnostic about the
>> last. I believe Comeau and Tendra are in error when they reject the
>> third and fourth expression: these expressions are not null pointer
>> constants, but they are constant expressions that evaluate to null
>> pointers, and should therefore be allowed in initializers.
>
> Only '0' and '(void*) 0' are required to work. All the rest may be
> accepted, without diagnostic, if they are implementation defined
> null pointer constants.

is (void*)0 guaranteed to work when it's a function pointer?

Skarmander

unread,
Nov 16, 2005, 7:34:06 AM11/16/05
to
Tim Rentsch wrote:
> Skarmander <inv...@dontmailme.com> writes:
>
>
>>Harald van Dijk wrote:
<snip>

>>> (void *) (void *) 0,
>>
>>Allowed. '(void*) 0' a null pointer constant as per above; this
>>expression cast to void* is a null pointer, and a null pointer is an
>>address constant allowed to serve as an initializer.
>
>
> It's true that the expression is a constant expression yielding
> a null pointer, but the expression has the wrong type. What's
> needed is a function pointer type, and '(void*)(void*)0' doesn't
> qualify in that regard. This expression should work only if
> it's a null pointer constant.
>
Oops, you're right. The standard doesn't allow assignment of void* to
function pointers, null pointers or no. Had the array used pointers to
objects, it would have been allowed.

This, of course, being the point of declaring the array as an array of
function pointers in the first place... Stupid me.

<snip>


>>The other compilers are in error if they do not issue a diagnostic about
>>the last expression. gcc seems to accept (int) (void*) 0 as a null
>>pointer constant and therefore allows the initialization without a
>>warning, but this is wrong.
>
>
> There was discussion here in comp.std.c recently about whether
> the Standard allows implementation defined null pointer constants.
> I believe there was a general consensus by the end that they are,
> under a blanket provision that implementations may define any
> additional extensions they wish as long as it doesn't change the
> behavior of any strictly conforming program. Under that provision
> (it was 4p6 IIRC), gcc is free to define '(int)(void*)0' as a null
> pointer constant.

This is a peculiar line of reasoning, since it seems to give compilers
great leeway in suppressing all manner of diagnostics if they can simply
redefine operations in ways that do not change the behavior of strictly
conforming programs -- when issuing diagnostics for constraint
violations is also something the standard requires.

Could gcc decide to declare 0.0 a null pointer constant? Strictly
conforming programs can't use this, after all, so why not? Well, for one
thing, it makes it harder to check for conformity if nothing else. Even
if gcc *can* do this, it probably shouldn't. But then, that's not
something the standard dictates.

S.

kuy...@wizard.net

unread,
Nov 16, 2005, 7:39:54 AM11/16/05
to
Jordan Abel wrote:
> On 2005-11-16, Tim Rentsch <t...@alumnus.caltech.edu> wrote:
> > Skarmander <inv...@dontmailme.com> writes:
> >
> >> Harald van Dijk wrote:
...

> >> > Specifically, which lines of this simple program, if any, are invalid?
> >> >
> >> > int main() {
> >> > int (*fp[])() = {
> >> > 0,
> >> Allowed. '0' is a constant integral expression evaluating to zero.
> >>
> >> > (void *) 0,
> >> Allowed. '(void *) 0' is a constant integral expression evaluating to
> >> zero, cast to void*.
> >
> > Right so far.
>
> Is it? It's no more right than the next one,
>
> >> > (void *) (void *) 0,
> >> Allowed. '(void*) 0' a null pointer constant as per above; this
> >> expression cast to void* is a null pointer, and a null pointer is an
> >> address constant allowed to serve as an initializer.
> >
> > It's true that the expression is a constant expression yielding
> > a null pointer, but the expression has the wrong type. What's
> > needed is a function pointer type, and '(void*)(void*)0' doesn't
> > qualify in that regard. This expression should work only if
> > it's a null pointer constant.
>
> Then the second would also be wrong.

The type constraints and conversions for intializers are the same as
for simple assignment (6.7.8p11). Those constraints are listed in
6.5.16.1p1. There is a special case for assigning a null pointer
constant to a pointer, which covers (void*)0. (void*)(void*)0 is not a
null pointer constant, it is a constant expression whose value is a
null pointer, but it does not qualify as a null pointer constant.
Therefore, that case doesn't apply, in which case the type of the
initializer must be compatible with the type of the object being
initialized. (void*) isn't compatible with function pointer types.

...


> is (void*)0 guaranteed to work when it's a function pointer?

(void*)0 is never a function pointer. It's a null pointer with type
void* that happens to also qualify as a null pointer constant. Because
it is a null pointer constant, it can legally used to initialize any
pointer, even of a type incompatible with 'void*'. The result is a null
pointer of the specified type. Null pointer constants can also be
assigned to or compared with pointers of any type.

Skarmander

unread,
Nov 16, 2005, 7:44:22 AM11/16/05
to
Jordan Abel wrote:
> On 2005-11-16, Tim Rentsch <t...@alumnus.caltech.edu> wrote:
>
>>Skarmander <inv...@dontmailme.com> writes:
>>
>>
>>>Harald van Dijk wrote:
>>>
>>>>What is a null pointer constant? I'm aware of the definition, but I'm
>>>>not sure if this allows indirect conversions to (void *) as well.
>>>>
>>>
>>>It does not, but such conversions (where defined) still yield null
>>>pointers, and null pointers are constant expressions.
>>
>>That's true.
>>
>>
>>>>Specifically, which lines of this simple program, if any, are invalid?
>>>>
>>>>int main() {
>>>> int (*fp[])() = {
>>>> 0,
>>>
>>>Allowed. '0' is a constant integral expression evaluating to zero.
>>>
>>>
>>>> (void *) 0,
>>>
>>>Allowed. '(void *) 0' is a constant integral expression evaluating to
>>>zero, cast to void*.
>>
>>Right so far.
>
>
> Is it? It's no more right than the next one,
>
>>>> (void *) (void *) 0,

Yes, it is. (void*) 0 is explicitly defined in the standard as a null
pointer constant. (void*) (void*) 0 is not.

<snip>


> is (void*)0 guaranteed to work when it's a function pointer?

(void*) 0 is not a function pointer. You probably mean something like

int (*fp)() = (void*) 0;

which is allowed, since (void*) 0 is a null pointer constant (though
arguably you should use "0" or "NULL" here for clarity). But

void* v = 0;
int (*fp)() = v;

is not allowed, since v is not a null pointer constant. So in this
sense, yes, (void*) 0 is "guaranteed to work".

S.

Jordan Abel

unread,
Nov 16, 2005, 8:49:27 AM11/16/05
to

The antecedent of "it" was "the variable to which (void*)0 is being
assigned".

> It's a null pointer with type void* that happens to also qualify as a
> null pointer constant. Because it is a null pointer constant, it can
> legally used to initialize any pointer, even of a type incompatible
> with 'void*'. The result is a null pointer of the specified type. Null
> pointer constants can also be assigned to or compared with pointers of
> any type.

It seems bizarre that an exception to type rules should be made to cover
this case. And anyway, it's not clear from the standard that this isn't
required in the general case for null pointers - The only word on it
seems to be:

c89 3.2.2.3
| Two null pointers, converted through possibly different sequences
|of casts to pointer types, shall compare equal.

It's not guaranteed that a function pointer can generally be represented
as a void *, or vice versa, but a reasonable interpretation of this
would be that it is impossible for a null pointer to lose its
"null-ness" across _ANY_ sequence of casts, even one that might not
preserve other pointer values [say, char * to int * and back, for
another example] such as between void * and a function pointer.

Jordan Abel

unread,
Nov 16, 2005, 8:50:21 AM11/16/05
to
On 2005-11-16, Skarmander <inv...@dontmailme.com> wrote:
> int (*fp)() = (void*) 0;
>
> which is allowed, since (void*) 0 is a null pointer constant (though
> arguably you should use "0" or "NULL" here for clarity). But
>
> void* v = 0;
> int (*fp)() = v;
>
> is not allowed, since v is not a null pointer constant.

No, but it is a null pointer.

Skarmander

unread,
Nov 16, 2005, 9:19:03 AM11/16/05
to

Yes, but that doesn't matter. The standard gives null pointers no
special privileges to circumvent assignment constraints, and the type of
the null pointer in question is void*, which cannot be assigned to a
function pointer.

The standard does not make provisions for converting void* to int (*)(),
so the bit where it says "conversion of a null pointer to another
pointer type yields a null pointer of that type" doesn't apply.
Implementations that provide this as an extension could be required to
do proper null pointer conversion, but as a whole it's not portable --
and the question was whether it's legal, which in general it's not.

S.

Harald van Dijk

unread,
Nov 16, 2005, 9:21:45 AM11/16/05
to
Thank you for your replies. I've also read the other thread mentioned,
and if I'm understanding correctly, the basic idea is that the 0 and
(void *) 0 are null pointer constants unconditionally, and the others
are if and only if they are documented as such (which they don't seem
to be -- at least, yet -- for gcc). Is that right?

I suppose this all is different in C++ (for which the compilers still
disagree, by the way), but that's not relevant here.

Skarmander

unread,
Nov 16, 2005, 9:40:46 AM11/16/05
to
Harald van Dijk wrote:
> Thank you for your replies. I've also read the other thread mentioned,
> and if I'm understanding correctly, the basic idea is that the 0 and
> (void *) 0 are null pointer constants unconditionally, and the others
> are if and only if they are documented as such (which they don't seem
> to be -- at least, yet -- for gcc). Is that right?
>
Yes. Conforming implementations must document extensions, so if a
compiler wants to allow (int) (void*) 0 as a null pointer constant, it
has to say so.

gcc seems to need a note of the form "any constant expression implicitly
convertible to integral zero is a null pointer constant", since that is
what it appears to use. By this rule, (char*) 0 is not a null pointer
constant (just a null pointer) and gcc warns on assignment, but (int)
(char*) 0 is an npc. If you ask me, this extension is of dubious value,
and probably just an artifact of the way gcc evaluates and types
constant expressions.

> I suppose this all is different in C++ (for which the compilers still
> disagree, by the way), but that's not relevant here.
>

No. The main change in C++ is, of course, that simple assignment no
longer allows void* to be assigned without explicit conversion. I don't
know how exactly it defines null pointers and associated rules, but I
wouldn't expect it to be wildly different from C in this regard.

S.

Harald van Dijk

unread,
Nov 16, 2005, 9:51:32 AM11/16/05
to
Skarmander wrote:
> > I suppose this all is different in C++ (for which the compilers still
> > disagree, by the way), but that's not relevant here.
> >
> No. The main change in C++ is, of course, that simple assignment no
> longer allows void* to be assigned without explicit conversion. I don't
> know how exactly it defines null pointers and associated rules, but I
> wouldn't expect it to be wildly different from C in this regard.

The whole reason for allowing these additional null pointer constants
in C is because it can not change the behaviour of any strictly
conforming program; it can in C++ (because of overloading). Did I
misunderstand the reasoning?

Skarmander

unread,
Nov 16, 2005, 12:14:15 PM11/16/05
to
It's more accurate to say C doesn't *disallow* additional constants as
long as they don't change the behavior of conforming programs (so no
defining "Hello, world!\n" as a null pointer constant). The standard
doesn't actually say explicitly that alternative constants are allowed
or encouraged, and I doubt the committee's intent was to make more null
pointer constants available to an implementation. It's just a side
effect of not being more specific than you have to.

Again, I don't actually know what the C++ standard has to say on null
pointers, and it's not relevant in this ng. But C++ will obviously
tighten the rules on what null pointer constants are allowed and how
null pointers convert, because it has a different type system (and
overloading does play a role in that, though the exact rules C++ has for
this make my head hurt).

S.

Keith Thompson

unread,
Nov 16, 2005, 3:35:07 PM11/16/05
to
Jordan Abel <jma...@purdue.edu> writes:
[...]

> It seems bizarre that an exception to type rules should be made to cover
> this case. And anyway, it's not clear from the standard that this isn't
> required in the general case for null pointers - The only word on it
> seems to be:
>
> c89 3.2.2.3
> | Two null pointers, converted through possibly different sequences
> |of casts to pointer types, shall compare equal.
>
> It's not guaranteed that a function pointer can generally be represented
> as a void *, or vice versa, but a reasonable interpretation of this
> would be that it is impossible for a null pointer to lose its
> "null-ness" across _ANY_ sequence of casts, even one that might not
> preserve other pointer values [say, char * to int * and back, for
> another example] such as between void * and a function pointer.

C89 3.2.2.3 doesn't apply here, because there is no conversions
(either implicit or explicit) between function pointers and other
pointers, except for the special case of converting a null pointer
constant.

--
Keith Thompson (The_Other_Keith) ks...@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.

Keith Thompson

unread,
Nov 16, 2005, 3:49:34 PM11/16/05
to

More precisely, it's an expression that evaluates to a null pointer
value at execution time. But that can't be relevant to the legality
of the initialization. If it were, something like this:

int foo;
void *v = (rand()>RAND_MAX/2 ? 0 : &foo);
int (*fp)() = v;

would be randomly legal or illegal.

It's important to keep in mind that a "null pointer constant" is a
source code construct, and a "null pointer" is a run-time value, and
never the twain shall meet. Whether something happens to have a null
pointer value cannot affect the compile-time legality of any program.
Legality can only be affected by the narrower question of whether
something is a null pointer constant.

Wojtek Lerch

unread,
Nov 16, 2005, 3:55:04 PM11/16/05
to
Keith Thompson wrote:
> int foo;
> void *v = (rand()>RAND_MAX/2 ? 0 : &foo);
> int (*fp)() = v;
>
> would be randomly legal or illegal.

Pseudo-randomly. ;-)

Keith Thompson

unread,
Nov 16, 2005, 4:29:27 PM11/16/05
to

And I thought *I* was pedantic! 8-)}

Tim Rentsch

unread,
Nov 16, 2005, 5:34:58 PM11/16/05
to
Keith Thompson <ks...@mib.org> writes:

> Jordan Abel <jma...@purdue.edu> writes:
> > On 2005-11-16, Skarmander <inv...@dontmailme.com> wrote:
> >> int (*fp)() = (void*) 0;
> >>
> >> which is allowed, since (void*) 0 is a null pointer constant (though
> >> arguably you should use "0" or "NULL" here for clarity). But
> >>
> >> void* v = 0;
> >> int (*fp)() = v;
> >>
> >> is not allowed, since v is not a null pointer constant.
> >
> > No, but it is a null pointer.
>
> More precisely, it's an expression that evaluates to a null pointer
> value at execution time. But that can't be relevant to the legality
> of the initialization. If it were, something like this:
>
> int foo;
> void *v = (rand()>RAND_MAX/2 ? 0 : &foo);
> int (*fp)() = v;
>
> would be randomly legal or illegal.
>
> It's important to keep in mind that a "null pointer constant" is a
> source code construct, and a "null pointer" is a run-time value, and
> never the twain shall meet. Whether something happens to have a null
> pointer value cannot affect the compile-time legality of any program.
> Legality can only be affected by the narrower question of whether
> something is a null pointer constant.

I agree about the distinction between null pointer constants
and null pointers, with an important exception: some
constant expressions are legal if they are null pointers,
and null-pointerness in these cases is determined at compile
time, based on null pointer constants in the expression.

Tim Rentsch

unread,
Nov 16, 2005, 5:50:35 PM11/16/05
to
Jordan Abel <jma...@purdue.edu> writes:

Hi Jordan,

Two questions:

1. Do you have a copy of the Standard or the more recent
draft N1124?

2. Do you consult one or the other of these documents
in an effort to discover (or verify) what you're
commenting on?

Since you're becoming a regular poster here and c.l.c,
I suggest that both questions merit a "yes" answer.

The suggestion is meant to help you as much as anyone
else; I know I learned a lot more about what the
Standard says when I tried to find answers in it
myself before following up in the newsgroups.

Keith Thompson

unread,
Nov 16, 2005, 5:53:36 PM11/16/05
to

Do you have a citation for that? I remember seeing a reference to
"null pointer" somewhere in the standard that should have referred to
a "null pointer constant".

Tim Rentsch

unread,
Nov 16, 2005, 6:11:15 PM11/16/05
to
Skarmander <inv...@dontmailme.com> writes:

> Tim Rentsch wrote:
[...]


> > There was discussion here in comp.std.c recently about whether
> > the Standard allows implementation defined null pointer constants.
> > I believe there was a general consensus by the end that they are,
> > under a blanket provision that implementations may define any
> > additional extensions they wish as long as it doesn't change the
> > behavior of any strictly conforming program. Under that provision
> > (it was 4p6 IIRC), gcc is free to define '(int)(void*)0' as a null
> > pointer constant.
>
> This is a peculiar line of reasoning, since it seems to give compilers
> great leeway in suppressing all manner of diagnostics if they can simply
> redefine operations in ways that do not change the behavior of strictly
> conforming programs -- when issuing diagnostics for constraint
> violations is also something the standard requires.
>
> Could gcc decide to declare 0.0 a null pointer constant? Strictly
> conforming programs can't use this, after all, so why not? Well, for one
> thing, it makes it harder to check for conformity if nothing else. Even
> if gcc *can* do this, it probably shouldn't. But then, that's not
> something the standard dictates.

It does seem peculiar, but I believe the consequences are what was
intended. I think the reasoning is that whether usual diagnostics
are hidden by extensions is more a quality of implementation issue.

There _is_ the requirement that any extensions be documented; if
gcc doesn't document 0.0 being a null pointer constant, then there's
a strong argument that issuing a diagnostic is required and not
just a question about quality of implementation.

Tim Rentsch

unread,
Nov 16, 2005, 6:16:33 PM11/16/05
to
Keith Thompson <ks...@mib.org> writes:

Section 6.6, paragraph 9. I had the same reaction that you did:
I read "null pointer", and thought to myself "oh, they mean
null pointer constant". Then I read it over a couple more times,
and now I'm pretty sure "null pointer" is both what was meant and
what's appropriate. But I'd be interested to hear your take
on it.

Keith Thompson

unread,
Nov 16, 2005, 7:16:57 PM11/16/05
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:
> Keith Thompson <ks...@mib.org> writes:
>> Tim Rentsch <t...@alumnus.caltech.edu> writes:
>> > Keith Thompson <ks...@mib.org> writes:
[...]

>> >> It's important to keep in mind that a "null pointer constant" is a
>> >> source code construct, and a "null pointer" is a run-time value, and
>> >> never the twain shall meet. Whether something happens to have a null
>> >> pointer value cannot affect the compile-time legality of any program.
>> >> Legality can only be affected by the narrower question of whether
>> >> something is a null pointer constant.
>> >
>> > I agree about the distinction between null pointer constants
>> > and null pointers, with an important exception: some
>> > constant expressions are legal if they are null pointers,
>> > and null-pointerness in these cases is determined at compile
>> > time, based on null pointer constants in the expression.
>>
>> Do you have a citation for that? I remember seeing a reference to
>> "null pointer" somewhere in the standard that should have referred to
>> a "null pointer constant".
>
> Section 6.6, paragraph 9. I had the same reaction that you did:
> I read "null pointer", and thought to myself "oh, they mean
> null pointer constant". Then I read it over a couple more times,
> and now I'm pretty sure "null pointer" is both what was meant and
> what's appropriate. But I'd be interested to hear your take
> on it.

It says:

An _address constant_ is a null pointer, a pointer to an lvalue
designating an object of static storage duration, or a pointer to
a function designator;
[...]

I'm convinced that "null pointer" should be "null pointer constant"
here. The only in interpretation I can think of for "null pointer" is
an expression that evaluates to a null pointer value, which again
raises the issue of not being able to determine until execution time
whether a program is legal.

Can you provide an example of an address constant (or something that
should be considered an address constant) that's (an expression
evaluating to) a null pointer without being a null pointer constant?

Christian Bau

unread,
Nov 16, 2005, 7:21:28 PM11/16/05
to
In article <lnsltwg...@nuthaus.mib.org>,
Keith Thompson <ks...@mib.org> wrote:

> Can you provide an example of an address constant (or something that
> should be considered an address constant) that's (an expression
> evaluating to) a null pointer without being a null pointer constant?

(char *) NULL

Keith Thompson

unread,
Nov 16, 2005, 8:05:02 PM11/16/05
to

Ok, but given

void *np = NULL;

the expression np evaluates to a null pointer, but clearly isn't an
address constant.

Possibly the phrase "null pointer" should be changed to something like
"null pointer constant, possibly cast to a pointer type".

Skarmander

unread,
Nov 16, 2005, 8:57:14 PM11/16/05
to
Keith Thompson wrote:
> Christian Bau <christ...@cbau.freeserve.co.uk> writes:
>
>>In article <lnsltwg...@nuthaus.mib.org>,
>> Keith Thompson <ks...@mib.org> wrote:
>>
>>
>>>Can you provide an example of an address constant (or something that
>>>should be considered an address constant) that's (an expression
>>>evaluating to) a null pointer without being a null pointer constant?
>>
>>(char *) NULL
>
>
> Ok, but given
>
> void *np = NULL;
>
> the expression np evaluates to a null pointer, but clearly isn't an
> address constant.
>
True, because this value is not "created explicitly using the unary &
operator or an integer constant cast to pointer type, or implicitly by
the use of an expression of array or function type". There is, as far as
I can think of, no way to implicitly create a null pointer through an
expression of array or function type, and explicit creation rules out
the use of a variable.

> Possibly the phrase "null pointer" should be changed to something like
> "null pointer constant, possibly cast to a pointer type".
>

But that's exactly what a null pointer *is*, per 6.3.2.3. Unless you're
still of the opinion that "conversion" can only ever happen at runtime
or that compile-time casts do not really "convert" in this sense, which
we recently had some debate over.

S.

Skarmander

unread,
Nov 16, 2005, 8:59:10 PM11/16/05
to
Skarmander wrote:
> Keith Thompson wrote:
<snip>

>> Possibly the phrase "null pointer" should be changed to something like
>> "null pointer constant, possibly cast to a pointer type".
>>
> But that's exactly what a null pointer *is*, per 6.3.2.3.

Grammar snafu; I am not implying equivalence between these definitions,
only contending that a "null pointer constant cast to a pointer type" is
a null pointer.

S.

Tim Rentsch

unread,
Nov 16, 2005, 9:38:12 PM11/16/05
to
Keith Thompson <ks...@mib.org> writes:

Address constants are a subset of constant expressions. There
are definite rules for what expressions qualify as constant
expressions; in particular, their value must be determined at
translation time, ie, before execution starts. So I don't think
the concern about being able to determine legality is really
an issue.


> Can you provide an example of an address constant (or something that
> should be considered an address constant) that's (an expression
> evaluating to) a null pointer without being a null pointer constant?

Let me give it a try:

extern unsigned int foo();

static int (*pf)() =
(int (*)( int )) ( sizeof(int) > sizeof(short) ? foo : 0 );

The initializing expression can evaluate to a null pointer (to a
function type). It does include a null pointer constant, but I
wouldn't say that it is a null pointer constant.

kuy...@wizard.net

unread,
Nov 16, 2005, 10:33:15 PM11/16/05
to
Jordan Abel wrote:
> On 2005-11-16, kuy...@wizard.net <kuy...@wizard.net> wrote:
> > Jordan Abel wrote:
...
> >> is (void*)0 guaranteed to work when it's a function pointer?
> >
> > (void*)0 is never a function pointer.
>
> The antecedent of "it" was "the variable to which (void*)0 is being
> assigned".

OK - that makes a bit more sense. In that case, the answer is simply
"Yes".

> > It's a null pointer with type void* that happens to also qualify as a
> > null pointer constant. Because it is a null pointer constant, it can
> > legally used to initialize any pointer, even of a type incompatible
> > with 'void*'. The result is a null pointer of the specified type. Null
> > pointer constants can also be assigned to or compared with pointers of
> > any type.
>
> It seems bizarre that an exception to type rules should be made to cover

> this case. ...

It is. It would have made a lot more sense to have a keyword for this
purpose ("null" or "nill" have been proposed), and have the special
case handling apply to that keyword. It would be illegal to use this
keyword in a non-pointer context, which is the most important reason
why such a keyword would be an improvement over the current way C does
this.

The reason for the wierd situation we have now is that early versions
of the C language were not as type-sensitive as the current language.
The constant "0" was pretty much the closest thing there was to a
"null" keyword; except that it wasn't universally the right thing.
Instead of a keyword, a macro named NULL was defined by each
implementation to be whatever it needed to be to work on that
implementation (somewhat) like that keyword should have worked. When
the language was made more type sensitive, this type-insensitive idiom
was so common it was just not feasible to outlaw it. Instead, they
created special rules to make it legal. I think this was the right
decision; but it is one of the ways in which the need (which is very
real) for C to maintain backward compatibility leads to making C less
well-designed than it could be.

And anyway, it's not clear from the standard that this isn't
> required in the general case for null pointers - The only word on it
> seems to be:
>
> c89 3.2.2.3
> | Two null pointers, converted through possibly different sequences
> |of casts to pointer types, shall compare equal.
>
> It's not guaranteed that a function pointer can generally be represented
> as a void *, or vice versa, but a reasonable interpretation of this
> would be that it is impossible for a null pointer to lose its
> "null-ness" across _ANY_ sequence of casts, even one that might not
> preserve other pointer values [say, char * to int * and back, for
> another example] such as between void * and a function pointer.

Yes, it is a null pointer, but the special exception is for null
pointer constants, not for null pointers. Therefore, the fact that it's
null is irrelevant. There's an important reason for this: null pointer
constants were defined to be easily detectable as such at compile time,
because this special handling is meant to be a compile time issue. In
general, null pointers are detectable only at run time (though this
particular null pointer can be detected at compile time).

kuy...@wizard.net

unread,
Nov 16, 2005, 10:51:25 PM11/16/05
to

Keith Thompson wrote:
> Tim Rentsch <t...@alumnus.caltech.edu> writes:
...

> > Section 6.6, paragraph 9. I had the same reaction that you did:
> > I read "null pointer", and thought to myself "oh, they mean
> > null pointer constant". Then I read it over a couple more times,
> > and now I'm pretty sure "null pointer" is both what was meant and
> > what's appropriate. But I'd be interested to hear your take
> > on it.
>
> It says:
>
> An _address constant_ is a null pointer, a pointer to an lvalue
> designating an object of static storage duration, or a pointer to
> a function designator;
> [...]
>
> I'm convinced that "null pointer" should be "null pointer constant"
> here. The only in interpretation I can think of for "null pointer" is
> an expression that evaluates to a null pointer value, which again
> raises the issue of not being able to determine until execution time
> whether a program is legal.

It goes on to say:

"it shall be created explicitly using the unary & operator or an


integer constant cast to pointer type, or implicitly by the use of an

expression of array or function type. The array-subscript [] and
member-access . and -> operators, the address & and indirection * unary
operators, and pointer casts may be used in the creation of an address
constant, but the value of an object shall not be accessed by use of
these operators."

As far as I can tell, any null pointer you can create within those
restrictions of can be easily identified as null during translation.
Can you come up with a counter-example?

> Can you provide an example of an address constant (or something that
> should be considered an address constant) that's (an expression
> evaluating to) a null pointer without being a null pointer constant?

Any constant expression whose type is a pointer type and whose value is
null. In other words, in include everything that we would ordinarily
expect "null pointer constant" to refer to, if it weren't for the fact
that the standard has defined that phrase to have a more specific
meaning.
There's lots of different ways of creating them, though they all
necessarily involve use of a null pointer constant, somewhere along the
line.

kuy...@wizard.net

unread,
Nov 16, 2005, 11:10:29 PM11/16/05
to
Harald van Dijk wrote:
...

> I suppose this all is different in C++ (for which the compilers still
> disagree, by the way), but that's not relevant here.

The C++ definition of a null pointer constant is almost identical to
the first half of the C definition, except for the use of the term
"rvalue". The big difference is the missing second half of the
definition: in C++ (void*)0 is a constant expression with a pointer
type an a null value, but it is NOT a null pointer constant.

There's one other minor difference: the definition of "integer constant
expression" is broader than it is in C. A variable or static data
member of a class can be used, so long as it's declared const. This
allows for the following fascinating possibility:

In one of the standard headers that's required to #define NULL:
namespace std
{
class __ourclass
{
static const int __null = 0;
}
}
#define NULL std::__ourclass::__null

What's so interesting about this possibility is that using this a NULL
defined this way as an argument to an overloaded function could,
through the magic of Koenig lookup, cause a different function overload
to be selected than would have been used if you'd used "0" instead.

Jun Woong

unread,
Nov 17, 2005, 1:06:21 AM11/17/05
to
"Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message news:kfnzmo4...@alumnus.caltech.edu...

[...]
>
> There was discussion here in comp.std.c recently about whether
> the Standard allows implementation defined null pointer constants.
> I believe there was a general consensus by the end that they are,
> under a blanket provision that implementations may define any
> additional extensions they wish as long as it doesn't change the
> behavior of any strictly conforming program. Under that provision
> (it was 4p6 IIRC), gcc is free to define '(int)(void*)0' as a null
> pointer constant.

On the other hand, there was another discussion here in csc, in which
the conclusion was that the definition given in the standard is
intended to be exhaustive, so a conforming implementation is not free
to provide any implementation-defined form of the null pointer
constant (NPC) that is different from the definition the standard
provides. Suppose an implementation which #defines NULL as:

#define NULL ((void *)0.0)

and do not forget that a s.c. program can stringize NULL and check
at least whether it is equal to "((void *)0.0)".

I believe that the "implementation-defined" wording given to the NPC
is intended to requrie implementations to document their choices, not
to allow them to exploit it in order to provide their own forms of
the NPC.


--
Jun, Woong (woong at icu.ac.kr)
Information and Communications Univ.


Jun Woong

unread,
Nov 17, 2005, 1:36:26 AM11/17/05
to
"Keith Thompson" <ks...@mib.org> wrote in message news:lnek5gj...@nuthaus.mib.org...

> If it were, something like this:
>
> int foo;
> void *v = (rand()>RAND_MAX/2 ? 0 : &foo);
> int (*fp)() = v;
>
> would be randomly legal or illegal.
>

It is.

Jordan Abel

unread,
Nov 17, 2005, 1:59:45 AM11/17/05
to
On 2005-11-17, Jun Woong <wo...@icu.ac.kr> wrote:
> "Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message news:kfnzmo4...@alumnus.caltech.edu...
> [...]
>>
>> There was discussion here in comp.std.c recently about whether
>> the Standard allows implementation defined null pointer constants.
>> I believe there was a general consensus by the end that they are,
>> under a blanket provision that implementations may define any
>> additional extensions they wish as long as it doesn't change the
>> behavior of any strictly conforming program. Under that provision
>> (it was 4p6 IIRC), gcc is free to define '(int)(void*)0' as a null
>> pointer constant.
>
> On the other hand, there was another discussion here in csc, in which
> the conclusion was that the definition given in the standard is
> intended to be exhaustive, so a conforming implementation is not free
> to provide any implementation-defined form of the null pointer
> constant (NPC) that is different from the definition the standard
> provides. Suppose an implementation which #defines NULL as:
>
> #define NULL ((void *)0.0)
>
> and do not forget that a s.c. program can stringize NULL and check
> at least whether it is equal to "((void *)0.0)".

So? NULL can be any of "0" or "((void *)0)" or "((void*) 0)" or "((void
*) 0x00)", already. yes that's a newline in that last one. or any other
of infinitely many variations - What's your point? Besides, the
implementation could allow 0.0 as a NPC while still having NULL
#define'd as ((void *)0). or 0. or 0x00000000.

Jun Woong

unread,
Nov 17, 2005, 2:17:02 AM11/17/05
to
"Jun Woong" <wo...@icu.ac.kr> wrote in message news:dlh892$20f$1...@news2.kornet.net...

> "Keith Thompson" <ks...@mib.org> wrote in message news:lnek5gj...@nuthaus.mib.org...
> > If it were, something like this:
> >
> > int foo;
> > void *v = (rand()>RAND_MAX/2 ? 0 : &foo);
> > int (*fp)() = v;
> >
> > would be randomly legal or illegal.
> >
>
> It is.
>


Oops, sorry. I meant it is illegal, not that its
legality is variable at runtime.

Jun Woong

unread,
Nov 17, 2005, 2:25:48 AM11/17/05
to
"Jordan Abel" <jma...@purdue.edu> wrote in message news:slrndnoak2...@random.yi.org...

> On 2005-11-17, Jun Woong <wo...@icu.ac.kr> wrote:
[...]

> >
> > On the other hand, there was another discussion here in csc, in which
> > the conclusion was that the definition given in the standard is
> > intended to be exhaustive, so a conforming implementation is not free
> > to provide any implementation-defined form of the null pointer
> > constant (NPC) that is different from the definition the standard
> > provides. Suppose an implementation which #defines NULL as:
> >
> > #define NULL ((void *)0.0)
> >
> > and do not forget that a s.c. program can stringize NULL and check
> > at least whether it is equal to "((void *)0.0)".
>
> So? NULL can be any of "0" or "((void *)0)" or "((void*) 0)" or "((void
> *) 0x00)", already. yes that's a newline in that last one. or any other
> of infinitely many variations - What's your point? Besides, the
> implementation could allow 0.0 as a NPC while still having NULL
~~~~~~~~~~~~~~~~~

> #define'd as ((void *)0). or 0. or 0x00000000.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>

That's exactly my point. The standard does not forbid an
implementation from allowing, say, 0x80000000 to act as a null pointer
constant, because a s.c. program is not allowed to use it in a context
where the null pointer constant requried anyway. But even in that
case, an implementation cannot #define NULL as 0x80000000; the "i-d"
wording in "NULL is an i-d null pointer constant" is not to allow any
free form of the null pointer constant. There are already other cases
in the standard where it uses "i-d" just to require implementations to
document their choices among what the standard gives explicitly.

Jordan Abel

unread,
Nov 17, 2005, 2:31:54 AM11/17/05
to

Wouldn't that be "implementation-specified", though?

Keith Thompson

unread,
Nov 17, 2005, 2:37:05 AM11/17/05
to

No, the standard doesn't define the term "implementation-specified".

Jun Woong

unread,
Nov 17, 2005, 2:45:08 AM11/17/05
to
"Keith Thompson" <ks...@mib.org> wrote in message news:ln7jb8g...@nuthaus.mib.org...

>
> Possibly the phrase "null pointer" should be changed to something like
> "null pointer constant, possibly cast to a pointer type".
>

I don't see any reason to change it. As Skarmander pointed out, the
"null pointer constant cast to a pointer type" is a "null pointer."
The problem you see here arises only when trying to put more
properties into them than the standard actually says about them.

Tim Rentsch

unread,
Nov 17, 2005, 3:34:26 AM11/17/05
to
"Jun Woong" <wo...@icu.ac.kr> writes:

> "Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message news:kfnzmo4...@alumnus.caltech.edu...
> [...]
> >
> > There was discussion here in comp.std.c recently about whether
> > the Standard allows implementation defined null pointer constants.
> > I believe there was a general consensus by the end that they are,
> > under a blanket provision that implementations may define any
> > additional extensions they wish as long as it doesn't change the
> > behavior of any strictly conforming program. Under that provision
> > (it was 4p6 IIRC), gcc is free to define '(int)(void*)0' as a null
> > pointer constant.
>
> On the other hand, there was another discussion here in csc, in which
> the conclusion was that the definition given in the standard is
> intended to be exhaustive,

Do you have a reference? I did a search using Google Groups and
didn't find any such thread. If you're talking about the thread
that transpired in September of 2005, I would say it's a
mischaracterization to say the _conclusion_ was that the
definition is supposed to be exhaustive. There was _discussion_
about whether the definition in 6.3.2.3 p3 is supposed to be
exhaustive, but not a conclusion.

I'm not aware of any reliably authoritative statement that says
definitions in the Standard are meant to be exhaustive.


> so a conforming implementation is not free
> to provide any implementation-defined form of the null pointer
> constant (NPC) that is different from the definition the standard
> provides.

Even if the definition of null pointer constant in 6.3.2.3 p3 is
intended to be exhaustive as far as the Standard uses the term,
nothing prevents an implementation from defining an extension
under section 4 p6 that provides additional forms of null
pointer constants. Or, if you prefer, the extension could
define a term "null pointer constantoid", and define certain
expressional forms that are "null pointer constantoids", and
stipulate that "null pointer constantoids" behave just like null
pointer constants in any context that a null pointer constant is
legal, and otherwise exhibit no unusual behavior.


> Suppose an implementation which #defines NULL as:
>
> #define NULL ((void *)0.0)
>
> and do not forget that a s.c. program can stringize NULL and check
> at least whether it is equal to "((void *)0.0)".

The same implementation that provides an extension for a
different null pointer constant could also provide an extension
that stringizes its own NULL as "0". :)

More seriously, a strictly conforming program can't depend on
what NULL is defined as, since NULL is implementation defined,
and strictly conforming programs can't depend on implementation
defined behavior.


> I believe that the "implementation-defined" wording given to the NPC
> is intended to requrie implementations to document their choices, not
> to allow them to exploit it in order to provide their own forms of
> the NPC.

The statement in 7.17 p3 does require that the choice of which
null pointer constant NULL uses be documented. It does not, by
itself, say either that an implementation can, or that an
implementation cannot, define additional forms of null pointer
constant. But it doesn't need to; a general statement that
implementations may have extensions as long as they don't change
the behavior of any strictly conforming program is given in
section 4 p6. This provision grants permission to define other
forms of null pointer constants (or "null pointer constantoids",
which amounts to the same thing).

Incidentally, the most common definition for NULL seems to be
'((void*)0)'. Yet '((void*)0)' is not a null pointer constant
as defined by 6.3.2.3 p3.

Tim Rentsch

unread,
Nov 17, 2005, 3:37:41 AM11/17/05
to
Jordan Abel <jma...@purdue.edu> writes:

[...]


> So? NULL can be any of "0" or "((void *)0)" or "((void*) 0)" or "((void

> *) 0x00)", already. [...]

Technically the definition of null pointer constant doesn't admit
'((void*)0)' as a null pointer constant. It does admit '(void*)0',
but not '((void*)0)'.

Keith Thompson

unread,
Nov 17, 2005, 4:34:04 AM11/17/05
to

Agreed, but I think the general consensus is that this is an
oversight. There's also 7.1.2p5, which says:

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.

Keith Thompson

unread,
Nov 17, 2005, 4:43:27 AM11/17/05
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:
[...]

> I'm not aware of any reliably authoritative statement that says
> definitions in the Standard are meant to be exhaustive.

Neither am I -- which is a great pity.

[...]

> More seriously, a strictly conforming program can't depend on
> what NULL is defined as, since NULL is implementation defined,
> and strictly conforming programs can't depend on implementation
> defined behavior.

A strictly conforming program's *output* can't depend on
implementation defined behavior. As long as the output is the same
for any allowed behavior, the program can still be strictly
conforming.

Jordan Abel

unread,
Nov 17, 2005, 4:48:56 AM11/17/05
to
On 2005-11-17, Keith Thompson <ks...@mib.org> wrote:
> Tim Rentsch <t...@alumnus.caltech.edu> writes:
> [...]
>> I'm not aware of any reliably authoritative statement that says
>> definitions in the Standard are meant to be exhaustive.
>
> Neither am I -- which is a great pity.
>
> [...]
>
>> More seriously, a strictly conforming program can't depend on
>> what NULL is defined as, since NULL is implementation defined,
>> and strictly conforming programs can't depend on implementation
>> defined behavior.
>
> A strictly conforming program's *output* can't depend on
> implementation defined behavior. As long as the output is the same
> for any allowed behavior, the program can still be strictly
> conforming.

Which means stringifying NULL is probably off-limits.

Tim Rentsch

unread,
Nov 17, 2005, 5:26:19 AM11/17/05
to
Keith Thompson <ks...@mib.org> writes:

> Tim Rentsch <t...@alumnus.caltech.edu> writes:
> [...]
> > I'm not aware of any reliably authoritative statement that says
> > definitions in the Standard are meant to be exhaustive.
>
> Neither am I -- which is a great pity.

What I'd like is a complete list stating which definitions are
intended to be exhaustive and which ones aren't. Or, better yet, a
careful revising/rewriting of _all_ definitions in the Standard so
that they follow the usual "if and only if" rule, and an explicit
statement in the Standard that this has been done.


> [...]
>
> > More seriously, a strictly conforming program can't depend on
> > what NULL is defined as, since NULL is implementation defined,
> > and strictly conforming programs can't depend on implementation
> > defined behavior.
>
> A strictly conforming program's *output* can't depend on
> implementation defined behavior. As long as the output is the same
> for any allowed behavior, the program can still be strictly
> conforming.

Yes, as a technical distinction that's important. Thank you
for the reminder.

It's still true, however, that a strictly conforming program can't be
relied on to detect an implementation that chooses to define NULL
strangely, because the implementation can maliciously defeat any
attempt to use side channels that a strictly conforming program might
try to exploit to "output" the information.

Tim Rentsch

unread,
Nov 17, 2005, 5:45:07 AM11/17/05
to
Keith Thompson <ks...@mib.org> writes:

> Tim Rentsch <t...@alumnus.caltech.edu> writes:
> > Jordan Abel <jma...@purdue.edu> writes:
> >
> > [...]
> >> So? NULL can be any of "0" or "((void *)0)" or "((void*) 0)" or "((void
> >> *) 0x00)", already. [...]
> >
> > Technically the definition of null pointer constant doesn't admit
> > '((void*)0)' as a null pointer constant. It does admit '(void*)0',
> > but not '((void*)0)'.
>
> Agreed, but I think the general consensus is that this is an
> oversight. There's also 7.1.2p5, which says:
>
> 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.

Certainly there are places in the Standard where it looks like an
oversight occurred (or such a strained interpretation is required
that it might as well be called an oversight). I'm not sure this
is one of them, however. Cases like this one where the meanings
are pretty black and white seem less likely to be oversights than
cases where the language is a little slippery.

In any event, the uncertainty suggests an interesting experiment.
If someone were to put in a Defect Report that '((void*)0)' isn't
listed as a null pointer constant, then either a TC would be
issued correcting that, or the response would be that a TC isn't
necessary. In the first case an oversight is corrected; in the
second case we have a de facto answer to the question about
whether additional choices are available for NULL.

Incidentally, it's possible to meet the requirements of 7.1.2 p5
without having to resort to using ((void*)0). An implementation
can always arrange for NULL to be defined as 0, or (0).

Harald van Dijk

unread,
Nov 17, 2005, 5:54:42 AM11/17/05
to
Tim Rentsch wrote:
> It's still true, however, that a strictly conforming program can't be
> relied on to detect an implementation that chooses to define NULL
> strangely, because the implementation can maliciously defeat any
> attempt to use side channels that a strictly conforming program might
> try to exploit to "output" the information.

Do you mean an implentation might legitimately define NULL as, for
example,

#define NULL ((void *) 0.0)
#define #NULL "0L"

-- assuming such a preprocessor extension exists, ((void *) 0.0) is a
null pointer constant, and the difference between 0L and ((void *) 0.0)
can't otherwise be detected (for example via sizeof)?

Jordan Abel

unread,
Nov 17, 2005, 7:33:00 AM11/17/05
to

You can't destringize, and the program which detects that it's "0L"
won't be strictly conforming if it acts differently from if it found
((void*)0). I suppose it could use a full-fledged parser to find that
the 0L is in fact a long literal, and from there take sizeof(long). But
then it's not clear that the implementation isn't allowed to use
((void*)0.0) in the first place.

Jordan Abel

unread,
Nov 17, 2005, 7:46:02 AM11/17/05
to
On 2005-11-17, Tim Rentsch <t...@alumnus.caltech.edu> wrote:
> Incidentally, it's possible to meet the requirements of 7.1.2 p5
> without having to resort to using ((void*)0). An implementation
> can always arrange for NULL to be defined as 0, or (0).

Or, as suggested on clc last month, it could define it as (void *)0 and
use other tricks to make the precedence work [say, #pragma objectmacro
NULL] thus NULL would act as an object macro yet #NULL would be "(void
*)0" - but that would be evil.

Also, I would argue that a null pointer constant surrounded by
parentheses is still valid for initialization on the grounds that

c89 [draft] 3.3.1
A parenthesized expression is a primary expression. Its type and
value are identical to those of the unparenthesized expression.

appears to imply it [i could find it in a c99 draft given time, but my
c89 draft is in plaintext - if someone wants to assert that this changed
in c99 i want proof because it would be surprising.]

Although there is a question of what exactly "nullpointerconstant"ness
is - is it a value, or is it something more 'zen'?

Harald van Dijk

unread,
Nov 17, 2005, 7:57:20 AM11/17/05
to

A simple example, since it seems I wasn't clear in what I meant:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR(x) STR_(x)
#define STR_(x) #x
int main() {
if(strcmp(STR(NULL), "0L") == 0)
printf("%ld\n", NULL);
else
printf("%ld\n", 0L);
}

I believe this is required to print "0\n", and nothing else, regardless
of how NULL is defined. (Do correct me if I'm wrong.) These kinds of
programs is why it would be important in my example implementation for
0L to have the same size and representation as ((void *) 0.0). (I'd
still like to know if that definition is allowed, by the way.)

Jun Woong

unread,
Nov 17, 2005, 8:03:19 AM11/17/05
to

Tim Rentsch wrote:

> "Jun Woong" <wo...@icu.ac.kr> writes:
>
> >
> > On the other hand, there was another discussion here in csc, in which
> > the conclusion was that the definition given in the standard is
> > intended to be exhaustive,
>
> Do you have a reference?

The title of the discussion has "NULL", IIRC. And I was one of the
participants. Using this information google can bring it, I believe.

> I did a search using Google Groups and
> didn't find any such thread. If you're talking about the thread
> that transpired in September of 2005, I would say it's a
> mischaracterization to say the _conclusion_ was that the
> definition is supposed to be exhaustive. There was _discussion_
> about whether the definition in 6.3.2.3 p3 is supposed to be
> exhaustive, but not a conclusion.

Unfortunately, I don't have time to read the entire thread. But
casting a glance at it, I feel it is repetition of the past discussion
which I gave the reference to.

>
> I'm not aware of any reliably authoritative statement that says
> definitions in the Standard are meant to be exhaustive.
>

Clause 3:

For the purposes of this International Standard, the following
definitions apply. Other terms are defined where they appear in
italic type or on the left side of a syntax rule. Terms explicitly
defined in this International Standard are not to be presumed to
refer implicitly to similar terms defined elsewhere.

[...]


>
> Even if the definition of null pointer constant in 6.3.2.3 p3 is
> intended to be exhaustive as far as the Standard uses the term,
> nothing prevents an implementation from defining an extension
> under section 4 p6 that provides additional forms of null
> pointer constants. Or, if you prefer, the extension could
> define a term "null pointer constantoid", and define certain
> expressional forms that are "null pointer constantoids", and
> stipulate that "null pointer constantoids" behave just like null
> pointer constants in any context that a null pointer constant is
> legal, and otherwise exhibit no unusual behavior.
>

I've already said, in other branch of this thread, that I also think
it is possible for an implementation to define "null pointer
constantoids" which behaves exactly like the null pointer constant.
But it's a separate issue whether an implementation is allowed to
define NULL as the "null pointer constantoids."

>
> > Suppose an implementation which #defines NULL as:
> >
> > #define NULL ((void *)0.0)
> >
> > and do not forget that a s.c. program can stringize NULL and check
> > at least whether it is equal to "((void *)0.0)".
>
> The same implementation that provides an extension for a
> different null pointer constant could also provide an extension
> that stringizes its own NULL as "0". :)

Do you really think so? Please think about what the # operator is
supposed to do. The # operator applies to the expansion of the NULL
macro, not directly to the NULL macro. What you are talking about is
an extension with which the expension of NULL depends on the context.

>
> More seriously, a strictly conforming program can't depend on
> what NULL is defined as, since NULL is implementation defined,
> and strictly conforming programs can't depend on implementation
> defined behavior.
>

Please find in the past discussion a small "s.c." program whose output
does (and should) not change in every conforming implementation. Its
output differs only in *a* broken implementation which defines NULL as
its own "null pointer constantoids."

[...]


>
> Incidentally, the most common definition for NULL seems to be
> '((void*)0)'.

Nope!

#include <stdio.h> // which contains #define NULL ((void *))

#define void int // the standard allows this

void main()
{
double *p = NULL; // will cause problems

return 0;
}

My and most implementations use a reserved identifier like "__void" in
the place of "void" when defining NULL whether they, not me, know this
or not. I really hope the committee put a new constraint into the next
revision of the standard to prevent users from #defining keywords,
which would be very helpful to an implementor especially when he/she
designs masking macros for the real functions.

kuy...@wizard.net

unread,
Nov 17, 2005, 8:05:50 AM11/17/05
to
Jun Woong wrote:
...

> and do not forget that a s.c. program can stringize NULL and check
> at least whether it is equal to "((void *)0.0)".

The question of whether or not conformance can be checked in that
fashion has been raised many times.

On 2001-02-17 Clive D. W. Feather wrote:
> In article <3A8DB0BE...@arl.army.mil>, Douglas A. Gwyn
> <gw...@arl.army.mil> writes
> >> > > > ... there was a strong consensus that strict program conformance
> >> > > > is not affected by what the standard macros expand to.
>
> >It ought to be in 7.1.2 Standard headers, but I don't see it.
> >I'm pretty sure we responded to at least one DR along those lines.
>
> DR044 question 2.
>
> | A conforming implementation need not provide strictly conforming
> | expansion of macros defined by the standard headers.


Back to Jun Woong:


> I believe that the "implementation-defined" wording given to the NPC
> is intended to requrie implementations to document their choices, not
> to allow them to exploit it in order to provide their own forms of
> the NPC.

I agree.

kuy...@wizard.net

unread,
Nov 17, 2005, 8:30:24 AM11/17/05
to

Jordan Abel wrote:
> On 2005-11-17, Tim Rentsch <t...@alumnus.caltech.edu> wrote:
> > Incidentally, it's possible to meet the requirements of 7.1.2 p5
> > without having to resort to using ((void*)0). An implementation
> > can always arrange for NULL to be defined as 0, or (0).
>
> Or, as suggested on clc last month, it could define it as (void *)0 and
> use other tricks to make the precedence work [say, #pragma objectmacro
> NULL] thus NULL would act as an object macro yet #NULL would be "(void
> *)0" - but that would be evil.
>
> Also, I would argue that a null pointer constant surrounded by
> parentheses is still valid for initialization on the grounds that
>
> c89 [draft] 3.3.1
> A parenthesized expression is a primary expression. Its type and
> value are identical to those of the unparenthesized expression.

You keep missing the point: whether or not something is a null pointer
constant is not determined by the type or the value of the expression.
It's determined entirely by the syntax of the expression, and the
defined syntax doesn't include parenthesis around a non-integral
expression. (0) is allowed, because it is an integral constant
expression with a value of 0. ((void*)0) is not allowed, because while
it contains a sub-expression which is an integral constant expression
converted to void*, the expression as a whole does not match that
description.

> appears to imply it [i could find it in a c99 draft given time, but my
> c89 draft is in plaintext - if someone wants to assert that this changed
> in c99 i want proof because it would be surprising.]
>
> Although there is a question of what exactly "nullpointerconstant"ness
> is - is it a value, or is it something more 'zen'?

Being a null pointer constant is determined entirely by the syntax of
an expression, not it's type and value.

Tim Rentsch

unread,
Nov 17, 2005, 10:27:19 AM11/17/05
to
"Jun Woong" <wo...@icu.ac.kr> writes:

> Tim Rentsch wrote:
> > "Jun Woong" <wo...@icu.ac.kr> writes:
> >
> > >
> > > On the other hand, there was another discussion here in csc, in which
> > > the conclusion was that the definition given in the standard is
> > > intended to be exhaustive,
> >
> > Do you have a reference?
>
> The title of the discussion has "NULL", IIRC. And I was one of the
> participants. Using this information google can bring it, I believe.

This response doesn't adequately address the question. A Google
Groups search in {comp.std.c,comp.lang.c} with these parameters
yielded several thousand messages in over 100 threads. I would
be left to guess which thread you meant (and of course there is
no guarantee that the thread you're thinking of is even one of
the threads in the list).

So let me ask again: do you have a reference? If you aren't
willing or able to track it down yourself, the answer is "no".


> > I did a search using Google Groups and
> > didn't find any such thread. If you're talking about the thread
> > that transpired in September of 2005, I would say it's a
> > mischaracterization to say the _conclusion_ was that the
> > definition is supposed to be exhaustive. There was _discussion_
> > about whether the definition in 6.3.2.3 p3 is supposed to be
> > exhaustive, but not a conclusion.
>
> Unfortunately, I don't have time to read the entire thread. But
> casting a glance at it, I feel it is repetition of the past discussion
> which I gave the reference to.

This statement provides essentially no information. You think
the threads are similar - that doesn't tell me anything.


> > I'm not aware of any reliably authoritative statement that says
> > definitions in the Standard are meant to be exhaustive.
> >
>
> Clause 3:
>
> For the purposes of this International Standard, the following
> definitions apply. Other terms are defined where they appear in
> italic type or on the left side of a syntax rule. Terms explicitly
> defined in this International Standard are not to be presumed to
> refer implicitly to similar terms defined elsewhere.

There is nothing in the cited paragraph that says anything about
definitions being exhaustive.


> [...]
> >
> > Even if the definition of null pointer constant in 6.3.2.3 p3 is
> > intended to be exhaustive as far as the Standard uses the term,
> > nothing prevents an implementation from defining an extension
> > under section 4 p6 that provides additional forms of null
> > pointer constants. Or, if you prefer, the extension could
> > define a term "null pointer constantoid", and define certain
> > expressional forms that are "null pointer constantoids", and
> > stipulate that "null pointer constantoids" behave just like null
> > pointer constants in any context that a null pointer constant is
> > legal, and otherwise exhibit no unusual behavior.
> >
>
> I've already said, in other branch of this thread, that I also think
> it is possible for an implementation to define "null pointer
> constantoids" which behaves exactly like the null pointer constant.
> But it's a separate issue whether an implementation is allowed to
> define NULL as the "null pointer constantoids."

An implementation is allowed to define NULL as a "null pointer
constantoid" under the as-if rule. As long as NULL behaves
like a null pointer constant, it doesn't have to really be one.


> > > Suppose an implementation which #defines NULL as:
> > >
> > > #define NULL ((void *)0.0)
> > >
> > > and do not forget that a s.c. program can stringize NULL and check
> > > at least whether it is equal to "((void *)0.0)".
> >
> > The same implementation that provides an extension for a
> > different null pointer constant could also provide an extension
> > that stringizes its own NULL as "0". :)
>
> Do you really think so? Please think about what the # operator is
> supposed to do. The # operator applies to the expansion of the NULL
> macro, not directly to the NULL macro. What you are talking about is
> an extension with which the expension of NULL depends on the context.

Yes, that's what I'm talking about. An implementation is allowed
to define extensions _and_ provide system headers through black
magic if it wants to. As long as the identifiers in the headers
behave as the Standard says they should, any implementation
trickery in defining them is perfectly permissible. What's
needed here doesn't require that much imagination even:

#pragma string_expansion_for NULL 0
#define NULL ((void*)0.0)

Assuming the obvious meaning for the #pragma, NULL would
macro expand as one thing and stringize as another. That
isn't too complicated, is it?


> > More seriously, a strictly conforming program can't depend on
> > what NULL is defined as, since NULL is implementation defined,
> > and strictly conforming programs can't depend on implementation
> > defined behavior.
> >
>
> Please find in the past discussion a small "s.c." program whose output
> does (and should) not change in every conforming implementation. Its
> output differs only in *a* broken implementation which defines NULL as
> its own "null pointer constantoids."

If you have something that you think is relevant for me to
read, include it directly in a response so there isn't any
question what you're talking about. I'm not going to go
looking and guessing whether one of the messages I find
might be what you're talking about.


> > Incidentally, the most common definition for NULL seems to be
> > '((void*)0)'.
>
> Nope!
>
> #include <stdio.h> // which contains #define NULL ((void *))
>
> #define void int // the standard allows this
>
> void main()
> {
> double *p = NULL; // will cause problems
>
> return 0;
> }
>
> My and most implementations use a reserved identifier like "__void" in
> the place of "void" when defining NULL whether they, not me, know this
> or not. I really hope the committee put a new constraint into the next
> revision of the standard to prevent users from #defining keywords,
> which would be very helpful to an implementor especially when he/she
> designs masking macros for the real functions.

You haven't presented any evidence to support your claim that
most implementations use a reserved identifier. Of course,
I didn't support any evidence for my claim either, but my
claim is neither as strong nor as unequivocal as yours.

Furthermore, regardless of what definition of NULL is most
common, I think most readers in comp.std.c would agree
that '((void*)0)' is _one_ of the common definitions of NULL.
There certainly is evidence that some significant implementations
consider '((void*)0)' to be an acceptable definition for NULL
even though it isn't one of the forms of null pointer constant
identified in the Standard.

Tim Rentsch

unread,
Nov 17, 2005, 10:41:08 AM11/17/05
to
"=?utf-8?B?SGFyYWxkIHZhbiBExLNr?=" <tru...@gmail.com> writes:

I think you have a fundamental misconception about what
kind of thing the term "null pointer constant" refers to.
A null pointer constant isn't a _value_; rather, a null
pointer constant is _any of a set of syntactic forms_ that
are identified by the Standard as allowable in certain
contexts. The expressions

0
0L
(void*) 0
(char) 0

all qualify as null pointer constants, but that doesn't say
anything about their representations as expressions. An
implementation is obliged to _produce_ a null pointer of
the appropriate type if one of these syntactic forms is
used where a null pointer constant is required, but these
expressions do not in and of themselves evaluate to null
pointers (with the obvious exception that (void*) 0
does evaluate to a null pointer).

Tim Rentsch

unread,
Nov 17, 2005, 10:46:52 AM11/17/05
to
Jordan Abel <jma...@purdue.edu> writes:

> On 2005-11-17, Tim Rentsch <t...@alumnus.caltech.edu> wrote:
> > Incidentally, it's possible to meet the requirements of 7.1.2 p5
> > without having to resort to using ((void*)0). An implementation
> > can always arrange for NULL to be defined as 0, or (0).
>
> Or, as suggested on clc last month, it could define it as (void *)0 and
> use other tricks to make the precedence work [say, #pragma objectmacro
> NULL] thus NULL would act as an object macro yet #NULL would be "(void
> *)0" - but that would be evil.

My point was that it's possible to define NULL as a regular
null pointer constant without using any extensions or #pragma
tricks, yet still meet the requirements of 7.1.2 p5.

Harald van Dijk

unread,
Nov 17, 2005, 11:25:32 AM11/17/05
to
Tim Rentsch wrote:
> > Do you mean an implentation might legitimately define NULL as, for
> > example,
> >
> > #define NULL ((void *) 0.0)
> > #define #NULL "0L"
> >
> > -- assuming such a preprocessor extension exists, ((void *) 0.0) is a
> > null pointer constant, and the difference between 0L and ((void *) 0.0)
> > can't otherwise be detected (for example via sizeof)?
>
> I think you have a fundamental misconception about what
> kind of thing the term "null pointer constant" refers to.

I explained my reasoning better in another message, sorry for the
confusion. If I was truly not aware of what a null pointer constant is,
I'm sorry to say I'm still not, because your explanation does not
disagree with my understanding of it.

As I said in that other message:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR(x) STR_(x)
#define STR_(x) #x
int main() {
if(strcmp(STR(NULL), "0L") == 0)
printf("%ld\n", NULL);
else
printf("%ld\n", 0L);

}

I believed (I'm not so sure now because of other messages) this is
required to print "0\n", even with this twisted definition of NULL, but
it may not if 0L (without implicit conversions) and ((void *) 0.0)
(again, without implicit conversions) do not have the same size and
representation. A similar example can be given with sizeof:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR(x) STR_(x)
#define STR_(x) #x
int main() {
if(strcmp(STR(NULL), "0L") == 0)

printf("%d\n", (int) (sizeof(NULL) - sizeof(0L)));
else
printf("%d\n", 0);

}

Given these examples, do you believe my requirements on size and
representation are correct, do you believe these programs are not
required to print "0\n", or am I missing something else?

kuy...@wizard.net

unread,
Nov 17, 2005, 11:36:43 AM11/17/05
to
Tim Rentsch wrote:
> "Jun Woong" <wo...@icu.ac.kr> writes:
>
> > Tim Rentsch wrote:
> > > "Jun Woong" <wo...@icu.ac.kr> writes:
> > >
> > > >
> > > > On the other hand, there was another discussion here in csc, in which
> > > > the conclusion was that the definition given in the standard is
> > > > intended to be exhaustive,
> > >
> > > Do you have a reference?
> >
> > The title of the discussion has "NULL", IIRC. And I was one of the
> > participants. Using this information google can bring it, I believe.
>
> This response doesn't adequately address the question. A Google
> Groups search in {comp.std.c,comp.lang.c} with these parameters
> yielded several thousand messages in over 100 threads. I would
> be left to guess which thread you meant (and of course there is
> no guarantee that the thread you're thinking of is even one of
> the threads in the list).

He specified csc, not clc. I found only 6 threads on csc with Woong as
an author, and "NULL" in the title. I found only four threads on csc
with "NULL" in the title that had messages containing the word
"exhaustive". The thread we're in right now was the only overlap I
found between the two lists of threads.

Jun Woong

unread,
Nov 17, 2005, 11:46:41 AM11/17/05
to
"Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message news:kfn7jb7...@alumnus.caltech.edu...

> "Jun Woong" <wo...@icu.ac.kr> writes:
> >
> > The title of the discussion has "NULL", IIRC. And I was one of the
> > participants. Using this information google can bring it, I believe.
>
> This response doesn't adequately address the question. A Google
> Groups search in {comp.std.c,comp.lang.c} with these parameters
> yielded several thousand messages in over 100 threads. I would
> be left to guess which thread you meant (and of course there is
> no guarantee that the thread you're thinking of is even one of
> the threads in the list).
>
> So let me ask again: do you have a reference? If you aren't
> willing or able to track it down yourself, the answer is "no".
>

Google puts the thread on the top (or the second because of this
thread) when you search csc using "NULL" as the keyword "NULL" and my
name as the author:

http://groups.google.co.kr/group/comp.std.c/browse_frm/thread/b5bd31f5098c5b48

You can see me there have argued exactly the same as you.

> >
> > Clause 3:
> >
> > For the purposes of this International Standard, the following
> > definitions apply. Other terms are defined where they appear in
> > italic type or on the left side of a syntax rule. Terms explicitly
> > defined in this International Standard are not to be presumed to
> > refer implicitly to similar terms defined elsewhere.
>
> There is nothing in the cited paragraph that says anything about
> definitions being exhaustive.
>

The null pointer constant is written in italic type to indicate that
it is the definition for the term, "null pointer constant," and also
that it is NOT to be presumed to refer implicitly to similar terms
defined elsewhere *including* an implementation, which means it should
be exhaustive.

> >
> > I've already said, in other branch of this thread, that I also think
> > it is possible for an implementation to define "null pointer
> > constantoids" which behaves exactly like the null pointer constant.
> > But it's a separate issue whether an implementation is allowed to
> > define NULL as the "null pointer constantoids."
>
> An implementation is allowed to define NULL as a "null pointer
> constantoid" under the as-if rule. As long as NULL behaves
> like a null pointer constant, it doesn't have to really be one.
>

Only when an implementation can provide another definition for the
term explicitly defined in the standard. Now it's time to re-read
what the clause 3 says.

> >
> > Do you really think so? Please think about what the # operator is
> > supposed to do. The # operator applies to the expansion of the NULL
> > macro, not directly to the NULL macro. What you are talking about is
> > an extension with which the expension of NULL depends on the context.
>
> Yes, that's what I'm talking about. An implementation is allowed
> to define extensions _and_ provide system headers through black
> magic if it wants to. As long as the identifiers in the headers
> behave as the Standard says they should, any implementation
> trickery in defining them is perfectly permissible. What's
> needed here doesn't require that much imagination even:
>
> #pragma string_expansion_for NULL 0
> #define NULL ((void*)0.0)
>
> Assuming the obvious meaning for the #pragma, NULL would
> macro expand as one thing and stringize as another. That
> isn't too complicated, is it?
>
>

With extensions like magic, contriving a program to check conformance
of an implementation has limitation per se. An implementation is
required to document what it chooses for NULL anyway. Should it
document "((void *)0.0)" or "0"? If it documents ((void *)0.0), then
it certainly does not meet the null pointer constant's definition,
which makes the implementation non-conforming; 0.0 is never an integer
constant. If it documents 0 and a user can see into the header and
find the implementation a liar then it is simply not conforming again.

[...]


> > > Incidentally, the most common definition for NULL seems to be
> > > '((void*)0)'.
> >
> > Nope!
> >
> > #include <stdio.h> // which contains #define NULL ((void *))
> >
> > #define void int // the standard allows this
> >
> > void main()
> > {
> > double *p = NULL; // will cause problems
> >
> > return 0;
> > }
> >
> > My and most implementations use a reserved identifier like "__void" in
> > the place of "void" when defining NULL whether they, not me, know this
> > or not. I really hope the committee put a new constraint into the next
> > revision of the standard to prevent users from #defining keywords,
> > which would be very helpful to an implementor especially when he/she
> > designs masking macros for the real functions.
>
> You haven't presented any evidence to support your claim that
> most implementations use a reserved identifier.

Because an implementation which uses "void" in its definition for NULL
would be simply broken. Do you have any implementation (which claims
to be conforming) where NULL is defined using the keyword "void," not
a reserved identifier like "__void?" Then write a bug report to state
that the implementation is in fact not conforming.

>
> Furthermore, regardless of what definition of NULL is most
> common, I think most readers in comp.std.c would agree
> that '((void*)0)' is _one_ of the common definitions of NULL.

From the viewpoint of the users, not of the implementors. It's not
that I'm arguing you should have said ((__void *)0) instead of
((void *)0) here. What I'm talking about is that in practice an
implementation should consider masking keywords with macros when
implementing the standard library.

> There certainly is evidence that some significant implementations
> consider '((void*)0)' to be an acceptable definition for NULL
> even though it isn't one of the forms of null pointer constant
> identified in the Standard.

Reading the standard literally ignoring the intention can hurt your
brain. ;-)

Keith Thompson

unread,
Nov 17, 2005, 3:02:46 PM11/17/05
to

Sure, but the definition of the NULL macro isn't the whole problem.
The issue is that this is a flaw in the definition of a null pointer
constant.

((void*)0) is an expression of type void* whose value is a null
pointer, but taking the definition literally, it's not a null pointer
constant, so it can't be used to initialize a function pointer. More
precisely, an implementation can probably define ((void*)0) as a null
pointer constant as a language extension, but an implementation that
doesn't choose to do so could legally reject the following declaration:

void (*funcptr)(void) = ((void*)0);

If ((void*)0) *is* a null pointer constant, then an implementation is
not allowed to reject the declaration (though of course it can issue
whatever diagnostics it likes).

6.5.1p5 says that a parenthesized expression inherits many of the
attributes of the unparenthesized expression:
type
value
whether it's an lvalue
whether it's a function designator
whether it's a void expression

Common sense tells me that the intent was for it to inherit *all*
relevant attributes, and they just didn't think to add
whether it's a null pointer constant
to the list.

I can't think of any reason the authors of the standard would *want*
to make (void*)0 a null pointer constant, but not make ((void*)0) a
null pointer constant.

(Insert my standard daydream about a "nil" keyword and a time machine.)

Keith Thompson

unread,
Nov 17, 2005, 3:16:01 PM11/17/05
to

Maybe.

Consider the following incomplete program:

#include <stdio.h>
#include <stddef.h>

#define str(x) # x
#define xstr(x) str(x)

static int looks_like_an_npc(const char *expr)
{
/* ... */
}

int main(void)
{
const char *null_definition = xstr(NULL);
if (looks_like_an_npc(null_definition)) {
printf("ok\n");
}
else {
printf("oops\n");
}
return 0;
}

The intent is that the function looks_like_an_npc() returns true if
and only if its argument is a string representation of a C expression
that could be a valid null pointer constant; for example, it returns
true for "0", "(void*)0", and "__null__", and false for "42" and
"@_@".

There's no universal way to tell by looking at the string whether it's
an NPC; for example, given "enum { __null__ };" "__null__" is a valid
null pointer constant, but the function can't see the previous
declaration, so it has to err on the side of caution.

The function has to include a full C expression parser, so the
implementation is left as an exercise.

Assuming looks_like_an_npc() is implemented correctly, the program
will always print "ok" if run under a conforming implementation.

Well, almost. If an implementation is allowed to define other forms
of null pointer constant, the program could print "oops" if the
implementation uses one of those forms to define NULL.

So you're probably right, but only because of the general permission
to provide extensions.

Keith Thompson

unread,
Nov 17, 2005, 3:21:39 PM11/17/05
to
"Jun Woong" <wo...@icu.ac.kr> writes:
> "Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message
> news:kfnzmo4...@alumnus.caltech.edu...
> [...]
>>
>> There was discussion here in comp.std.c recently about whether
>> the Standard allows implementation defined null pointer constants.
>> I believe there was a general consensus by the end that they are,
>> under a blanket provision that implementations may define any
>> additional extensions they wish as long as it doesn't change the
>> behavior of any strictly conforming program. Under that provision
>> (it was 4p6 IIRC), gcc is free to define '(int)(void*)0' as a null
>> pointer constant.
>

> On the other hand, there was another discussion here in csc, in which
> the conclusion was that the definition given in the standard is
> intended to be exhaustive, so a conforming implementation is not free

> to provide any implementation-defined form of the null pointer
> constant (NPC) that is different from the definition the standard
> provides.

My recollection is that Doug Gwyn in particular has never accepted my
contention that all definitions in the standard *should* be
exhaustive, and should be corrected if they're not.

Keith Thompson

unread,
Nov 17, 2005, 3:31:45 PM11/17/05
to
"Jun Woong" <wo...@icu.ac.kr> writes:
[...]

> Please find in the past discussion a small "s.c." program whose output
> does (and should) not change in every conforming implementation. Its
> output differs only in *a* broken implementation which defines NULL as
> its own "null pointer constantoids."
[...]

Such a program is strictly conforming if and only if an implementation
is not allowed to a "null pointer constantoid" as the definition of
NULL.

This does raise a mildly interesting point: it's possible for a
program (though perhaps not a strictly conforming one) to detect
whether the implementation uses a "null pointer constantoid" as the
definition of NULL. There are three cases that can be distinguished:

NULL is defined as something that's definitely a valid null
pointer constant, such as 0;

NULL is defined as something that may or may not be a valid null
pointer constant, depending on previous declarations, such as
__null__; or

NULL is defined as something that definitely isn't one of the
valid forms of null pointer constant specified in the standard,
such as $NULL or 42.

I *think* that all three are allowed. The standard explicitly says
that an implementation may accept other forms of constant expressions
(6.6p10) and that a conforming implementation may have extensions that
don't alter the behavior of any strictly conforming program (4p6). I
believe this means that a conforming implementation may define other
forms of null pointer constants, and therefore that it may define NULL
as one of those other forms; a program whose output depends on this
implementation-defined behavior is not strictly conforming.

Keith Thompson

unread,
Nov 17, 2005, 3:36:25 PM11/17/05
to
kuy...@wizard.net writes:
[...]

> On 2001-02-17 Clive D. W. Feather wrote:
>> In article <3A8DB0BE...@arl.army.mil>, Douglas A. Gwyn
>> <gw...@arl.army.mil> writes
>> >> > > > ... there was a strong consensus that strict program conformance
>> >> > > > is not affected by what the standard macros expand to.
>>
>> >It ought to be in 7.1.2 Standard headers, but I don't see it.
>> >I'm pretty sure we responded to at least one DR along those lines.
>>
>> DR044 question 2.
>>
>> | A conforming implementation need not provide strictly conforming
>> | expansion of macros defined by the standard headers.

And I just wasted some time by posting before reading that. Oh, well.

On the other hand, DR044 refers to the C90 standard, submitted in 1992
<http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_044.html>.
Shouldn't there be an explicit statement to that effect in the C99
standard?

Tim Rentsch

unread,
Nov 17, 2005, 3:53:59 PM11/17/05
to
kuy...@wizard.net writes:

> Tim Rentsch wrote:
> > "Jun Woong" <wo...@icu.ac.kr> writes:
> >
> > > Tim Rentsch wrote:
> > > > "Jun Woong" <wo...@icu.ac.kr> writes:
> > > >
> > > > >
> > > > > On the other hand, there was another discussion here in csc, in which
> > > > > the conclusion was that the definition given in the standard is
> > > > > intended to be exhaustive,
> > > >
> > > > Do you have a reference?
> > >
> > > The title of the discussion has "NULL", IIRC. And I was one of the
> > > participants. Using this information google can bring it, I believe.
> >
> > This response doesn't adequately address the question. A Google
> > Groups search in {comp.std.c,comp.lang.c} with these parameters
> > yielded several thousand messages in over 100 threads. I would
> > be left to guess which thread you meant (and of course there is
> > no guarantee that the thread you're thinking of is even one of
> > the threads in the list).
>
> He specified csc, not clc.

Yes, now that you point it out I'm reminded that he did, in his
earlier posting. It wouldn't have helped much - as I did the
searching csc by itself had over 90 threads still with thousands of
messages. But you're right that the newsgroup information was there.


> I found only 6 threads on csc with Woong as
> an author, and "NULL" in the title. I found only four threads on csc
> with "NULL" in the title that had messages containing the word
> "exhaustive". The thread we're in right now was the only overlap I
> found between the two lists of threads.

Obviously you're better at using Google search features than I am.
With more effort I'm sure I could have narrowed the search down more,
but there wasn't much incentive since there was no guarantee that the
thread in question would be there, and no way of knowing which thread
he meant if it were. I think I made a reasonable, good faith effort
given his response.

Tim Rentsch

unread,
Nov 17, 2005, 4:09:58 PM11/17/05
to
Keith Thompson <ks...@mib.org> writes:

I don't think we're really in disagreement about about the sensibility
of making '((void*)0)' a Standardly-approved null pointer constant.
All I was trying to say is that the conditions of 7.1.2 p5 don't
require that it be one.


> (Insert my standard daydream about a "nil" keyword and a time machine.)

Sometime I'll have to ask you to explain why you think having
a nil keyword offers such a big advantage. Not that I agree
or disagree necessarily, I'm just not sure why you think it's
important.

Keith Thompson

unread,
Nov 17, 2005, 5:04:33 PM11/17/05
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:
> Keith Thompson <ks...@mib.org> writes:
[...]

>> (Insert my standard daydream about a "nil" keyword and a time machine.)
>
> Sometime I'll have to ask you to explain why you think having
> a nil keyword offers such a big advantage. Not that I agree
> or disagree necessarily, I'm just not sure why you think it's
> important.

The point is to have the nil keyword be the *only* accepted null
pointer constant (thus the need for the time machine). This would
largely eliminate the confusion between null pointers and pointers
that happen to be all-bits-zero, as well as making section 5 of the C
FAQ mostly unnecessary.

Adding a nil keyword without changing anything else wouldn't be
particularly useful.

Douglas A. Gwyn

unread,
Nov 17, 2005, 5:38:26 PM11/17/05
to
Jun Woong wrote:
> #define void int // the standard allows this

It wasn't meant to. The sentence in 6.4.1 reserving a set of
identifiers as keywords includes "and shall not be used
otherwise" (or words to that effect) which was meant to apply
to all phases of translation, even though the keywords are
only interpreted in the later phases of translation.

Keith Thompson

unread,
Nov 17, 2005, 7:04:57 PM11/17/05
to

The sentence in question is:

The above tokens (case sensitive) are reserved (in translation
phases 7 and 8) for use as keywords, and shall not be used
otherwise.

If the restriction is intended to apply to all phases of translation,
why is the phrase "(in translation phases 7 and 8)" there?

Also, 7.2.1p4, discussing standard headers, explicitly acknowledges
the possibility of defining a keyword as a macro:

The program shall not have any macros with names lexically
identical to keywords currently defined prior to the inclusion.

(a real-world case of "exceptio probat regulam in casibus non
exceptis").

There are cases where defining a keyword as a macro might be useful,
such as

#define restrict /* nothing */
#define inline /* nothing */

either for compatibility with pre-C99 compilers or to verify that a
program behaves properly with or without the keyword. (I'm not
arguing that it's a good idea.)

I wouldn't mind if such a restriction were in the language, but it
appears that it isn't, and adding it would break existing code.

Jun Woong

unread,
Nov 18, 2005, 5:56:27 AM11/18/05
to
"Keith Thompson" <ks...@mib.org> wrote in message news:lniruqd...@nuthaus.mib.org...

> "Douglas A. Gwyn" <DAG...@null.net> writes:
[...]

> >
> > It wasn't meant to. The sentence in 6.4.1 reserving a set of
> > identifiers as keywords includes "and shall not be used
> > otherwise" (or words to that effect) which was meant to apply
> > to all phases of translation, even though the keywords are
> > only interpreted in the later phases of translation.
>
> The sentence in question is:
>
> The above tokens (case sensitive) are reserved (in translation
> phases 7 and 8) for use as keywords, and shall not be used
> otherwise.
>
> If the restriction is intended to apply to all phases of translation,
> why is the phrase "(in translation phases 7 and 8)" there?
>
> Also, 7.2.1p4, discussing standard headers, explicitly acknowledges
> the possibility of defining a keyword as a macro:
>
> The program shall not have any macros with names lexically
> identical to keywords currently defined prior to the inclusion.
>
> (a real-world case of "exceptio probat regulam in casibus non
> exceptis").
>

It is certain that the current wording of the standard allows users
to mask keywords by #defining them as macros after every standard
header #included.

> There are cases where defining a keyword as a macro might be useful,
> such as
>
> #define restrict /* nothing */
> #define inline /* nothing */
>
> either for compatibility with pre-C99 compilers or to verify that a
> program behaves properly with or without the keyword. (I'm not
> arguing that it's a good idea.)
>

C99 Rationale (draft) also shows the similar examples with stating
possibility of abuse that is masking keywords *before* #including the
standard headers.

I remember when we discussed the issue about the <ctype.h> functions'
masking macros, Larry brought a solution which is useful only when
the standard forbids masking keywords with macros. At that time, some
including me agreed that the next revision of the standard should.
Masking keywords with macros, even if useful, needs an extreme care
anyway. I believe that forbidding it does not lose much; some exsiting
codes which do that would be able to run with no big problem in
the "undefined" mode.

Jun Woong

unread,
Nov 18, 2005, 6:35:14 AM11/18/05
to

"Keith Thompson" <ks...@mib.org> wrote in message news:lnzmo3d...@nuthaus.mib.org...

> "Jun Woong" <wo...@icu.ac.kr> writes:
> > Please find in the past discussion a small "s.c." program whose output
> > does (and should) not change in every conforming implementation. Its
> > output differs only in *a* broken implementation which defines NULL as
> > its own "null pointer constantoids."
>
> Such a program is strictly conforming if and only if an implementation
> is not allowed to a "null pointer constantoid" as the definition of
> NULL.

It is. The clause 3 explicitly says that every term the standard
defines is intended to be exclusive, and that nobody is allowed to
extend it.

[...]


>
> NULL is defined as something that's definitely a valid null
> pointer constant, such as 0;
>

No problem.

> NULL is defined as something that may or may not be a valid null
> pointer constant, depending on previous declarations, such as
> __null__; or

Allowed only if __null__ is an integer constant whose value is 0
and ...

>
> NULL is defined as something that definitely isn't one of the
> valid forms of null pointer constant specified in the standard,
> such as $NULL or 42.
>

Allowed only if $NULL is an integer constant whose value is 0 and ...
($NULL may be a valid identifier under a conforming implementation).
42 is never an integer constant whose value is 0, so it's not allowed.

We need to note that the standard doesn't need to provide the NULL
macro at all; we already have a perfectly legal and portable way to
write the null pointer constant in our codes. I think that NULL is
provided primarily for readability and compatibility (with an existing
code), *not* for its variety in implementations. This means that NULL
is very different than, say, the offsetof macro.

> I *think* that all three are allowed. The standard explicitly says
> that an implementation may accept other forms of constant expressions
> (6.6p10)

The wording in question should have been narrowed to reflect the
reason for its existence. Frankly speaking, I'm not very sure whether
or not the wording can apply here. But considering its genuine purpose
I don't think it is intended to apply to the NULL definition issue. As
opposed to offsetof, NULL already has the perfectly portable form
which every conforming implementation can take advantage of.

> and that a conforming implementation may have extensions that
> don't alter the behavior of any strictly conforming program (4p6).

Which doesn't mean that an implementation may extend the definition
the standard provides.


Tim Rentsch

unread,
Nov 18, 2005, 8:13:57 AM11/18/05
to
"Jun Woong" <wo...@icu.ac.kr> writes:

> "Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message news:kfn7jb7...@alumnus.caltech.edu...
> > "Jun Woong" <wo...@icu.ac.kr> writes:
> > >
> > > The title of the discussion has "NULL", IIRC. And I was one of the
> > > participants. Using this information google can bring it, I believe.
> >
> > This response doesn't adequately address the question. A Google
> > Groups search in {comp.std.c,comp.lang.c} with these parameters
> > yielded several thousand messages in over 100 threads. I would
> > be left to guess which thread you meant (and of course there is
> > no guarantee that the thread you're thinking of is even one of
> > the threads in the list).
> >
> > So let me ask again: do you have a reference? If you aren't
> > willing or able to track it down yourself, the answer is "no".
> >
>
> Google puts the thread on the top (or the second because of this
> thread) when you search csc using "NULL" as the keyword "NULL" and my
> name as the author:
>
> http://groups.google.co.kr/group/comp.std.c/browse_frm/thread/b5bd31f5098c5b48
>
> You can see me there have argued exactly the same as you.

The link allowed me to find a thread at Google Groups. The page
claimed 139 messages in the thread, but listed only 137 messages.
I read the 137 messages.


> > > Clause 3:
> > >
> > > For the purposes of this International Standard, the following
> > > definitions apply. Other terms are defined where they appear in
> > > italic type or on the left side of a syntax rule. Terms explicitly
> > > defined in this International Standard are not to be presumed to
> > > refer implicitly to similar terms defined elsewhere.
> >
> > There is nothing in the cited paragraph that says anything about
> > definitions being exhaustive.
> >
>
> The null pointer constant is written in italic type to indicate that
> it is the definition for the term, "null pointer constant," and also
> that it is NOT to be presumed to refer implicitly to similar terms
> defined elsewhere *including* an implementation, which means it should
> be exhaustive.

With all due respect to your language ability, I believe my
command of the English language is better than yours. The
sentence about "similar terms defined elsewhere" is talking about
documents that define terms in other contexts, not about
implementation-defined extensions. For starters, the terms
mentioned in a C implementation document aren't "similar terms
defined elsewhere", they are the very same terms as used in the
Standard.


> > > I've already said, in other branch of this thread, that I also think
> > > it is possible for an implementation to define "null pointer
> > > constantoids" which behaves exactly like the null pointer constant.
> > > But it's a separate issue whether an implementation is allowed to
> > > define NULL as the "null pointer constantoids."
> >
> > An implementation is allowed to define NULL as a "null pointer
> > constantoid" under the as-if rule. As long as NULL behaves
> > like a null pointer constant, it doesn't have to really be one.
> >
>
> Only when an implementation can provide another definition for the
> term explicitly defined in the standard. Now it's time to re-read
> what the clause 3 says.

First of all your comment is a non sequitur. The paragraph you're
responding to doesn't say anything about changing a definition of
a term defined in the Standard.

Second, the cited paragraph from section 3 isn't on point. The
question about whether NULL can be a "null pointer constantoid"
(that acts like a null pointer constant) doesn't have anything
to do with how the Standard indicates term definitions.

Third, nothing in the cited paragraph says anything about whether
definitions are supposed to be exhaustive, or whether definitions
may be extended by an implementation. If you think it does, then
you need to re-read it.


> > > Do you really think so? Please think about what the # operator is
> > > supposed to do. The # operator applies to the expansion of the NULL
> > > macro, not directly to the NULL macro. What you are talking about is
> > > an extension with which the expension of NULL depends on the context.
> >
> > Yes, that's what I'm talking about. An implementation is allowed
> > to define extensions _and_ provide system headers through black
> > magic if it wants to. As long as the identifiers in the headers
> > behave as the Standard says they should, any implementation
> > trickery in defining them is perfectly permissible. What's
> > needed here doesn't require that much imagination even:
> >
> > #pragma string_expansion_for NULL 0
> > #define NULL ((void*)0.0)
> >
> > Assuming the obvious meaning for the #pragma, NULL would
> > macro expand as one thing and stringize as another. That
> > isn't too complicated, is it?
> >
> >
>
> With extensions like magic, contriving a program to check conformance
> of an implementation has limitation per se. An implementation is
> required to document what it chooses for NULL anyway. Should it
> document "((void *)0.0)" or "0"? If it documents ((void *)0.0), then
> it certainly does not meet the null pointer constant's definition,
> which makes the implementation non-conforming; 0.0 is never an integer
> constant. If it documents 0 and a user can see into the header and
> find the implementation a liar then it is simply not conforming again.

Implementations are allowed all sorts of leeway with regard to
standard headers. For one thing, they don't have to be actual
files - the compiler can magically produce them whenever one is
#include'd. Also, headers can include #pragma's, which pretty
much means all bets are off when it comes to being able to look
"under the hood". Certainly an implementation could define NULL
by

#pragma null_pointer_constant NULL

and have no other program text about how NULL is defined, and
still be conforming.


> [...]
> > > > Incidentally, the most common definition for NULL seems to be
> > > > '((void*)0)'.
> > >
> > > Nope!
> > >
> > > #include <stdio.h> // which contains #define NULL ((void *))
> > >
> > > #define void int // the standard allows this
> > >
> > > void main()
> > > {
> > > double *p = NULL; // will cause problems
> > >
> > > return 0;
> > > }
> > >
> > > My and most implementations use a reserved identifier like "__void" in
> > > the place of "void" when defining NULL whether they, not me, know this
> > > or not. I really hope the committee put a new constraint into the next
> > > revision of the standard to prevent users from #defining keywords,
> > > which would be very helpful to an implementor especially when he/she
> > > designs masking macros for the real functions.
> >
> > You haven't presented any evidence to support your claim that
> > most implementations use a reserved identifier.
>
> Because an implementation which uses "void" in its definition for NULL
> would be simply broken. Do you have any implementation (which claims
> to be conforming) where NULL is defined using the keyword "void," not
> a reserved identifier like "__void?" Then write a bug report to state
> that the implementation is in fact not conforming.

Absurd. No sensible person thinks having 'void' in a definition
for NULL automatically makes an implementation non-conforming.

Incidentally, what I asked for was evidence. What you gave was
argument, not evidence.


> > Furthermore, regardless of what definition of NULL is most
> > common, I think most readers in comp.std.c would agree
> > that '((void*)0)' is _one_ of the common definitions of NULL.
>
> From the viewpoint of the users, not of the implementors. It's not
> that I'm arguing you should have said ((__void *)0) instead of
> ((void *)0) here. What I'm talking about is that in practice an
> implementation should consider masking keywords with macros when
> implementing the standard library.

Irrelevant. The question is about which definitions for NULL
are permissible, not about quality of implementations.


> > There certainly is evidence that some significant implementations
> > consider '((void*)0)' to be an acceptable definition for NULL
> > even though it isn't one of the forms of null pointer constant
> > identified in the Standard.
>
> Reading the standard literally ignoring the intention can hurt your
> brain. ;-)

Your comments are largely non-responsive. I don't know whether this
happens because of your difficulty with English or because it's just
your personal style. In either case I'm not particularly interested
in continuing a discussion where one side doesn't really engage in the
discussion but mostly just repeatedly asserts his position.

Tim Rentsch

unread,
Nov 18, 2005, 9:00:48 AM11/18/05
to
"=?utf-8?B?SGFyYWxkIHZhbiBExLNr?=" <tru...@gmail.com> writes:

Ok, I think I understand now what question you're trying to ask.

Suppose there were an implementation-defined extension saying
that the expression

((void*) 0.0)

behaves exactly like the expression '0L', in all respects,
_except_ that where ever it appears that doesn't "need" a null
pointer constant is flagged with a diagnostic. I believe such an
extension doesn't change any strictly conforming program, since
it can't appear in one.

Further suppose (using your suggested syntax), that #NULL and
NULL are defined thusly:

#define NULL ((void *) 0.0)
#define #NULL "0L"

(And the second #define works.)

I believe such an implementation, assuming it's conformant
otherwise, behaves just as a conformant implementation would.
There's an obvious exception here - ((void*)0.0) would normally
produce a diagnostic in a conformant implementation, and it
doesn't here (in those places where a null pointer constant is
needed). But except for that the two implementations would
be indistinguishable. In particular, the programs you gave
would give the same output in the hypothetical implementation
as in a conforming implementation, not counting possible
diagnostics in the hypothetical implementation.

It's my opinion that the language of the Standard allows this
hypothetical implementation to be deemed a conforming
implementation.

Does that answer your question?

Tim Rentsch

unread,
Nov 18, 2005, 9:14:27 AM11/18/05
to
Keith Thompson <ks...@mib.org> writes:

> Tim Rentsch <t...@alumnus.caltech.edu> writes:
> > Keith Thompson <ks...@mib.org> writes:
> [...]
> >> (Insert my standard daydream about a "nil" keyword and a time machine.)
> >
> > Sometime I'll have to ask you to explain why you think having
> > a nil keyword offers such a big advantage. Not that I agree
> > or disagree necessarily, I'm just not sure why you think it's
> > important.
>
> The point is to have the nil keyword be the *only* accepted null
> pointer constant (thus the need for the time machine). This would
> largely eliminate the confusion between null pointers and pointers
> that happen to be all-bits-zero, as well as making section 5 of the C
> FAQ mostly unnecessary.

I understand that you want 'nil' or whatever to be the only accepted
null pointer constant. What I was asking about is Why does that seem
so significant? I grant that a lot of people think null pointers have
an all zero representation, but why is that such a big deal? (To
be explicit, the question is not rhetorical.)

Incidentally, would you keep the ability to test pointers directly
in if's and while's, etc:

if(p)
if(!p)
...

Are these allowed or disallowed in your time-machined C?

Skarmander

unread,
Nov 18, 2005, 10:02:39 AM11/18/05
to
Tim Rentsch wrote:
> Keith Thompson <ks...@mib.org> writes:
>
>
>>Tim Rentsch <t...@alumnus.caltech.edu> writes:
>>
>>>Keith Thompson <ks...@mib.org> writes:
>>
>>[...]
>>
>>>>(Insert my standard daydream about a "nil" keyword and a time machine.)
>>>
>>>Sometime I'll have to ask you to explain why you think having
>>>a nil keyword offers such a big advantage. Not that I agree
>>>or disagree necessarily, I'm just not sure why you think it's
>>>important.
>>
>>The point is to have the nil keyword be the *only* accepted null
>>pointer constant (thus the need for the time machine). This would
>>largely eliminate the confusion between null pointers and pointers
>>that happen to be all-bits-zero, as well as making section 5 of the C
>>FAQ mostly unnecessary.
>
>
> I understand that you want 'nil' or whatever to be the only accepted
> null pointer constant. What I was asking about is Why does that seem
> so significant? I grant that a lot of people think null pointers have
> an all zero representation, but why is that such a big deal? (To
> be explicit, the question is not rhetorical.)
>
Though I'm sure Keith can talk for himself, this is big enough that I
want to chip in.

You *honestly* do not see why using "0" for null pointer constants is a
bad idea? I'm sure it all seems simple and logical for people who know
the standard, but here's the deal: the language isn't learned by reading
the standard.

Telling people that a null pointer constant looks like this:
0
and then telling them that it has nothing to do with the value
represented by this:
0
is a real mind trip.

It's not just that this is not intuitive. It's *pointless*. People can
learn this, but why should they even have to in the first place? This
may have made sense when null pointers really *were* all-bits-zero, but
the only reason for keeping it that way after this assumption was
dropped was (and is) the hysteria of the raisins.

> Incidentally, would you keep the ability to test pointers directly
> in if's and while's, etc:
>
> if(p)
> if(!p)
> ...
>
> Are these allowed or disallowed in your time-machined C?

I don't know about Keith, but if I had a time machine I would add 'bool'
to C, the semantics of 'if' would be redefined to test booleans only,
and == and != would return booleans. I would allow conversions of
booleans to integers (I'm not sure whether they should be implicit or
explicit), but disallow conversion of any type to boolean.

In short, I'd disallow *any* statement of the form
if (p)
where 'p' is not a boolean. That *would* mean extra keystrokes for some
popular cryptic C idioms. It's a price I'd be willing to pay, even in
retrospect, when I already know C well enough not to be bothered by
these things anymore (and gladly perpetuate the confusion myself).

S.

Harald van Dijk

unread,
Nov 18, 2005, 10:33:59 AM11/18/05
to

It does, thanks.

Wojtek Lerch

unread,
Nov 18, 2005, 10:55:41 AM11/18/05
to
"Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message
news:kfnmzk2...@alumnus.caltech.edu...

> I understand that you want 'nil' or whatever to be the only accepted
> null pointer constant. What I was asking about is Why does that seem
> so significant? I grant that a lot of people think null pointers have
> an all zero representation, but why is that such a big deal? (To
> be explicit, the question is not rhetorical.)

One thing that hasn't been mentioned here is that this would allow the
standard to forbid using a null pointer constant as an argument to a varargs
function (or to a function without a prototype).

(BTW I think (nil) and (((nil))) should be considered null pointer
constants, too.)


kuy...@wizard.net

unread,
Nov 18, 2005, 11:04:50 AM11/18/05
to
Tim Rentsch wrote:
...

> I understand that you want 'nil' or whatever to be the only accepted
> null pointer constant. What I was asking about is Why does that seem
> so significant? I grant that a lot of people think null pointers have
> an all zero representation, but why is that such a big deal? (To
> be explicit, the question is not rhetorical.)

The most important way in which nil would differ from an NPC is that it
would never be legal in a non-pointer context, with a corresponding
improvement in type safety.

Douglas A. Gwyn

unread,
Nov 18, 2005, 11:24:58 AM11/18/05
to
Keith Thompson wrote:
> Tim Rentsch <t...@alumnus.caltech.edu> writes:
> > Sometime I'll have to ask you to explain why you think having
> > a nil keyword offers such a big advantage.
> The point is to have the nil keyword be the *only* accepted null
> pointer constant (thus the need for the time machine). This would
> largely eliminate the confusion between null pointers and pointers
> that happen to be all-bits-zero, as well as making section 5 of the C
> FAQ mostly unnecessary.

Specifically, there is no good reason to exclude address 0
from the range of valid object pointer values, and on many
platforms it is desirable to allow that address. If the
in-band representation of "invalid pointer value" were
represented by a symbol, such as "nil", then implementations
would have the freedom to e.g. reserve the address of a
library object such as
extern char __nothing_here;
as its value to represent "nil". Actually the current C
standard allows such a representation, but it further
requires for such an implementation that pointers to
address 0 be artificially mapped to that address, merely
for historical compatibility.

Douglas A. Gwyn

unread,
Nov 18, 2005, 11:29:42 AM11/18/05
to
Keith Thompson wrote:
> If the restriction is intended to apply to all phases of translation,
> why is the phrase "(in translation phases 7 and 8)" there?

That's where the keywords have meaning. The parenthetical
is (in my opinion) misplaced.

> Also, 7.2.1p4, discussing standard headers, explicitly acknowledges
> the possibility of defining a keyword as a macro:
> The program shall not have any macros with names lexically
> identical to keywords currently defined prior to the inclusion.

Great; that addresses the immediate problem of defining NULL as
(void*)0 in the *standard* headers.

> There are cases where defining a keyword as a macro might be useful,
> such as
> #define restrict /* nothing */
> #define inline /* nothing */

Sure, you can do that (also #define void int etc.) but it is not
essential for strictly conforming programs.

Jun Woong

unread,
Nov 18, 2005, 12:19:34 PM11/18/05
to

"Tim Rentsch" <t...@alumnus.caltech.edu> wrote in message news:kfnsltu...@alumnus.caltech.edu...

> "Jun Woong" <wo...@icu.ac.kr> writes:
[...]
> >
> > The null pointer constant is written in italic type to indicate that
> > it is the definition for the term, "null pointer constant," and also
> > that it is NOT to be presumed to refer implicitly to similar terms
> > defined elsewhere *including* an implementation, which means it should
> > be exhaustive.
>
> With all due respect to your language ability, I believe my
> command of the English language is better than yours. The
> sentence about "similar terms defined elsewhere" is talking about
> documents that define terms in other contexts, not about
> implementation-defined extensions. For starters, the terms
> mentioned in a C implementation document aren't "similar terms
> defined elsewhere", they are the very same terms as used in the
> Standard.
>

I admit that the real purpose of that clause is what you said. It
prohibits a reader from bring a definition from another document.
But is it such a big jump of logic to regard extending the definitions
the standard defines as making them refer to similar terms defined
elsewhere?

As I recall, the commitee has answered (and the answer constitutes
the normative part of the standard) that at least one term defined
in the standard, "integer types," is intended to be exhaustive, not
making the reasoning ground clear. Then what makes it stop before the
"null pointer constant?"

>
[...]


>
> Implementations are allowed all sorts of leeway with regard to
> standard headers. For one thing, they don't have to be actual
> files - the compiler can magically produce them whenever one is
> #include'd. Also, headers can include #pragma's, which pretty
> much means all bets are off when it comes to being able to look
> "under the hood". Certainly an implementation could define NULL
> by
>
> #pragma null_pointer_constant NULL
>
> and have no other program text about how NULL is defined, and
> still be conforming.
>

Even if an implementation does it, it should be a macro (at least act
as a macro) and thus have its own spelling. The implementation is also
required to documented its choice and the document should show that it
is the null pointer constant, not other things. Of course, the
implementation should not be a liar, which could make the standard's
"implementation-defined" completely meaningless.

[...]


> >
> > Because an implementation which uses "void" in its definition for NULL
> > would be simply broken. Do you have any implementation (which claims
> > to be conforming) where NULL is defined using the keyword "void," not
> > a reserved identifier like "__void?" Then write a bug report to state
> > that the implementation is in fact not conforming.
>
> Absurd. No sensible person thinks having 'void' in a definition
> for NULL automatically makes an implementation non-conforming.
>

Read another branch of this thread where the conformance problem about
masking keyword with macros discussed. You seem to not understand what
the standard allows for s.c. programs.

> Incidentally, what I asked for was evidence. What you gave was
> argument, not evidence.
>

You are talking about the "most" I've said, aren't you? It is not a
statistical problem. It's about conformance. If we are talking about
a real and practical implementation, then every sensible person should
say that using "void" as opposed to "__void" in defining NULL makes
the implementation *automatically* non-conforming. I don't think about
your another magical extension here.

[...]


> >
> > From the viewpoint of the users, not of the implementors. It's not
> > that I'm arguing you should have said ((__void *)0) instead of
> > ((void *)0) here. What I'm talking about is that in practice an
> > implementation should consider masking keywords with macros when
> > implementing the standard library.
>
> Irrelevant. The question is about which definitions for NULL
> are permissible, not about quality of implementations.
>

I say again, it's not a QoI issue.

[...]


> >
> > Reading the standard literally ignoring the intention can hurt your
> > brain. ;-)
>
> Your comments are largely non-responsive. I don't know whether this
> happens because of your difficulty with English or because it's just
> your personal style. In either case I'm not particularly interested
> in continuing a discussion where one side doesn't really engage in the
> discussion but mostly just repeatedly asserts his position.

If you feel like I'm impolite about the response, I apologize it. What
I tried to say was that the committee's intention is very clear in
this case; it is to allow ((void *)0) to be the valid null pointer
constant. Therefore my point is that it is useless to keep saying
that the standard does not allow ((void *)0) to be the null pointer
constant. I also think that the wording in question should be fixed,
possibly through a DR or the next revision, but also think that it has
big significance right now.

Wojtek Lerch

unread,
Nov 18, 2005, 12:43:41 PM11/18/05
to
"Douglas A. Gwyn" <DAG...@null.net> wrote in message
news:437E0176...@null.net...

> Keith Thompson wrote:
>> Also, 7.2.1p4, discussing standard headers, explicitly acknowledges
>> the possibility of defining a keyword as a macro:
>> The program shall not have any macros with names lexically
>> identical to keywords currently defined prior to the inclusion.
>
> Great; that addresses the immediate problem of defining NULL as
> (void*)0 in the *standard* headers.

Does it?

#include <stdio.h>
#define void main
int void( int argc, char **argv ) {
return void == NULL;
}

Jun Woong

unread,
Nov 18, 2005, 1:08:01 PM11/18/05
to

Assuming that NULL is defined as ((void *)0), after preprocessing it
results in:

int main ( int argc, char * * argv ) {
return main == ( ( main * ) 0 ) ;

Wojtek Lerch

unread,
Nov 18, 2005, 1:27:34 PM11/18/05
to
"Jun Woong" <wo...@icu.ac.kr> wrote in message
news:1132337281.8...@o13g2000cwo.googlegroups.com...

> Wojtek Lerch wrote:
>>
>> #include <stdio.h>
>> #define void main
>> int void( int argc, char **argv ) {
>> return void == NULL;
>> }
>
> Assuming that NULL is defined as ((void *)0), after preprocessing it
> results in:
>
> int main ( int argc, char * * argv ) {
> return main == ( ( main * ) 0 ) ;
> }

Exactly. But does that mean that the implementation is non-conforming
because it defined NULL in a way that allowed a legitimate macro definition
in a program to break it, or does it mean that the program is not
strictly-conforming because it breaks a valid definition of NULL and then
attempts to use it?

In other words, when the standard says that NULL "expands to an
implementation-defined null pointer constant", does it mean that the
implementation must make sure that macro expansion turns NULL into a null
pointer constant in every correct program, even one that defines a macro
named "void" or "long", or does it merely mean that the replacement list of
the NULL macro must look like a null pointer constant, and if a program
breaks it by defining "void" or "long" as macros, then it's the program's
problem?


Skarmander

unread,
Nov 18, 2005, 2:41:33 PM11/18/05
to
Wojtek Lerch wrote:
<snip>

> In other words, when the standard says that NULL "expands to an
> implementation-defined null pointer constant", does it mean that the
> implementation must make sure that macro expansion turns NULL into a null
> pointer constant in every correct program, even one that defines a macro
> named "void" or "long", or does it merely mean that the replacement list of
> the NULL macro must look like a null pointer constant, and if a program
> breaks it by defining "void" or "long" as macros, then it's the program's
> problem?
>
When the standard says that a standard macro "expands" to something, it
is not reasonable to interpret this as if the phrase "no matter what
macro definitions could be in effect" were added, because this
effectively means standard macros are not allowed to use keywords in
their expansion, except where the standard explicitly defines them in
this way (for example, 'assert' with NDEBUG defined (7.2)).

This is undesirable for many reasons (one of which is that it
complicates writing portable implementations of the standard library),
and I do not believe this could be the intent. The standard does not
forbid defining keywords as macros, to serve certain special purposes,
but that doesn't mean it requires implementations to make sure no
standard macro can ever break by such a definition.

In short, if you break things this way, you get to keep the pieces. It's
not reasonable to demand they're unbreakable, or to attach this meaning
to the innocuous verb "expand".

S.

Keith Thompson

unread,
Nov 18, 2005, 3:11:03 PM11/18/05
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:
> Keith Thompson <ks...@mib.org> writes:
>
>> Tim Rentsch <t...@alumnus.caltech.edu> writes:
>> > Keith Thompson <ks...@mib.org> writes:
>> [...]
>> >> (Insert my standard daydream about a "nil" keyword and a time machine.)
>> >
>> > Sometime I'll have to ask you to explain why you think having
>> > a nil keyword offers such a big advantage. Not that I agree
>> > or disagree necessarily, I'm just not sure why you think it's
>> > important.
>>
>> The point is to have the nil keyword be the *only* accepted null
>> pointer constant (thus the need for the time machine). This would
>> largely eliminate the confusion between null pointers and pointers
>> that happen to be all-bits-zero, as well as making section 5 of the C
>> FAQ mostly unnecessary.
>
> I understand that you want 'nil' or whatever to be the only accepted
> null pointer constant. What I was asking about is Why does that seem
> so significant? I grant that a lot of people think null pointers have
> an all zero representation, but why is that such a big deal? (To
> be explicit, the question is not rhetorical.)

The purpose would be to avoid confusion and special-case rules.
You've seen how confusing it can be from this thread, from the myriad
of other threads in comp.{std,lang}.c, from the need for section 5 of
the FAQ, and so forth.

We have a rule that says 0 converted to a pointer type yields a
particular value, but an expression with the value 0 converted to a
pointer type doesn't necessarily yield that value; there are no other
cases I know of where the behavior of a conversion can depend on
whether the operand is a constant.

We have, because of a simple oversight, an standard that says (void*)0
is a null pointer constant, but ((void*)0) isn't -- but the
parentheses are required in the definition of an object-like macro.

I've seen programs that mis-use NULL as a character constant, such as
char s[10];
s[0] = 'x';
s[1] = NULL;
that compile without errors if NULL happens to be defined as 0, but
not if NULL is defined as (void*)0.

All these problems exist because pre-K&R C freely allowed implicit
conversions between integers and pointers, programmers used a literal
0 as a null pointer, and it was necessary to generalize the concept
without breaking existing code.

We've been working on the current model of null pointer constants for
decades, and we still haven't quite gotten it right.

All this could have been avoided by dropping the idea of null pointer
constants and replacing it with a "nil" keyword. (You could then have
"#define NULL nil" if you want, but there's no need for it.)

> Incidentally, would you keep the ability to test pointers directly
> in if's and while's, etc:
>
> if(p)
> if(!p)
> ...
>
> Are these allowed or disallowed in your time-machined C?

Now that's a good question. I think they'd have to be disallowed.

The current rule for expressions is simple: "if (expr)" is equivalent
to "if ((expr) != 0)". If 0 couldn't be converted to a pointer type,
then "ptr != 0" wouldn't be a valid expression, so "if (ptr != 0)"
would be invalid, so "if (ptr)" would be invalid. The same argument
applies to "if (!ptr)".

Personally, I don't think that would be a great loss. I find
"if (ptr != nil)" clearer anyway (in real C, I use "if (ptr != NULL)".
Not being able to use a pointer value as a condition is no worse, in
my opinion, than not being able to use a struct value.

If that's too painful, you could always define a special rule to make
"if (ptr)" equivalent to "if (ptr != nil)".

For that matter, I wouldn't mind having a special built-in "bool"
type, and requiring it in all expressions, so "if (n)" would have to
be written as "if (n != 0)" if n is an int. I know a lot of C
programmers would hate this idea -- and yes, if I want Pascal I know
where to find it.

Of course, none of these changes are remotely practical for any future
language with the name "C", barring the above-mentioned time machine.

Jordan Abel

unread,
Nov 18, 2005, 3:15:26 PM11/18/05
to
On 2005-11-18, Skarmander <inv...@dontmailme.com> wrote:
> Wojtek Lerch wrote:
> <snip>
>> In other words, when the standard says that NULL "expands to an
>> implementation-defined null pointer constant", does it mean that the
>> implementation must make sure that macro expansion turns NULL into a null
>> pointer constant in every correct program, even one that defines a macro
>> named "void" or "long", or does it merely mean that the replacement list of
>> the NULL macro must look like a null pointer constant, and if a program
>> breaks it by defining "void" or "long" as macros, then it's the program's
>> problem?
>>
> When the standard says that a standard macro "expands" to something, it
> is not reasonable to interpret this as if the phrase "no matter what
> macro definitions could be in effect" were added, because this
> effectively means standard macros are not allowed to use keywords in
> their expansion, except where the standard explicitly defines them in
> this way (for example, 'assert' with NDEBUG defined (7.2)).
>
> This is undesirable for many reasons (one of which is that it
> complicates writing portable implementations of the standard library),

Out of curiosity, how is such a thing possible?

Seriously. How can you have a portable implementation of malloc? or
fputc? [yeah, you could have fputc call fwrite, but, you can't have
turtles all the way down]

Keith Thompson

unread,
Nov 18, 2005, 3:26:06 PM11/18/05
to

I just thought of a possible glitch. It shouldn't be too hard to work
around it, but I'm not quite sure how.

What is the type of nil? The most obvious answer is void*, but
there's no implicit conversion from void* to pointer-to-function.

Every expression in C has a type independent of its context; even 0 is
inherently of type int, and is converted to a pointer type only in an
appropriate context. I suppose we could have an implicit conversion
from void* that happens for nil but not for a non-constant expression
with the same type and value; that's ugly, but it may be the least
ugly solution. Other languages make nil have whatever pointer type is
imposed by the context, but that seems un-C-like.

We'd also have to fix the standard's wording about initializing static
objects, so pointers are initialized to nil rather than zero.

(And again, this is all completely impractical unless you're designing
a new language without concern for breaking any existing code. I'm
adding this disclaimer in case someone jumps into the middle of this
discussion and thinks I'm proposing an actual change to the language.)

Jordan Abel

unread,
Nov 18, 2005, 3:27:35 PM11/18/05
to

I don't think it says that. It gives a verbal description, which _you_
tihnk only applies to (void*)0, and which _i_ think also applies to
((void*)0). and (((void *)(000L))). and ((void *)'\0') - there may also
be an argument that ((void *)(*"")) is.

>> if(p)
>> if(!p)
>> ...
>>
>> Are these allowed or disallowed in your time-machined C?
>
> Now that's a good question. I think they'd have to be disallowed.

why? Just allow conditionals to contain a pointer, which is tested for
"nil"-ness.

> The current rule for expressions is simple: "if (expr)" is equivalent
> to "if ((expr) != 0)". If 0 couldn't be converted to a pointer type,
> then "ptr != 0" wouldn't be a valid expression, so "if (ptr != 0)"
> would be invalid, so "if (ptr)" would be invalid. The same argument
> applies to "if (!ptr)".

Or you could define a ! operator for pointers, and make if(expr)
equivalent to if(!!expr) rather than if(0 != expr)

Skarmander

unread,
Nov 18, 2005, 3:41:53 PM11/18/05
to
Jordan Abel wrote:
<snip>
[redefining keywords]

>>This is undesirable for many reasons (one of which is that it
>>complicates writing portable implementations of the standard library),
>
>
> Out of curiosity, how is such a thing possible?
>
> Seriously. How can you have a portable implementation of malloc? or
> fputc? [yeah, you could have fputc call fwrite, but, you can't have
> turtles all the way down]

Oh, here we go again. I should stop using the word "portable" -- "I do
not think it means what I think it means", to paraphrase a popular flick...

I obviously don't mean "standard library written in 100% standard C";
that's impossible, since the library must provide some operations and
types that cannot be defined in terms of standard C minus the standard
library.

I typically don't use "portable" as a yes or no property (I probably
should start doing that since everybody else is). I use even this sense
of "portable" more literally as "thing that can be carried to different
places with little difficulty", and as such it has degrees.

Formulating it properly, then: "it complicates writing those portions of
the standard library in 100% standard C that can be written in 100%
standard C, and those portions written in non-standard C that rely on
certain fundamental subsets of standard C". I think that covers it.

S.

Skarmander

unread,
Nov 18, 2005, 4:09:12 PM11/18/05
to
Keith Thompson wrote:
<snip>

> For that matter, I wouldn't mind having a special built-in "bool"
> type, and requiring it in all expressions, so "if (n)" would have to
> be written as "if (n != 0)" if n is an int. I know a lot of C
> programmers would hate this idea -- and yes, if I want Pascal I know
> where to find it.
>
Make that "if I want languages that are not C, I know where to find
them". I wouldn't call this a Pascalism.

Almost all popular imperative languages have a boolean type; certainly
most Algol derivatives. C does not, probably because of its minimalistic
origins; B had such operators as #+ for floating-point addition because
it had no separate floating-point type. All type information was
effectively encoded in the constants and operators. C still has that for
booleans.

S.

Keith Thompson

unread,
Nov 18, 2005, 4:21:16 PM11/18/05
to
Jordan Abel <jma...@purdue.edu> writes:
> On 2005-11-18, Keith Thompson <ks...@mib.org> wrote:
>> Tim Rentsch <t...@alumnus.caltech.edu> writes:
[...]

>> We have, because of a simple oversight, an standard that says (void*)0
>> is a null pointer constant, but ((void*)0) isn't
>
> I don't think it says that. It gives a verbal description, which _you_
> tihnk only applies to (void*)0, and which _i_ think also applies to
> ((void*)0). and (((void *)(000L))). and ((void *)'\0') - there may also
> be an argument that ((void *)(*"")) is.

I don't see how the description applies to ((void*)0). (void*)0 is
"[a]n integer constant expression with the value 0, or such an
expression cast to type void *". ((void*)0) is not. The standard
explicitly lists the attributes of an unparenthesized expression that
are inherited by the parenthesized expression; being a null pointer
constant isn't one of them. You can't assume that expr and (expr) are
exactly equivalent, because the standard doesn't say they are.

Personally, I think it's quite obvious that this is an oversight
rather than the actual intent, so implementations are justified in
treating ((void*)0) as a null pointer constant anyway, but doing so
does violate the letter of the standard.

>>> if(p)
>>> if(!p)
>>> ...
>>>
>>> Are these allowed or disallowed in your time-machined C?
>>
>> Now that's a good question. I think they'd have to be disallowed.
>
> why? Just allow conditionals to contain a pointer, which is tested for
> "nil"-ness.
>
>> The current rule for expressions is simple: "if (expr)" is equivalent
>> to "if ((expr) != 0)". If 0 couldn't be converted to a pointer type,
>> then "ptr != 0" wouldn't be a valid expression, so "if (ptr != 0)"
>> would be invalid, so "if (ptr)" would be invalid. The same argument
>> applies to "if (!ptr)".
>
> Or you could define a ! operator for pointers, and make if(expr)
> equivalent to if(!!expr) rather than if(0 != expr)

Sure, you could do that. My goal, since I'm inventing a C-like
language from scratch (rather than getting rich on the stock market
since I've got this time machine; don't ask me to justify my sense of
priorities) is to have as few special-case rules as possible. I'd
rather force everyone to write "if (ptr != nil)" than add a rule whose
only purpose is to let people save a few keystrokes by writing "if (ptr)".

If you think that being able to write "if (ptr)" is important, then
you should definitely add that feature to *your* hypothetical C-like
time-machine-enabled language.

And just to clarify one more point: the C standard committee, back in
the late 1980s, had to decide between backward compatibility and
cleanliness. I personally wish they had decided differently, but I'm
not suggesting that the decision they made wasn't the right one at the
time. We'd probably have to go back at least a decade earlier to make
this kind of change safely.

Jordan Abel

unread,
Nov 18, 2005, 4:40:16 PM11/18/05
to
On 2005-11-18, Keith Thompson <ks...@mib.org> wrote:
> Jordan Abel <jma...@purdue.edu> writes:
>> On 2005-11-18, Keith Thompson <ks...@mib.org> wrote:
>>> Tim Rentsch <t...@alumnus.caltech.edu> writes:
> [...]
>
>>> We have, because of a simple oversight, an standard that says (void*)0
>>> is a null pointer constant, but ((void*)0) isn't
>>
>> I don't think it says that. It gives a verbal description, which _you_
>> tihnk only applies to (void*)0, and which _i_ think also applies to
>> ((void*)0). and (((void *)(000L))). and ((void *)'\0') - there may also
>> be an argument that ((void *)(*"")) is.
>
> I don't see how the description applies to ((void*)0). (void*)0 is
> "[a]n integer constant expression with the value 0, or such an
> expression cast to type void *". ((void*)0) is not.

Yes it is. [see section "we're never going to get anything done arguing
in circles"]

> The standard explicitly lists the attributes of an unparenthesized
> expression that are inherited by the parenthesized expression; being a
> null pointer constant isn't one of them.

It's not clear that "being a null pointer constant" is an attribute in
the sense you're using the word. It seems doubtful that "being an
integer constant expression with the value 0 cast to type void *"
contains any elements that are not thus inherited.

Regardless, I couldn't find this list, so I have no way to argue against
that [a section number would be nice]

> You can't assume that expr and (expr) are exactly equivalent, because
> the standard doesn't say they are.

Again, where does it say they're not?

> Personally, I think it's quite obvious that this is an oversight
> rather than the actual intent, so implementations are justified in
> treating ((void*)0) as a null pointer constant anyway, but doing so
> does violate the letter of the standard.

The letter of the standard is not as clear as you claim it is.

kuy...@wizard.net

unread,
Nov 18, 2005, 4:48:12 PM11/18/05
to
Keith Thompson wrote:
> kuy...@wizard.net writes:
...

> > The most important way in which nil would differ from an NPC is that it
> > would never be legal in a non-pointer context, with a corresponding
> > improvement in type safety.
>
> I just thought of a possible glitch. It shouldn't be too hard to work
> around it, but I'm not quite sure how.
>
> What is the type of nil? The most obvious answer is void*, but
> there's no implicit conversion from void* to pointer-to-function.

The keyword nil would be useable only in the same contexts where a null
pointer constant is currently given special handling that makes it's
type effectively the same as some other pointer type. That same special
handling would apply, but only to 'nil', with a corresponding
improvement in the clarity of the standard.

> ugly solution. Other languages make nil have whatever pointer type is
> imposed by the context, but that seems un-C-like.

It isn't un-C-like; that's exactly what the current standard does with
null pointer constants.

> (And again, this is all completely impractical unless you're designing
> a new language without concern for breaking any existing code. I'm
> adding this disclaimer in case someone jumps into the middle of this
> discussion and thinks I'm proposing an actual change to the language.

I'll retain a copy of your disclaimer, for that same purpose.

It is loading more messages.
0 new messages