Discussion:
Null pointer constant
(too old to reply)
bartc
2017-06-18 16:14:04 UTC
Permalink
Raw Message
Take this program:

int main(void) {
int a, b;
int *p;

p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}

AIUI, the 0's should all be null pointer constants in these contexts.

But many compilers fail to accept all three numbered lines.

All will do (1), but some report a type warning on (2) and/or (3).

Are lines (2) and (3) valid C?

(gcc reports a type error both those lines.)
--
bartc
bartc
2017-06-18 16:16:20 UTC
Permalink
Raw Message
Sorry, meant to post in comp.lang.c.
Kaz Kylheku
2017-06-18 17:22:14 UTC
Permalink
Raw Message
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.

Treating that case as null pointer constants would require that
to be derived from p, which is "upstairs" in the abstract syntax tree:

=
/ \
p ?:
/|\
a 0 0

that would mean that the "null-pointer-constant" property of a
literal isn't a purely synthesized attribute, but also inherited.

Does that have precedent anywhere in the language?

Type-like attributes generally go bottom-up.
Keith Thompson
2017-06-18 18:13:35 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.

In `p = 0;`, `0` is a null pointer constant -- and it would be in
any context. A null pointer constant yields a null pointer *if* it's
converted to a pointer type, but that's not part of the definition
(though it is in the same paragraph).

The context here is relevant because it causes an implicit conversion
to a pointer type, but `0` would be a null pointer constant even
without that conversion. (If it's not converted to a pointer type,
the fact that it's a null pointer constant is irrelevant.)

[...]
--
Keith Thompson (The_Other_Keith) kst-***@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Kaz Kylheku
2017-06-18 22:09:07 UTC
Permalink
Raw Message
Post by Keith Thompson
Post by Kaz Kylheku
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).

In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
Keith Thompson
2017-06-18 22:46:23 UTC
Permalink
Raw Message
[...]
Post by Kaz Kylheku
Post by Keith Thompson
Post by Kaz Kylheku
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).
0 is always of type int. (It may be converted to a pointer type if it's
in the right context.)
Post by Kaz Kylheku
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
--
Keith Thompson (The_Other_Keith) kst-***@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Philip Lantz
2017-06-19 07:16:49 UTC
Permalink
Raw Message
Post by Keith Thompson
[...]
Post by Kaz Kylheku
Post by Keith Thompson
Post by Kaz Kylheku
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).
0 is always of type int. (It may be converted to a pointer type if it's
in the right context.)
Yes, 0 is always of type int. It is also always a null pointer constant.
Philip Lantz
2017-06-19 07:18:24 UTC
Permalink
Raw Message
Post by Philip Lantz
Post by Keith Thompson
[...]
Post by Kaz Kylheku
Post by Keith Thompson
Post by Kaz Kylheku
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).
0 is always of type int. (It may be converted to a pointer type if it's
in the right context.)
Yes, 0 is always of type int. It is also always a null pointer constant.
I'm sorry, I didn't read your previous post carefully enough. You already
addressed this.
Richard Damon
2017-06-18 23:34:07 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Keith Thompson
Post by Kaz Kylheku
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
The question is if the standard defines (a ? 0 : 0) as a 'constant',
which is a well defined term in the standard. Since a is not know, I
don;t think it matches the definition. The fact that the compiler with
just a bit of smarts can determine this (and possibly optimize on it)
doesn't mean that the language defines it as a constant.
James Kuyper
2017-06-19 11:22:58 UTC
Permalink
Raw Message
Post by Richard Damon
Post by Kaz Kylheku
Post by Keith Thompson
Post by Kaz Kylheku
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
The question is if the standard defines (a ? 0 : 0) as a 'constant',
which is a well defined term in the standard. Since a is not know, I
It most definitely isn't a constant, but that's not the relevant
question. What matters is whether it's a integer constant expression.

'a' is known, from it's declaration, to identify an object of type
'int', with automatic storage duration, no linkage, and a scope that
includes almost the entire body of main(). I think you mean that the
value of 'a' is not known.
Post by Richard Damon
don;t think it matches the definition. The fact that the compiler with
just a bit of smarts can determine this (and possibly optimize on it)
doesn't mean that the language defines it as a constant.
The key issue is the following definitions:

"An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant. ..."
(6.3.2.3p3). The phrase "null pointer constant" is in italics in this
sentence, an ISO convention for indicating that this sentence
constitutes the official definition of that phrase.

"An integer constant expression ... shall only have operands that are
integer constants, enumeration constants, character constants, sizeof
expressions whose results are integer constants, _Alignof expressions,
and floating constants that are the immediate operands of casts. ..."
(6.6p6). The phrase "integer constant expression" is in italics in this
sentence.

Neither 'a' nor 'b' fit into any of those categories. Therefore, no
expression of which they are an operand can qualify as an integer
constant expression. Without qualifying as an ICE, such an expression
can't qualify as a null pointer constant, either.
Kaz Kylheku
2017-06-19 00:37:22 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Keith Thompson
Post by Kaz Kylheku
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
I feel that is overstated. The issue is simply the familiar old
situation that a ternary expression whose operands are constant
expressions isn't required to be a constant expression.
Implementations may recognize additional constant expressions
beyond those required. So the treatment of ?: can differ
among implementations.

There is the additional twist here that a isn't a constant
expression.

In any case, if a ? 0 : 0 were required to be a constant expression, all
othet rules being equal, it would be a zero-valued integer
constant expression, and hence a n.p. constant.
--
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List: http://www.kylheku.com/diy
ADA MP-1 Mailing List: http://www.kylheku.com/mp1
bartc
2017-06-19 09:28:33 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Kaz Kylheku
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
I feel that is overstated. The issue is simply the familiar old
situation that a ternary expression whose operands are constant
expressions isn't required to be a constant expression.
I think it is when all three operands are constants. As you can just a
ternary expression for the length of a file-scope array for example.
Post by Kaz Kylheku
Implementations may recognize additional constant expressions
beyond those required. So the treatment of ?: can differ
among implementations.
There is the additional twist here that a isn't a constant
expression.
In any case, if a ? 0 : 0 were required to be a constant expression, all
othet rules being equal, it would be a zero-valued integer
constant expression, and hence a n.p. constant.
For the purpose of conditionally assigning either 0 or 0 to a pointer
depending on whether a was true, the conditional expression doesn't need
to be constant; it can be a runtime expression.

(The two zeros can be hidden behind macros, enums, or yielded from
expressions that reduce to zero. A compiler may replace it with an
unconditional assignment.)

But it sounds like C lets implementers off the hook by not requiring
that embedded null-pointer-constants are recognised when part of a comma
and/or conditional expression. A simple workaround is to use (void*)0 or
NULL in those situations.
--
bartc
Tim Rentsch
2017-06-19 10:57:40 UTC
Permalink
Raw Message
Post by Keith Thompson
Post by Kaz Kylheku
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
In a ? 0 : 0, neither 0 is in a "pointer context" which would have
to come from the opposite operand.
As I read the standard's definition, whether an expression is a
null pointer constant or not does not depend on its context.
I agree; the real question is what the type of each 0, not whether
or not it is a null pointer constant (which 0 always is, as a
matter of lexical category).
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
I feel that is overstated. The issue is simply the familiar old
situation that a ternary expression whose operands are constant
expressions isn't required to be a constant expression.
Implementations may recognize additional constant expressions
beyond those required. So the treatment of ?: can differ among
implementations.
There is the additional twist here that a isn't a constant
expression.
I think the point can be argued about whether 'a?0:0' can be
allowed as a constant expression under any circumstances. The
reason is it cannot be evaluated at translation time: even
though its /value/ can be determined, it cannot be /evaluated/
at translation time, because its evaluation requires accessing
the contents of object 'a', but at translation time there is
no object 'a'. So the question is open to debate, 6.6 p10
notwithstanding.
In any case, if a ? 0 : 0 were required to be a constant
expression, all othet rules being equal, it would be a zero-valued
integer constant expression, and hence a n.p. constant.
Your reasoning is off here. The expression ((int)(1./10.)) is a
constant expression, and it is an integer-valued expression with
the value zero, but it is not an integer constant expression, and
hence not a null pointer constant. To be a null pointer constant
it's not enough for an expression to be a constant expression, be
of integer type, and have the value zero; it must be an integer
constant expression (or such an expression cast to type void *),
which does not necessarily follow from its constant-ness and its
type and value.
James Kuyper
2017-06-19 11:25:14 UTC
Permalink
Raw Message
...
Post by Kaz Kylheku
Post by Kaz Kylheku
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
I feel that is overstated. The issue is simply the familiar old
situation that a ternary expression whose operands are constant
expressions isn't required to be a constant expression.
Implementations may recognize additional constant expressions
beyond those required. So the treatment of ?: can differ
among implementations.
Can you cite any rule that makes 1?2:3 any more questionable as a
constant expression than 5-4?
Keith Thompson
2017-06-19 16:02:31 UTC
Permalink
Raw Message
Post by James Kuyper
...
Post by Kaz Kylheku
Post by Kaz Kylheku
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
I feel that is overstated. The issue is simply the familiar old
situation that a ternary expression whose operands are constant
expressions isn't required to be a constant expression.
Implementations may recognize additional constant expressions
beyond those required. So the treatment of ?: can differ
among implementations.
Can you cite any rule that makes 1?2:3 any more questionable as a
constant expression than 5-4?
A conditional (ternary) expression is an constant integer expression
if all three of its operands are constant integer expressions.
The definition of "constant expression" excludes comma operators,
but not conditional operators.
--
Keith Thompson (The_Other_Keith) kst-***@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jakob Bohm
2017-06-19 22:22:33 UTC
Permalink
Raw Message
Post by Keith Thompson
Post by James Kuyper
...
Post by Kaz Kylheku
Post by Kaz Kylheku
In p = (a ? 0 : 0) there is no basis for these null pointer constants
for being converted to the pointer type p.
I feel that is overstated. The issue is simply the familiar old
situation that a ternary expression whose operands are constant
expressions isn't required to be a constant expression.
Implementations may recognize additional constant expressions
beyond those required. So the treatment of ?: can differ
among implementations.
Can you cite any rule that makes 1?2:3 any more questionable as a
constant expression than 5-4?
A conditional (ternary) expression is an constant integer expression
if all three of its operands are constant integer expressions.
The definition of "constant expression" excludes comma operators,
but not conditional operators.
Simple question: What is/was the rationale for making comma operator
expressions not constant expressions, provided that the value-ignored
parts of the comma expression do not have side effects (which would be
rare but logically valid)?

Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
Transformervej 29, 2860 Søborg, Denmark. Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded
s***@casperkitty.com
2017-06-19 23:43:55 UTC
Permalink
Raw Message
Post by Jakob Bohm
Simple question: What is/was the rationale for making comma operator
expressions not constant expressions, provided that the value-ignored
parts of the comma expression do not have side effects (which would be
rare but logically valid)?
If some implementations happened to accept the comma operator as part of
a constant and others didn't, would requiring that they all accept the
operator have yielded sufficient benefit as to justify the expense of
changing those that didn't? Note that implementations are explicitly
allowed to accept forms beyond those given, so the lack of a mandate
shouldn't be taken to imply that implementations that used to accept the
comma operator should cease to do so.
Jakob Bohm
2017-06-20 01:46:40 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by Jakob Bohm
Simple question: What is/was the rationale for making comma operator
expressions not constant expressions, provided that the value-ignored
parts of the comma expression do not have side effects (which would be
rare but logically valid)?
If some implementations happened to accept the comma operator as part of
a constant and others didn't, would requiring that they all accept the
operator have yielded sufficient benefit as to justify the expense of
changing those that didn't? Note that implementations are explicitly
allowed to accept forms beyond those given, so the lack of a mandate
shouldn't be taken to imply that implementations that used to accept the
comma operator should cease to do so.
There is little benefit in a change, just curious as to the original
rationale.

Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
Transformervej 29, 2860 Søborg, Denmark. Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded
s***@casperkitty.com
2017-06-20 16:03:42 UTC
Permalink
Raw Message
Post by Jakob Bohm
Post by s***@casperkitty.com
Post by Jakob Bohm
Simple question: What is/was the rationale for making comma operator
expressions not constant expressions, provided that the value-ignored
parts of the comma expression do not have side effects (which would be
rare but logically valid)?
If some implementations happened to accept the comma operator as part of
a constant and others didn't, would requiring that they all accept the
operator have yielded sufficient benefit as to justify the expense of
changing those that didn't? Note that implementations are explicitly
allowed to accept forms beyond those given, so the lack of a mandate
shouldn't be taken to imply that implementations that used to accept the
comma operator should cease to do so.
There is little benefit in a change, just curious as to the original
rationale.
I think the idea was that while operators like ||, &&, and ?: imply
sequence points, the first operands are generally not evaluated *for the
purpose of any side effects produced thereby*. Given the expression (x,y)
sub-expression x would serve no purpose if it didn't have side-effects and
thus no useful purpose would be served by allowing that construct within a
constant expression.

If I were designing the language, I would allow the comma operator in the
absence of any reason to disallow it. I'd also interpret (0 && intexpr),
(1 || intexpr), (1 ? intconst : intexpr), and (0 ? intexpr : intconst)
as integer constant expressions and am somewhat curious as to why they're
not allowed given that in each of the above, "intexpr" should be "not
evaluated".

Keith Thompson
2017-06-18 18:17:36 UTC
Permalink
Raw Message
Post by bartc
int main(void) {
int a, b;
int *p;
p = 0; // (1)
p = (a, b, 0); // (2)
p = (a ? 0 : 0); // (3)
}
AIUI, the 0's should all be null pointer constants in these contexts.
[...]

I'll answer this in comp.lang.c.
--
Keith Thompson (The_Other_Keith) kst-***@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Loading...