Implementation-defined null pointer constant?

Implementation-defined null pointer constant?

Post by Keith Thom » Fri, 16 Sep 2005 04:37:58


May an implementation define forms of null pointer constant other than
the ones specified in the standard?

C99 6.3.2.3p3 says:

An integer constant expression with the value 0, or such an
expression cast to type void *, is called a _null pointer
constant_. If a null pointer constant is converted to a pointer
type, the resulting pointer, called a _null pointer_, is
guaranteed to compare unequal to a pointer to any object or
function.

C99 7.17p3 (describing <stddef.h>) says:

The macros are
NULL
which expands to an implementation-defined null pointer constant;
[...]

It has been argued elsewhere that the phrase "implementation-defined
null pointer constant" in 7.17p3 implies that an implementation may
define other forms of null pointer constant, for example (void*)42.
The fact that there are other definitions in the standard that aren't
actually exhaustive adds some plausibility to this argument.

The counterargument is that the phrase "implementation-defined null
pointer constant" merely means that the implementation may choose (and
must document) which of the allowed forms of null pointer constant it
uses as the expansion of NULL. If the standard intended to allow
other forms, 6.3.2.3p3 would have been a much better place to say so.

There is a visible effect. If an implementation is *not* allowed to
define other forms of null pointer constant, then any conforming
implementation must issue a diagnostic for the declaration of
func_ptr_2 in the following:

int main(void)
{
void (*func_ptr_1)(void) = (void*)0;
/*
* Legal, since (void*)0 is a null pointer constant
*/

void (*func_ptr_2)(void) = (void*)42;
/*
* If (void*)42 is a null pointer constant, this is legal.
* Otherwise, it's a constraint violation.
*/

return 0;
}

If it is allowed, then a conforming implementation that documents
(void*)42 as a null pointer constant would not be required to issue a
diagnostic.

What is the intent?

--
Keith Thompson (The_Other_Keith) XXXX@XXXXX.COM < http://www.yqcomputer.com/ ~kst>
San Diego Supercomputer Center <*> < http://www.yqcomputer.com/ ~kst>
We must do something. This is something. Therefore, we must do this.
 
 
 

Implementation-defined null pointer constant?

Post by Richard Ke » Fri, 16 Sep 2005 06:06:24

Keith Thompson < XXXX@XXXXX.COM > writes:

[..]

(void *)0x100000000LL is perhaps a more realistic example which
evaluates to a null pointer on an implementation I have to hand. Yet,
lacking the integral constant of value 0, it is not a null pointer
constant.

6.3.2.3 expressly allows the conversion of null pointers to any other
pointer type, without mention of whether they were spelled out as a
null pointer constant or arrived at by some other means.

So I don't believe the example shows the distinction you want.

--
http://www.yqcomputer.com/

 
 
 

Implementation-defined null pointer constant?

Post by Keith Thom » Fri, 16 Sep 2005 06:59:57

ichard Kettlewell < XXXX@XXXXX.COM > writes:

Unless an implementation is allowed to, and in fact does, define
(void *)0x100000000LL as a null pointer constant. The question is whether
it's allowed to. (I'm going to withhold my opinion on this question,
at least for now.)


No, 6.3.2.3 describes what happens *if* a null pointer is converted to
another pointer type. It then goes on to describe which conversions
are legal.

6.7.8p11 says:

The initializer for a scalar shall be a single expression,
optionally enclosed in braces. The initial value of the object is
that of the expression (after conversion); the same type
constraints and conversions as for simple assignment apply, taking
the type of the scalar to be the unqualified version of its
declared type.

(Oddly, this paragraph appears under "Semantics" rather than
"Constraints", implying that a violation of the "shall" invokes
undefined behavior rather than being an actualy constraint violation.)

6.5.16.1 describes the constraints for simple assignment. For pointer
types, it allows both operands to be pointers to compatible types, one
operand to be a pointer to an object or incomplete type and the other
to be void*, or the left operand to be a pointer (including a function
pointer) and the right operand to be a null pointer constant.

Assigning a void* expression other than a null pointer constant to a
function pointer object without a cast is a constraint violation.

For that matter, assigning it with a cast is also a constraint
violation. 6.3.2.3 defines what pointer conversions are legal, and
converting void* to a function pointer isn't allowed even with an
explicit cast. (Allowing this is a common extension.)

To avoid the little semantics vs. constraints glitch in 6.3.2.3, and
to use a more realistic candidate for an implementation-defined null
pointer constant, here's an updated version of the test program:

int main(void)
{
void (*func_ptr_1)(void);
void (*func_ptr_2)(void);

func_ptr_1 = (void*)0;
/*
* Legal, since (void*)0 is a null pointer constant
*/

func_ptr_2 = (void*)0x100000000LL;
/*
* If (void*)0x100000000LL is a null pointer constant, this is legal.
* Otherwise, it's a constraint violation.
*/

return 0;
}

--
Keith Thompson (The_Other_Keith) XXXX@XXXXX.COM <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.
 
 
 

Implementation-defined null pointer constant?

Post by kuype » Fri, 16 Sep 2005 08:17:02


I think that this definition was intended to be exhaustive.

On 1999-08-18, Michael Rubenstein asserted in this newsgroup that:

He got no response to that assertion; but I wouldn't recommend
concluding that it was uncontroversial.

DR 067 indicates that, in C90, the list of standard integral types was
exhaustive, and was intended to be. Since there was no explicit
statement to that effect in the text, I think it's fair to assume that
exhaustive definitions are the default, and explicit wording is needed
to render a definition non-exhaustive.

Can you provide a clear counter-example: a definition provided by the
standard that is clearly not intended to be exhaustive, but which fails
to say so explicitly?
 
 
 

Implementation-defined null pointer constant?

Post by Keith Thom » Fri, 16 Sep 2005 11:16:27


XXXX@XXXXX.COM writes:

[snip]

I'm not sure about intent, but:

6.5:

An _expression_ is a sequence of operators and operands that
specifies computation of a value, or that designates an object or
a function, or that generates side effects, or that performs a
combination thereof.

By this definition, 42 is not an expression, since it contains no
operators and therefore no operands. But I think that's just an
error.

6.3.2.3p3:

An integer constant expression with the value 0, or such an
expression cast to type void *, is called a _null pointer
constant_. If a null pointer constant is converted to a pointer
type, the resulting pointer, called a _null pointer_, is
guaranteed to compare unequal to a pointer to any object or
function.

The second sentence is supposed to be the definition of the term "null
pointer", but it only says that a null pointer can be produced by
conversion of a null pointer constant; there are numerous other things
that can generate null pointers.

(The definition of lvalue in 6.3.2.1p1 isn't an example of a
non-exhaustive definition, just a broken one.)

I'm not finding any really good examples of deliberately
non-exhaustive definitions; perhaps I or someone else can find more
later. But Doug Gwyn has argued strongly here that definitions are
not required to be exhaustive.

--
Keith Thompson (The_Other_Keith) XXXX@XXXXX.COM < http://www.yqcomputer.com/ ~kst>
San Diego Supercomputer Center <*> < http://www.yqcomputer.com/ ~kst>
We must do something. This is something. Therefore, we must do this.
 
 
 

Implementation-defined null pointer constant?

Post by Douglas A. » Sat, 17 Sep 2005 22:50:26


Yes, a constant that acts as a null pointer value isn't
necessarily a n.p.c. That doesn't keep it from being
useful, just not something a s.c. program can rely on.
 
 
 

Implementation-defined null pointer constant?

Post by David R Tr » Sun, 18 Sep 2005 00:21:19


I've always maintained that the implementation can define NULL as
any kind of "implementation-defined null pointer constant" that it
sees fit, e.g.:

#define NULL __null__ // impl-specific keyword
#define NULL ((void *) 0x80000000) // CICS

I've not seen a convincing argument that either of these violates
the letter nor the intent of the standard. As long as the compiler
accepts all other mandated forms of "null pointer constant", it
seems reasonable to conclude that it can define NULL any way it
chooses, provided that it behaves like a null pointer constant in
all legal contexts.

After all, conforming programs are supposed to confine their use of
null pointers constants to '0', '0L', '(void*)0', and 'NULL', and
that last one is explicitly defined by the implementation. Unless
it is expected that an implementation provide standard header files
that are portable to other implementations (which is certainly not
the case), it is only reasonable to assume that the implementor has
a fair amount of latitude in defining the standard macros within
them. Implementations are also explicitly allowed to provide
extensions as long they do not modify the behavior of conforming
code.

-drt
 
 
 

Implementation-defined null pointer constant?

Post by kuype » Sun, 18 Sep 2005 02:39:54


...

Neither of those constructs can appear in strictly conforming code,
therefore implementation is free to treat them as null pointer
constants under the as-if rule. An implementation would not be free to
treat a construct that could occur in strictly conforming code as an
NPC unless it matches the standard's definition of an NPC. That would
make it very difficult to #define NULL as expanding into such a
construct.


The definition of null pointer constants is much broader than that. 0U,
'\0', (2*6-4*3), 0UL, 0x0, 0ll, my_enumeration_constant_with_value_0,
sizeof(array1)-sizeof(array1) and (int)0.1 are all null pointer
constants that are permitted in conforming programs.

If you're talking about good programming style, rather than what is
legal, only "NULL" should be used when a null pointer constant is
intended. Some people prefer 0, or consider 0 and NULL to be equally
good, but I disagree. I don't know of any argument that favors the
other options you list.

...

Extensions are allowed to modify the behavior of conforming code.
They're allowed to change the behavior of even strictly conforming
code, so long as the modification doesn't affect the content of the
program's outputs (the observable behavior). What they're not allowed
to do is to modify the observable behavior of strictly conforming code.
 
 
 

Implementation-defined null pointer constant?

Post by Wojtek Ler » Sun, 18 Sep 2005 03:38:03


Syntax errors and constraint violations can't appear in strictly
conforming code either, but implementations are not allowed to accept
them without a diagnostic. Do you think it would be OK for an
implementation to define NULL as

#define NULL } __null__ {

and accept it as a null pointer constant?


What are you referring to as the as-if rule? I thought the as-if rule
was the part of 5.1.2.3 that explains what optimizations are allowed.


Are strictly conforming programs forbidden to stringize NULL and parse
the result to see if it matches the standard definition of null pointer
constant?
 
 
 

Implementation-defined null pointer constant?

Post by Keith Thom » Sun, 18 Sep 2005 04:13:51

"Douglas A. Gwyn" < XXXX@XXXXX.COM > writes:


That wasn't my point. My point was that the definition of the term
"null pointer" says only that it can be produced by a null pointer
constant converted to a pointer type. The definition itself is not
exhaustive, in the sense that there are things that are null pointers
that *aren't* produced by a null pointer constant converted to a
pointer type. One example is the result of strchr("foo", 'x').

This was in response to XXXX@XXXXX.COM 's question:

Can you provide a clear counter-example: a definition provided by
the standard that is clearly not intended to be exhaustive, but
which fails to say so explicitly?

(We've disagreed before about whether definitions *should* be
exhaustive; I'm not (for the moment) trying to resurrect that
argument.)

--
Keith Thompson (The_Other_Keith) XXXX@XXXXX.COM < http://www.yqcomputer.com/ ~kst>
San Diego Supercomputer Center <*> < http://www.yqcomputer.com/ ~kst>
We must do something. This is something. Therefore, we must do this.
 
 
 

Implementation-defined null pointer constant?

Post by Keith Thom » Sun, 18 Sep 2005 04:32:52

"David R Tribble" < XXXX@XXXXX.COM > writes:


(An implementation could do this by adding
enum { __null__ };
to the appropriate header(s).)


But an implementation is also required to produce diagnostics for code
that violates constraints. An implementation that treats
((void *) 0x80000000) as a null pointer constant will fail to produce
a diagnostic if it's used to initialize a function pointer. That's
acceptable if implementations are *allowed* to define additional NPCs,
but not if they aren't.

Consider this. 6.3.2.3 defines the term "null pointer constant"; 7.17
defines the NULL macro. If the intent were to allow additional forms
of null pointer constant, why would it say so in 7.17 rather than in
6.3.2.3? The definition of NULL should depend on the definition of
"null pointer constant"; it shouldn't extend it.

If the standard didn't define the NULL macro, would 6.3.2.3 be enough
to convince you that implementations are allowed to define additional
null pointer constants?

--
Keith Thompson (The_Other_Keith) XXXX@XXXXX.COM < http://www.yqcomputer.com/ ~kst>
San Diego Supercomputer Center <*> < http://www.yqcomputer.com/ ~kst>
We must do something. This is something. Therefore, we must do this.
 
 
 

Implementation-defined null pointer constant?

Post by kuype » Sun, 18 Sep 2005 10:27:03

ojtek Lerch wrote:

Yes, so long as it consistently treats that string of tokens as a null
pointer constant, an not as something that contains a '}' character
that would, for instance, terminate a block, causing a syntax error. So
long as the user code itself doesn't contain a syntax error, and the
implementation uses special-case parsing to avoid incorrectly treating
the expansion of NULL as a syntax error, it should be OK. I think it
would be a very peculiar choice; something that works within the
language is more reasonable than something that would require
special-case handling in the parser. But I think it would be legal.

My point is that it's trivial for an implementation to handle __null__
in a special fashion, and a bit harder to treat } __null__ { as
special. However, it's very difficult to treat a NULL that expands
into, for instance, "42" as special. That's because "42" can occur in
strictly conforming user code.


What I mean is that, so long as an implementation behaves as if NULL
expanded into a null pointer constant, it doesn't matter what it
actually expands into. If the compiler handles it properly, the only
way strictly conforming code could tell would be by stringizing the
expansion of the NULL macro and parsing it. It's not obvious from a
strict reading of the standard, but from previous discusssion I've
participated in on this newsgroup, I think it's pretty clearly not the
intent of the committee to constrain implementors by requiring that
such a parse would work.


 
 
 

Implementation-defined null pointer constant?

Post by Keith Thom » Sun, 18 Sep 2005 11:32:12

XXXX@XXXXX.COM writes:
[...]

Of course, if } __null__ { is the definition of the NULL macro, then
it must be a valid null pointer constant even if it's used directly in
user code. For example, an implementation that defines NULL this way
would have to accept

int *ptr = } __null__ {;

I don't believe there's any permission to treat the token sequence
differently depending on whether it's the expansion of the NULL macro
or not.

However, there's another statement in the standard that may render
this whole thing irrelevant. C99 4p6 says:

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

If I'm reading this correctly, it implies that an implementation can
legally treat (void*)42 as a null pointer constant. Since this
doesn't alter the behavior of any strictly conforming program, it's an
allowed extension; the fact that it inhibits diagnostics for some
things that would otherwise be constraint violations doesn't
disqualify it, as long as the extension is documented (required by C99
4p8).

Neither 6.3.2.3p3 (defining "null pointer constant") nor 7.17p3
(defining the NULL macro) gives this permission, but 4p6 does.

I think it also implies that many, perhaps most (probably not all) of
the syntax rules and constraints in the standard can be sidestepped,
as long as the implementation documents the extension. A conforming
implementation can accept '$' characters in identifers, binary integer
constants like 0b11001001, types "long short" and "short long",
floating-point enumerators, variable-size bit-fields, etc. -- as long
as it documents the extensions and none of them affect the behavior of
any strictly conforming program. It doesn't even have to have a mode
in which these non-portable constructs are diagnosed.

Is this really the intent, or am I missing something?

If this is the intent, the permission in 5.1.2.2.1 to define main()
"in some other implementation-defined manner" seems to be redundant;
the implementation can already permit "float main(_Complex argc)"
as an extension.

--
Keith Thompson (The_Other_Keith) XXXX@XXXXX.COM <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.
 
 
 

Implementation-defined null pointer constant?

Post by kuype » Sun, 18 Sep 2005 23:50:25


...

Yes, that's why I think it's feasible to give NULL such a peculiar
definition, only if the appearance of that same token sequence in user
code is guaranteed to have undefined behavior.


I thought that undefined behavior was permission for anything?


I agree.


Many people have said so, for precisely this reason.
 
 
 

Implementation-defined null pointer constant?

Post by David R Tr » Mon, 19 Sep 2005 00:45:01

David R Tribble writes:



But if the indent was to limit the NULL macro to one of the standard
forms of null pointer constant, why does 7.17 allow an "implementation-
defined" form? Why doesn't it just say "one of the definitions of a
n.p.c as defined in 6.3.2.3"?



In that case, there would be no rules about what NULL could be or
couldn't be, so the implementation would be even less contrained.
But allowing "an implementation-defined" form seems pretty
unconstrained as it is.

-drt