C Standard Regarding Null Pointer Dereferencing

S

Shao Miller

...to be
honest, it was never a question to me.  I have been operating under
the _assumption_ that _any_ intelligent response received in this
forum (as Peter Seebach's posts after his first are) are those of
seasoned C developers.
Please and kindly allow me to retract this statement regarding
intelligent responses. It is wholly inaccurate. The use of the term
"intelligent" was not at all appropriate here. What I very well
should have said was simply "detailed," instead. There is no doubt
about the intelligence of Peter Seebach's first post nor of any of his
subsequent posts; no doubt for me and there should be no doubt for any
other reader. Unfortunately, I was missing a piece, here: "..._any_
intelligent response including detail..." This inaccuracy has been a
failure of mine and I fully offer an apology for it.
 
R

Richard Bos

Shao Miller said:
Wow. I've not been a student since 1995, but never did homework then,
either.

That explains a lot.

If you _had_ done your homework when you were in high school, you might
have learned to focus on the important details, not on the unimportant
ones.
The important detail in this case is: what _is_ the required behaviour,
in the abstract machine, of your code? Answer: there is none. And
therefore, the code's behaviour is undefined by omission _even if_ there
is no direct way to prove that it is undefined by commission.

Richard
 
B

Ben Bacarisse

Shao Miller said:
Absolutely agreed that it is just one of those things. The challenge
I perceive from this code is:

Why is a cast to 'void' well-defined but "dereferencing" a 'void *'
not well-defined?

As Richard Harter has said, to some extent it's just a consequence of
the over-use of one keyword. But there is some logic to the
distinction: Evaluating an expression and throwing way the result is a
commonly done thing (although most often done implicitly).
Dereferencing a pointer to get nothing is a useless activity.

Putting aside the words of the standard for a moment, the purpose of
evaluating an expression of the form *E is to recover a function
designator or an object from a function pointer or an object pointer.
Since void is not an object type, applying * to an expression of type
void * is counter-intuitive and goes against the main point of the
operator. C chooses to make it undefined.

Now we look at the text for the unary '*' operator (6.5.3.2,p4).
There we see a definition for the type of the result (of an
evaluation), just as we do for casting, would you agree?

They both talk about type, but the way the type is used is very
different. A cast converts and a pointer dereference interprets. Given
E of type T *, *E does not do any conversion (see the common trick of
"type-punning").
Thus if the
operand has type pointer-to-void, it suggests that the result has type
'void'.

I'd say is true at some level, but since there is no defined result,
it's type is not really relevant. It's like saying the result of 1/0 is
of type int. Yes it in is some sense, but since it is undefined the
type could be double[7]. When it is not evaluated (and the UB is
avoided) it does then have a type, but so does *(void *)E.
I would suggest that we must use the very same reasoning as
we do in our interpretation of cast operators to conclude that the
result is thus a void-expression (6.3.2.2,p1). The sentences
describing the result if pointing to an object and pointing to a
function do not apply. Nonetheless, the text appears to define the
result of evaluating application of unary '*' to an expression with
type pointer-to-void as being a result with type 'void'.

Only if you accept that a result can be nothing but a type and if you
reject the "undefined by omission" clause. Consider this:

The best slogan will win a car! The make of car will be the one
your slogan refers to.

What is the result of submitting the losing slogan "Fords are OK"?

Please take your time to consider the implications before responding.

Some people could take offence at this and, at the same time, it is
unlikely to prevent ill-considered replies.
I am very hopeful for an agreement here, but assume that if major
overhauls and negative implications might be perceived, that an
agreement is less likely to happen. I really don't see how:

int x;
void *p = &x;
*p;

could be a huge deal as well-defined behaviour.

Some bugs (such as the above) would go undetected. What is the gain
that makes this loss acceptable? In effect, this is my suggestion in
another form: what useful program needs to apply * to a void *?

<snip>
 
B

Ben Bacarisse

Are you suggesting that the above example doesn't matter?

Not to me, not. I can't, of course, speak for anyone else.
One such well-defined program might even be itself a C
implementation. It would be good to do it right by everyone here.

Then I think you asked the wrong questions. By analogy, it is as if you
asked: What is the result of this piece of C:

0

because its clearly not an expression? I am having a hard time
believing that you don't know what an implementation should do with

(void)13;
 
B

Ben Bacarisse

Shao Miller said:
On Jul 24, 11:09 am, Ben Bacarisse <[email protected]> wrote:
Agreed.

That suggests that you agree that type is not part of the result -- that
it is a static property of the expression form (roughly speaking, a
compile-time property rather than a run-time one). If so, then a
type-only result (a run-time idea if ever there was one) in
meaningless. That is certainly how I read the intent of the standard,
but I think you have repeated the type-only result idea in later replies.

I see the truth of it. This initialization specifies the value
initially stored in the object but there is no assignment expression.
If we take the text to mean "...assigned to the pointer by an
assignment expression...", then "the pointer" could not have been
assigned-to.

I don't follow you here. You said '*ip' "would be" covered "after"
and then said both would remain undefined by omission.

Yes, that's confusing. Forget for the moment that it is my contention
that both are undefined. Let's take your reading where defining the
type alone is enough. In the first case (const int *ip = 0; *ip) the
final sentence about the pointer having been assigned an invalid value
does not apply because there is no assignment. In the second (int *ip;
ip = 0; *ip) it would be explicitly undefined. What is the purpose of
this distinction? Could it be that the wording is wrong? Maybe
assignment has nothing to do with it and it is just the value of the
pointer that matters?

Please excuse me. Have I said suggested incorrectly?

It's probably just a language thing. "Guide" has this meaning:

A book of instruction or information for beginners or novices (in an
art, etc.). [OED online second edition 1989]

(There are other meaning of course, such a book providing travel
information, but none could relate to the C standard except
figuratively.)
Have I
suggested that this "unpaid work of dozens of experts over more than
two decades" is not a valuable resource? If so, what would cause you
to suggest that have implied/meant/stated that?

Yes but it is probably unintentional.
I believe this
resource to be _the_ most valuable resource for C. Is it a guide? In
what way(s) is it more than guide?

It is not information or instruction for beginners. It is an
international standard for a programming language.
Is it a math textbook? I would
answer that with "no."

Me too, but that's not the point.
Then I have misinterpreted and apologize. What should be the main
tool in case of ambiguity?

Reasoning, logic, common sense. All are better than popular consensus.
An example is to follow thought the consequences to the "pointer has
been assigned" sentence to see what it would mean. Do the consequences
make sense? Could there be another somewhat figurative interpretation
that seems to make more sense? It won't always work but it beats
popular consensus any day!
An accepted possibility. So do you suggest that common sense and
perusal of the draft/standard is all that is required to develop a
conforming implementation?

I don't know, but I doubt it. The many formal defect reports suggest not.
If so, do you see how that might come over
to someone? Would you be willing to help to refine an interpretation
of the term "common sense"?

Sure. I gave one example. In general, if there are two interpretations
that both have sound and useful consequences then we have a real problem
that might be hard to resolve. It won't always be easy to tell what
constitutes such a conflict but your example of *(char *)0 is not, to my
mind, one of them.

Can an expression at translation-time evaluate to a null pointer
constant?
Yes.

Can an expression at run-time evaluate to a null pointer
constant?

It can yield a value that compared equal to one. Other than that I
don't know what the question means.
6.3.2.3 p3 includes detail concerning an "integer constant
expression". Could the value of an object be an integer constant
expression?

The value of an object can compare equal to an integer constant
expression. Is that what "be" means to you?
If everyone knows or at least is adamant that "you can't
dereference a null pointer," could this constraint hurt the standard
for C?

You mean, should it be added explicitly? It think is, very nearly, in
the section you just cited (6.3.2.3 p3). If you combine this with the
meaning of == between pointers you find that a null pointer can't point
at an object (not can it can't to a function) so * of it is undefined.
Could it help to prevent questions like mine from accumulating
more responses than a single response with a citation?

Yes, probably. I think we'll have to live without it though.
Agreed. I have abandoned that constraint in favour of the "null
pointer constant" constraint mentioned.

That does not seem to be a big gain. I've never seen a null pointer
constant dereferenced in 25 years of looking at C. Making it a
constraint violation won't help much.
Could a void expression be considered a type-only result? 6.3.2.2 p1
describes an expression with no value (an empty set) and type 'void'.
Did we agree that such an expression is evaluated? They way I
perceive them to work is: If an expression's type is defined to have a
type "T" and a value is not defined for evaluation, and that type "T"
is 'void', the expression is a void expression. Is this a reasonable
perspective?

Not to me, no. In effect it adds only one new value to the language --
one that can't be used for anything. That does not seem reasonable. It
seems that you've rejected all other type-only values so do you see
*(char *)0 as undefined now?
 
S

Seebs

No, that's the usual way of things in clc, so that is not the
explanation for the high temperature of your entry into the group.

Exactly. The use of "invented" to refer to someone else's position is not
addressing their position, but making a meta-claim about it, which is that
their position was manufactured, rather than being the result of study of
the external world. That's what "invented" means.

And again, someone whose English skills don't make this fairly obvious should
not be arguing with people about the meanings of English sentences, in
general.

I've seen plenty of people with much more controversial points argue them
without trouble, because they didn't start out by insulting people and
ignoring what other people had to say, or dismissing it with nothing more
solid than "but I'm still not certain". (The latter may be, if anything,
more annoying; once you've presented a rigorous formal proof, for someone
to continue to say "but I'm not convinced, show me more explicit evidence"
is at the very least going to be frustrating.)

-s
 
S

Shao Miller

Actually its pretty simple.  He's confused by the void/void*
hack.
I disagree regarding confusion, but around the hack.
 Experienced C programmers are so used to it that it that
they don't see it as a hack.  Simply:

C has a whole bunch of primitive types, char, int, long, et al.
(It also has composite types, but let's not go there, okay.)
In addition there are pointer types.  The (almost) uniform rule
is dereferencing something of type T * yields something of type
T.  The exception, of course, is our friends, void and void *.
Despite appearances void * is not a pointer to a void.
I do not dispute that "void * is not a pointer to a void". Does is
not, however, have type "pointer-to-void"? I believe that it does.
That is not confusing.
Why is void an exception?  It's a hack.
I deduced this the first time I'd read a book on C. I might have
tried the same hack.
 When they were doing the
standard they had two issues they were trying to clear up.  One
was providing a way to say that a function returns nothing.  The
other was to provide generic pointers that would be automatically
converted without casting.
Just a guess: The third was allowing for an uninteresting result to be
discarded. Would you agree?

int func(void) {
return 5;
}

int main(void) {
(void)func();
return 0;
}
 These are two different concepts.
The hack is that they used one keyword for the two concepts,
making void a "it's a dessert topping and a floorwax" thingie.
Yes and I agree. It slices and dices and makes orange juice out of
sawdust. :)

Thank you for your response, Richard.
 
S

Shao Miller

That explains a lot.

If you _had_ done your homework when you were in high school, you might
have learned to focus on the important details, not on the unimportant
ones.
It is not clear to me as to which discussion I've provided that
suggests a focus on unimportant details. Perhaps you have only read
some of the posts, here? Regardless of which details you mean, what
is it exactly that deems them as important or unimportant? I think
that a detail with consequences would be more important than a detail
with none, in discussion of this subject matter. Is that a quick,
cheap way to think of it that you would agree with?
The important detail in this case is: what _is_ the required behaviour,
in the abstract machine, of your code? Answer: there is none. And
therefore, the code's behaviour is undefined by omission _even if_ there
is no direct way to prove that it is undefined by commission.
The matter of '(void)*(char *)0;' is resolved for myself as certainly
UB, by the way. Are you referring to some other code? What would be
the best way to announce that the matter of the original post is
resolved, to avoid wasting the time of responders who still believe it
to be an issue? What I did was to respond to the first post. Was
there a better way?
 
S

Shao Miller

As Richard Harter has said, to some extent it's just a consequence of
the over-use of one keyword.  But there is some logic to the
distinction: Evaluating an expression and throwing way the result is a
commonly done thing (although most often done implicitly).
Dereferencing a pointer to get nothing is a useless activity.
What I have attempted to suggest over and over again is that it should
be simply another form of void expression. We have two already, why
not three? Please consider:

static int i = 15;

void *func(void) {
i++;
return &i;
}

int main(void) {
*func();
return 0;
}

Compare 'main()' to this one:

int main(void) {
(void)func();
return 0;
}

My personal aesthetic would be for the former, rather than the
latter. I have suggested that the unary '*' operator defines the
result of '*func()' above to have type 'void'. Since this is known to
be a void expression (at translation time), and void expressions are
evaluated for their side effects, what is the problem here? In this
context, the unary '*' would simply mean "throw away the returned
'void *'". To me, that would be useful, not useless.
Putting aside the words of the standard for a moment, the purpose of
evaluating an expression of the form *E is to recover a function
designator or an object from a function pointer or an object pointer.
Sure. I even agree that that's the only way anyone I've ever known to
use it has used it. It just so happens that another purpose might
yield a pleasantry. Somewhat akin to the multiple purposes of 'void'
in the first place (the "hack").
Since void is not an object type, applying * to an expression of type
void * is counter-intuitive and goes against the main point of the
operator.  C chooses to make it undefined.
Ok, but what about the referenced draft? It appears to be well-
defined there as a void expression.
They both talk about type, but the way the type is used is very
different.  A cast converts and a pointer dereference interprets.
This is an interesting perspective to me and I will try to follow
along your train of thought, here.
 Given
E of type T *, *E does not do any conversion (see the common trick of
"type-punning").
Agreed. No conversion of a value. However, if you consider taking an
operand of one type and returning a result with another type, that
might be considered to be a conversion of type. Would you agree?
I'd say is true at some level, but since there is no defined result,
it's type is not really relevant.
My reasoning goes:
1. A type for the result is defined.
2. That type is defined to be 'void'.
3. Such a result is a void expression.
4. By 1, 2, 3, it is defined to be a void expression.

Do you perceive a flaw in this reasoning for any of those points?
Compare to casting to 'void':
1. A type for the result is defined.
2. That type is defined to be 'void'.
3. Such a result is a void expression.
4. By 1, 2, 3, it is defined to be a void expression.

Essentially, does any other treatment not need to pull in that 'void
*' is not a valid type for an operand of unary '*' from somewhere?
Does any other treatment not need to suggest that the operand
_shall_only_ have type pointer-to-function-type or type pointer-to-
object-type?
 It's like saying the result of 1/0 is
of type int.  Yes it in is some sense, but since it is undefined the
type could be double[7].  When it is not evaluated (and the UB is
avoided) it does then have a type, but so does *(void *)E.
We are in agreement, here. Also note that in:

int i = 15;
void *p = &i;
void *q;
q = p;
*p;

Evaluation of 'p' is defined, because we can assign it to 'q'. So at
what point exactly during evaluation of the last line does the
undefined behaviour creep in? If 'p' had an invalid value
(uninitialized, unassigned), surely that would be UB.
Only if you accept that a result can be nothing but a type and if you
reject the "undefined by omission" clause.
Does the result of casting to 'void' not have nothing but a type? Is
not the result of calling a "function returning void" one with nothing
but a type? I readily accept the "undefined by omission" clause. But
is it not _included_? "If the operand has type ‘‘pointer to type’’,
the result has type ‘‘type’’." 6.5.3.2,p4. Are 'void' and 'void *'
not both types?
 Consider this:

  The best slogan will win a car!  The make of car will be the one
  your slogan refers to.

What is the result of submitting the losing slogan "Fords are OK"?
The losing slogan is evaluated for its side effects (judges'
consideration) and the prize to the loser has no value and cannot be
used in any way. Is that not so?
Some people could take offence at this and, at the same time, it is
unlikely to prevent ill-considered replies.
What do _you_ perceive as potentially offensive about this request? I
cannot fathom how this could be offensive at all. I explain directly
thereafter that I believe that if major overhauls or negative
implications are perceived, that we are less likely to reach an
agreement. So if someone takes their time to consider the
implications, not only can they offer a nice set of implications, but
I can have some degree of certainty that their agreement was not based
on a quick read.
Some bugs (such as the above) would go undetected.
I think what you are suggesting here is that if we agree on well-
defined behaviour and treat it as a void expression, that a programmer
might not have intended it and will have a tough time finding it.

Suppose, for a moment, the a programmer had forgotten the type of
'p' (thought it was 'int *') as well as the prefix increment
operator. That might be a reasonably common error (it's two
keystrokes away from '*p;'). Constraint 6.5.3.1,p1 catches it.

Another "two-keystroker" would be a function call with no arguments.
If the programmer tried this and the function did not have type
"function returning pointer-to-void", we should catch either a "result
unused" or an attempt to dereference something not a pointer, perhaps.

No problems, with those.
 What is the gain
that makes this loss acceptable?  In effect, this is my suggestion in
another form: what useful program needs to apply * to a void *?

static int i = 15;

void *func(void) {
i++;
return &i;
}

int main(void) {
int *j;
int k = 5;
j = func();
/* ... */
j = &k;
/*
* We don't have a pointer to 'i' any more, but wish to increment
it.
* We know that 'func()' does this, but we are using 'j' for 'k'
indirection.
* Call 'func' and discard its result.
*/
*func();
return 0;
}

Thanks, Ben.
 
S

Shao Miller

Not to me, not.  I can't, of course, speak for anyone else.
That's completely fair.
Then I think you asked the wrong questions.  By analogy, it is as if you
asked: What is the result of this piece of C:

  0

because its clearly not an expression?
I believe that that is an expression. "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." 6.5,p1. This definition has already
been noted by Keith and I believe yourself as odd. We can agree to
reasonably try to disambiguate with other parts of the text. "A
constant is a primary expression. Its type depends on its form and
value, as detailed in 6.4.4." 6.5.1,p3.
 I am having a hard time
believing that you don't know what an implementation should do with

  (void)13;
I believe that you have misunderstood my question. Response posts
have continually demonstrated that my non-questions might be offensive
to some posters, despite my lack of intention for such offence. I
have no trouble seeing that '(void)13;' should:
1. Convert the value of the expression '13' to a result with type
'void'. 'void' cannot have a value, so the value is discarded.
2. Note that an expression of type 'void' is a void expression.
3. Note that this expression is part of an expression statement.
 
S

Shao Miller

Exactly.  The use of "invented" to refer to someone else's position is not
addressing their position, but making a meta-claim about it, which is that
their position was manufactured, rather than being the result of study of
the external world.  That's what "invented" means.
I have apologized for my use of "invented" already, for how it was
interpreted. If one allows one's self, just for a minute, to consider
the perspective of a person expecting each and every point in a
convincing argument to be something like:
A. Such-and-such, as per [reference]
B. Such-and-such-else, as per [reference]
C. Yet more, as per [reference]
D. Can we agree on [something without reference]?
E. I interpret "foo" to mean "bar"
F. Can we agree that with A, B, C, D, E, that "baz"? That's how I got
to "baz" for myself.

That is, this draws from material accessible to both parties, instead
of knowledge and experience available to only one party. It also
draws on agreements between both parties. It also details a personal
interpretation of something, giving at least more detail about
something.

You might notice that many of my posts take this form. Hopefully,
thus shows that I am not a hypocrite.

Now if one further allows one's self to additionally consider the use
of "invented" as meaning neither explicitly referenced, brought
forward as something to be agreed upon, nor brought forward as
anything more than a personal interpretation, its use makes sense
without insulting the knowledge and experience of the poster. What it
means is "where does that come from? You don't specify."

I had hoped that that was clear when I added to what I meant by
"invented." When I first wrote "invented" and thought about it, I
thought, "that's too harsh." Hopefully that can put to rest the
argument regarding familiarity with English speakers' use of English.
I added the additional meaning to try to help to clarify what was
meant. I did not scroll up and type it out as high as I meant to,
before the first use of "invented." It appears that even the
additional meaning was not enough to clarify. In fact, "invent" has
proven here to be a word with a gigantic impact. I imagine steam
coming out of ears and eyes wide.

If one allows one's self to be tolerant of the perspectives of others,
one might see how I was constraining my own interpretation of response
posts with this perspective. That's my challenge to deal with the
consequences. A huge mistake was having this expectation without ever
having agreed upon rules of engagement. I have already apologized.
And again, someone whose English skills don't make this fairly obvious should
not be arguing with people about the meanings of English sentences, in
general.
And that's Peter Seebach's opinion on "invented" and "fairly obvious"
as being an insult. The opinion has value to me, now. I disagree
that a party's inappropriate use of one word warrants dismissal of any
other of that party's arguments. That strikes me as too general an
argument for so little a "mistake".
I've seen plenty of people with much more controversial points argue them
without trouble, because they didn't start out by insulting people and
ignoring what other people had to say, or dismissing it with nothing more
solid than "but I'm still not certain".  (The latter may be, if anything,
more annoying; once you've presented a rigorous formal proof, for someone
to continue to say "but I'm not convinced, show me more explicit evidence"
is at the very least going to be frustrating.)
A: Such-and-such.
B: No way!
A: Whatever. You're obviously a moron without any capacity for
reasoning and I don't care.
B: That's fine that you don't care to discuss it any longer.

Compare to:
A: Such-and-such.
B: No way! But it's fine if that leads you to not care to discuss it
anymore.
A: You're right. I don't.

The latter avoids a personal attack and was fully my intention. The
technique does not always work.
 
B

Ben Bacarisse

Shao Miller said:
On Jul 25, 12:22 pm, Ben Bacarisse <[email protected]> wrote:
I believe that you have misunderstood my question.

Maybe. But it does not matter. You have a habit of cutting off all the
context so to explain myself I'd have to put it all back. Life is too
short for that.

<snip>
 
S

Shao Miller

That suggests that you agree that type is not part of the result -- that
it is a static property of the expression form (roughly speaking, a
compile-time property rather than a run-time one).  If so, then a
type-only result (a run-time idea if ever there was one) in
meaningless.  That is certainly how I read the intent of the standard,
but I think you have repeated the type-only result idea in later replies.
As someone pointed out, the meaning of "result" not being set out
explicitly in the discussion might be hindering mutual understanding.
I personally consider the "result" of an expression in C to have both
static properties available at translation time as well as possibly
associated values during evaluation at execution time.

As far as I'm concerned, an expression is "what you wrote" and a
result is "what you get." You "get" some things at translation-time
and possibly other things during evaluation at execution time.

Without considering "result" in this way, it is possible that we might
consider it to only have meaning during evaluation at execution time,
and requiring a value. That is simply not the way I consider it. If
you or others would rather that I did, I shall do so for the purposes
of common meaning during discussion. In that case, I'd have to resort
to using "expression" only. I would be reluctant to do so since
'sizeof' would have trouble determining the type of the expression
'*(char *)c', since the text only defines the type for results.
Yes, that's confusing.  Forget for the moment that it is my contention
that both are undefined.  Let's take your reading where defining the
type alone is enough.  In the first case (const int *ip = 0; *ip) the
final sentence about the pointer having been assigned an invalid value
does not apply because there is no assignment.  In the second (int *ip;
ip = 0; *ip) it would be explicitly undefined.  What is the purpose of
this distinction?  Could it be that the wording is wrong?  Maybe
assignment has nothing to do with it and it is just the value of the
pointer that matters?
Hence my posts where I have agreed to treatment of that text to mean
it as it has been offered by other posters, including yourself. Keith
pointed out that evaluation of a pointer yielded a value, and that
that value, not an lvalue, was the operand to '*'. I think we all
agree with that. I'm trying to drop discussion of the point.
It's probably just a language thing.  "Guide" has this meaning:

  A book of instruction or information for beginners or novices (in an
  art, etc.).  [OED online second edition 1989]

(There are other meaning of course, such a book providing travel
information, but none could relate to the C standard except
figuratively.)
Ahem. Will you please observe the first two sentences found here[1]?
They are:

"The Oxford English Dictionary is the accepted authority on the
evolution of the English language over the last millennium. It is an
unsurpassed guide to the meaning, history, and pronunciation of over
half a million words, both present and past."

Please note that "guide" is included there, and that you have
referenced this guide to support your point that my use of the word
could be offensive.

Perhaps there is something else that could be offending. I don't
really want to get into it, as it would be tangential to my current
interest regarding dereferencing pointers to 'void'.
Yes but it is probably unintentional.
No, I have not. If you and certain other posters continue to inject
negative implications, connotations, or anything else that can be
perceived negatively to my statements just because you do not like
what I have to say, the most I can do is to address it and apologize.
Is it remotely possible that these "bad things" are being attached for
some other reason? I appreciate that you have recognized that "it is
probably unintentional." That's the absolute truth, so thanks for
seeing it.
It is not information or instruction for beginners.  It is an
international standard for a programming language.
Right.


Me too, but that's not the point.
The point is to avoid calling the draft for a standard of C with
filename 'n1256.pdf' a "guide." Fine. Another word to add to my
list. Only identified with your kind feedback.
Reasoning, logic, common sense.  All are better than popular consensus.
An example is to follow thought the consequences to the "pointer has
been assigned" sentence to see what it would mean.  Do the consequences
make sense?  Could there be another somewhat figurative interpretation
that seems to make more sense?  It won't always work but it beats
popular consensus any day!
I must have omitted that the path would be:
1. Read the material
2. Apply reasoning, logic, common sense
3. In case of ambiguity, find out how others interpret

For me, 3 is not possible without 2. Ambiguity cannot be determined
without having applied these things. Would you agree?
I don't know, but I doubt it.  The many formal defect reports suggest not.
Agreed.


Sure.  I gave one example.  In general, if there are two interpretations
that both have sound and useful consequences then we have a real problem
that might be hard to resolve.  It won't always be easy to tell what
constitutes such a conflict but your example of *(char *)0 is not, to my
mind, one of them.
Tirelessly agreed-to. Let us all please drop '(void)*(char *)0;' and
discuss dereferencing a pointer-to-void as a useful form of void
expression.
Agreed.


It can yield a value that compared equal to one.  Other than that I
don't know what the question means.
The "snipped" context was regarding my proposal for a constraint. If
I "snipped" it, I apologize. You've suggested in another post that
that behaviour on my part demands more time to restore context. I'll
cut it out. (Get it?)

Since the constraint regards an integer constant expression and casts,
the constraint is a legitimate translation-time constraint. Would you
agree?
The value of an object can compare equal to an integer constant
expression.  Is that what "be" means to you?
Sure it can compare equal to one. This value cannot "be" one, right.
An expression can "be" one, can it not? We are considering
expressions at translation-time. The constraint has meaning.
You mean, should it be added explicitly?  It think is, very nearly, in
the section you just cited (6.3.2.3 p3).  If you combine this with the
meaning of == between pointers you find that a null pointer can't point
at an object (not can it can't to a function) so * of it is undefined.
Yes, I did mean that I believe it should be added explicitly. The
additional constraint results in an implementation being capable of
diagnosing it at translation-time. That is valuable. Undefined
behaviour by the last sentence is during evaluation. Consider
'sizeof'.
Yes, probably.  I think we'll have to live without it though.
Agreed. One can hope.
That does not seem to be a big gain.  I've never seen a null pointer
constant dereferenced in 25 years of looking at C.  Making it a
constraint violation won't help much.
Hey, I haven't seen one either, fortunately. :) Help much? Maybe
not, other than for 'sizeof', at least. A better chance to request of
the novice, "What do you think you're doing?"
Not to me, no.  In effect it adds only one new value to the language --
one that can't be used for anything.  That does not seem reasonable.  It
seems that you've rejected all other type-only values so do you see
*(char *)0  as undefined now?
See above.

Thanks, Ben.

[1] http://dictionary.oed.com/about/
 
B

Ben Bacarisse

Shao Miller said:
What I have attempted to suggest over and over again is that it should
be simply another form of void expression. We have two already, why
not three? Please consider:

static int i = 15;

void *func(void) {
i++;
return &i;
}

int main(void) {
*func();
return 0;
}

Compare 'main()' to this one:

int main(void) {
(void)func();
return 0;
}

My personal aesthetic would be for the former, rather than the
latter.

Mine is for a third:

func();

Your suggestion seems to have very little utility.
I have suggested that the unary '*' operator defines the
result of '*func()' above to have type 'void'. Since this is known to
be a void expression (at translation time), and void expressions are
evaluated for their side effects, what is the problem here? In this
context, the unary '*' would simply mean "throw away the returned
'void *'". To me, that would be useful, not useless.

You do know it's undefined, yes? The problem is suggesting a change to
a language standard with so little benefit. I see from later text that
you don't agree that it's undefined. If the current language permitted
it, there would be no problem. I'd still advice people to chose my
simpler function call but you would free to use *func() all you want.

Ok, but what about the referenced draft? It appears to be well-
defined there as a void expression.

Well, I think not. void is an incomplete type not an object type and a
void * is therefore not a pointer to an object for the purposes of the *
operator. If it were, * would yield an lvalue of type void which
directly contradicts 6.3.2.1 p1:

An lvalue is an expression with an object type or an incomplete type
other than void;

Agreed. No conversion of a value. However, if you consider taking an
operand of one type and returning a result with another type, that
might be considered to be a conversion of type. Would you agree?

No. Conversions have special rules in C. To use the term for something
else would add confusion.
My reasoning goes:
1. A type for the result is defined.
2. That type is defined to be 'void'.
3. Such a result is a void expression.
4. By 1, 2, 3, it is defined to be a void expression.

Do you perceive a flaw in this reasoning for any of those points?

Yes. The type is not enough. The pointer must be an object pointer not
a pointer to an incomplete type.
Compare to casting to 'void':
1. A type for the result is defined.
2. That type is defined to be 'void'.
3. Such a result is a void expression.
4. By 1, 2, 3, it is defined to be a void expression.

Essentially, does any other treatment not need to pull in that 'void
*' is not a valid type for an operand of unary '*' from somewhere?
Does any other treatment not need to suggest that the operand
_shall_only_ have type pointer-to-function-type or type pointer-to-
object-type?

Undefined by omission. That's way stronger than a suggestion. If you
don't accept it, just say and I'll shut up. I have no interest in
persuading you one way or the other.
 It's like saying the result of 1/0 is
of type int.  Yes it in is some sense, but since it is undefined the
type could be double[7].  When it is not evaluated (and the UB is
avoided) it does then have a type, but so does *(void *)E.
We are in agreement, here. Also note that in:

int i = 15;
void *p = &i;
void *q;
q = p;
*p;

Evaluation of 'p' is defined, because we can assign it to 'q'. So at
what point exactly during evaluation of the last line does the
undefined behaviour creep in?

When the * is applied to something that is no an object pointer (or a
function pointer).
If 'p' had an invalid value
(uninitialized, unassigned), surely that would be UB.

Does the result of casting to 'void' not have nothing but a type? Is
not the result of calling a "function returning void" one with nothing
but a type? I readily accept the "undefined by omission" clause. But
is it not _included_? "If the operand has type ‘‘pointer to type’’,
the result has type ‘‘type’’." 6.5.3.2,p4. Are 'void' and 'void *'
not both types?

You've lost me. Yes they are both type. Were the result defined, we'd
know its type. It's pointless knowing the type if the result is not
defined. That was the point of the analogy:
The losing slogan is evaluated for its side effects (judges'
consideration) and the prize to the loser has no value and cannot be
used in any way. Is that not so?

There is a prize to the loser? Not by my reading. The result is
undefined by omission in my book. Knowing what type it _would_ be were
the slogan to win makes no difference to the undefined nature of the
result.
What do _you_ perceive as potentially offensive about this request? I
cannot fathom how this could be offensive at all.

It's just my opinion. Please disregard it.

I think what you are suggesting here is that if we agree on well-
defined behaviour and treat it as a void expression, that a programmer
might not have intended it and will have a tough time finding it.

"Tough time" is much more than I'd say. You are proposing a relaxation
of a current rule (from my point of view -- I know you don't agree
with that view) so the space of valid programs grows a little, however
small. This is a cost -- a tiny one -- which might be worth it if there
is a benefit.
Suppose, for a moment, the a programmer had forgotten the type of
'p' (thought it was 'int *') as well as the prefix increment
operator. That might be a reasonably common error (it's two
keystrokes away from '*p;'). Constraint 6.5.3.1,p1 catches it.

Another "two-keystroker" would be a function call with no arguments.
If the programmer tried this and the function did not have type
"function returning pointer-to-void", we should catch either a "result
unused" or an attempt to dereference something not a pointer, perhaps.

No problems, with those.

I can't follow any of this.
static int i = 15;

void *func(void) {
i++;
return &i;
}

int main(void) {
int *j;
int k = 5;
j = func();
/* ... */
j = &k;
/*
* We don't have a pointer to 'i' any more, but wish to increment
it.
* We know that 'func()' does this, but we are using 'j' for 'k'
indirection.
* Call 'func' and discard its result.
*/
*func();
return 0;
}

I'd call func();. If you want to signal the discarded value,
(void)func(); does that much more clearly. I don't see any benefit to
the *.
 
B

Ben Bacarisse

Shao Miller said:
As someone pointed out, the meaning of "result" not being set out
explicitly in the discussion might be hindering mutual understanding.
I personally consider the "result" of an expression in C to have both
static properties available at translation time as well as possibly
associated values during evaluation at execution time.

As far as I'm concerned, an expression is "what you wrote" and a
result is "what you get." You "get" some things at translation-time
and possibly other things during evaluation at execution time.

Without considering "result" in this way, it is possible that we might
consider it to only have meaning during evaluation at execution time,
and requiring a value. That is simply not the way I consider it. If
you or others would rather that I did, I shall do so for the purposes
of common meaning during discussion. In that case, I'd have to resort
to using "expression" only. I would be reluctant to do so since
'sizeof' would have trouble determining the type of the expression
'*(char *)c', since the text only defines the type for results.

That does not clear up the question of whether, in your opinion, a
result can be defined because the type is defined (and nothing more).
Obviously I'm a "no" on that and I don't think any more debate will be
useful so a simple "yes" or "no" from you will be enough to end this
topic.
Hence my posts where I have agreed to treatment of that text to mean
it as it has been offered by other posters, including yourself. Keith
pointed out that evaluation of a pointer yielded a value, and that
that value, not an lvalue, was the operand to '*'. I think we all
agree with that. I'm trying to drop discussion of the point.

OK. This whole thread will be wrapped up in no time!
It's probably just a language thing.  "Guide" has this meaning:

  A book of instruction or information for beginners or novices (in an
  art, etc.).  [OED online second edition 1989]

(There are other meaning of course, such a book providing travel
information, but none could relate to the C standard except
figuratively.)
Ahem. Will you please observe the first two sentences found here[1]?
They are:

"The Oxford English Dictionary is the accepted authority on the
evolution of the English language over the last millennium. It is an
unsurpassed guide to the meaning, history, and pronunciation of over
half a million words, both present and past."

Please note that "guide" is included there, and that you have
referenced this guide to support your point that my use of the word
could be offensive.

Of course the OED is a guide to the language. It is also an
authoritative dictionary of reference (another description from the same
site). Being one does not preclude the other. You said you failed to
regard the C standard as anything more that a guide.
Perhaps there is something else that could be offending. I don't
really want to get into it, as it would be tangential to my current
interest regarding dereferencing pointers to 'void'.

I agree. I think arguments about tone are pointless.
No, I have not. If you and certain other posters continue to inject
negative implications, connotations, or anything else that can be
perceived negatively to my statements just because you do not like
what I have to say, the most I can do is to address it and apologize.
Is it remotely possible that these "bad things" are being attached for
some other reason? I appreciate that you have recognized that "it is
probably unintentional." That's the absolute truth, so thanks for
seeing it.

I won't comment on tone anymore.

The point is to avoid calling the draft for a standard of C with
filename 'n1256.pdf' a "guide." Fine. Another word to add to my
list. Only identified with your kind feedback.

If you called is an "unsurpassed guide" I suspect some people would say you
are being too generous. Calling it "no more than a guide" is another
matter. I defined the word only because I suspected it was not quite
what you meant. The word itself is not the problem.
I must have omitted that the path would be:
1. Read the material
2. Apply reasoning, logic, common sense
3. In case of ambiguity, find out how others interpret

For me, 3 is not possible without 2. Ambiguity cannot be determined
without having applied these things. Would you agree?

Yes, with the refinement that 1, 2 and 3 iterate. The effect is a
careful consideration of the arguments and reasoning of others. This is
very different to what I take the phrase "popular consensus" to mean.
For example, I'd bet that the popular consensus amongst C programmers is
that

int two_d_array[10][10];
two_d_array[0][20] = 42;

is well-defined, but it's hard to find well-reasoned arguments from C
experts to support that view.

Yes, I did mean that I believe it should be added explicitly. The
additional constraint results in an implementation being capable of
diagnosing it at translation-time. That is valuable. Undefined
behaviour by the last sentence is during evaluation. Consider
'sizeof'.

I think I now understand what you are proposing but I had to go back and
forth though the thread finding this spot in each message to see what
I'd said and what you'd then replied. I now don't have the energy say
much more than if you want a constraint against * being applied to a
null pointer constant, go for it. I don't mind one way or the other.
The effort in proposing a change to the standard calls seems to demand a
more significant benefit, but I won't be doing it.

<snip>
 
S

Shao Miller

Mine is for a third:

  func();

Your suggestion seems to have very little utility.
Ok.


You do know it's undefined, yes?  The problem is suggesting a change to
a language standard with so little benefit.  I see from later text that
you don't agree that it's undefined.  If the current language permitted
it, there would be no problem.  I'd still advice people to chose my
simpler function call but you would free to use *func() all you want.
Ok.



Well, I think not.  void is an incomplete type not an object type and a
void * is therefore not a pointer to an object for the purposes of the *
operator.  If it were, * would yield an lvalue of type void which
directly contradicts 6.3.2.1 p1:

  An lvalue is an expression with an object type or an incomplete type
  other than void;
It's not a pointer to a function-type, either. If it were, '*' would
yield a function designator with type 'void', which doesn't appear to
make sense, either. Was there a reason you chose to treat 'void *' to
most closely resemble pointer-to-an-object-type versus pointer-to-a-
function-type, here? I'm guessing that you figured it was more
obvious that 'void *' is not a pointer-to-a-function-type. If I
recall correctly, ANSI C didn't even allow for a function pointer
value to be assigned to a 'void *'. I could be misremembering.
No.  Conversions have special rules in C.  To use the term for something
else would add confusion.
Ok.




Yes.  The type is not enough.  The pointer must be an object pointer not
a pointer to an incomplete type.
Why have you omitted a function designator here, again? Because it's
too obvious?

Also, why must the two "ifs" imply that the type of operand cannot be
'void *'? Here's the text of 6.5.3.2,p4:

"The unary * operator denotes indirection. If the operand points to a
function, the result is
a function designator; if it points to an object, the result is an
lvalue designating the
object. If the operand has type ‘‘pointer to type’’, the result has
type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the
unary * operator is
undefined."

Are you suggesting that according to the above, the lack of "...; if
it has type ‘‘pointer to void’’, the result has no value and the
expression is a void expression..." is what would be needed for my
suggested interpretation to be valid; the omission of such thus
resulting in no definition for the behaviour? I'm trying to pinpoint
why you consider this to not be a natural consequence of "If the
operand has type..." combined with "The (nonexistent) value of a void
expression (an expression that has type void)..." from 6.3.2.2,p1.

Should the constraint "The operand of the unary * operator shall have
pointer type." from 6.5.3.2,p2" be interpreted to mean "The operand of
the unary * operator shall have a pointer type suitable either for
pointing to an object or for pointing to a function." due to the
sentence "If the operand points to..." but _not_ due to the next
sentence "If the operand has type..."?
Undefined by omission.  That's way stronger than a suggestion.  If you
don't accept it, just say and I'll shut up.  I have no interest in
persuading you one way or the other.
Well, there's also the alternative to "shutting up" that is helping me
to understand your interpretation. Even if I could never be persuaded
by some kind of stubbornness, it would still have value as an easily
accessible reference. The choice is yours, clearly.
 It's like saying the result of 1/0 is
of type int.  Yes it in is some sense, but since it is undefined the
type could be double[7].  When it is not evaluated (and the UB is
avoided) it does then have a type, but so does *(void *)E.
We are in agreement, here.  Also note that in:
int i = 15;
void *p = &i;
void *q;
q = p;
*p;
Evaluation of 'p' is defined, because we can assign it to 'q'.  So at
what point exactly during evaluation of the last line does the
undefined behaviour creep in?

When the * is applied to something that is no an object pointer (or a
function pointer).
That's what I've gathered as your meaning, from this post. Thanks.
You've lost me.  Yes they are both type.  Were the result defined, we'd
know its type.  It's pointless knowing the type if the result is not
defined.
"Cast operators", 6.5.4:

"...Constraints

2 Unless the type name specifies a void type, the type name shall
specify qualified or
unqualified scalar type and the operand shall have scalar type.

3 Conversions that involve pointers, other than where permitted by the
constraints of
6.5.16.1, shall be specified by means of an explicit cast.

Semantics

4 Preceding an expression by a parenthesized type name converts the
value of the
expression to the named type. This construction is called a cast.89) A
cast that specifies
no conversion has no effect on the type or value of an expression.

5 If the value of the expression is represented with greater precision
or range than required
by the type named by the cast (6.3.1.8), then the cast specifies a
conversion even if the
type of the expression is the same as the named type."

Where do we see a definition of the result of casting to 'void',
here? Is it not omitted just the same as in the unary '*' operator?
Does that mean that casting to 'void' yields undefined behaviour? I
should think not for the latter, as I'm positive you'd agree.
 That was the point of the analogy:



There is a prize to the loser?  Not by my reading.  The result is
undefined by omission in my book.  Knowing what type it _would_ be were
the slogan to win makes no difference to the undefined nature of the
result.
Ok. I concede this. A prize is not defined for the loser. They have
what they had before the contest was judged, a Ford with an empty set
of physical components. This is not quite the same as having a
prize. So again to cast operators, where is the result of casting to
'void' defined?
... ... ... -- and I did not cut the code that is being discussed here.



"Tough time" is much more than I'd say.  You are proposing a relaxation
of a current rule (from my point of view -- I know you don't agree
with that view) so the space of valid programs grows a little, however
small.  This is a cost -- a tiny one -- which might be worth it if there
is a benefit.
Ok.




I can't follow any of this.
Ok.





I'd call func();.  If you want to signal the discarded value,
(void)func(); does that much more clearly.  I don't see any benefit to
the *.
Ok.
 
B

Ben Bacarisse

Richard Heathfield said:
Ben Bacarisse wrote:

For example, I'd bet that the popular consensus amongst C programmers is
that

int two_d_array[10][10];
two_d_array[0][20] = 42;

is well-defined, but it's hard to find well-reasoned arguments from C
experts to support that view.

This is of course a well-chewed-over chestnut. My own view is that the
behaviour of the code is theoretically undefined, *but* you'd be
hard-pressed to find an implementation that did anything particularly
surprising with it.

Yes. I have a sort of internal scale of undefined behaviour and this is
over on the "not scary" end of the scale. Close, but not quite so safe,
is running off front of an array. At the other end is using a freed
pointer which is almost always disastrous.

However...

In practice, implementations are not going to add code to play
bounds-cop on you when you are clearly well within program-owned
memory. My own view, therefore, is that the behaviour is undefined in
theory but well-defined in practice. In theory, theory and practice
should be the same, but in practice we know that they aren't.

The real worry for me is that a compiler will use the UB to come to some
conclusion that I, as the programmer, disagree with. It would be within
it's rights to conclude that

two_d_array[0][20] = 42;

does not change two_d_array[2][0] and so the register copy will do. I
would not be 100% sure than nothing like that could happen. For that
reason alone I consider it undefined in practice as well as in theory.
 
S

Shao Miller

That does not clear up the question of whether, in your opinion, a
result can be defined because the type is defined (and nothing more).
Obviously I'm a "no" on that and I don't think any more debate will be
useful so a simple "yes" or "no" from you will be enough to end this
topic.
Yes. Apologies for not abiding by the request for nothing further,
but: In the case of casting to 'void', the result is well-defined
(empty set of values, since it's type 'void') and a type for the
expression before evaluation is not explicitly defined. I'm going to
assume the latter to be ridiculous due to the extremely common usage
of casting to void not yielding UB. Somewhere, we must infer the type
for the expression regardless of the result of evaluation, in order
for it to qualify as a void expression.
OK.  This whole thread will be wrapped up in no time!
That is a desirable outcome.
... ... ...
If you called is an "unsurpassed guide" I suspect some people would say you
are being too generous.  Calling it "no more than a guide" is another
matter.  I defined the word only because I suspected it was not quite
what you meant.  The word itself is not the problem.
Very well.
I must have omitted that the path would be:
1. Read the material
2. Apply reasoning, logic, common sense
3. In case of ambiguity, find out how others interpret
For me, 3 is not possible without 2.  Ambiguity cannot be determined
without having applied these things.  Would you agree?

Yes, with the refinement that 1, 2 and 3 iterate.  The effect is a
careful consideration of the arguments and reasoning of others. Agreed.


 This is
very different to what I take the phrase "popular consensus" to mean.
For example, I'd bet that the popular consensus amongst C programmers is
that

  int two_d_array[10][10];
  two_d_array[0][20] = 42;

is well-defined, but it's hard to find well-reasoned arguments from C
experts to support that view.
Aha. My take would make good use of 6.5.2.1,p2:

"The definition of the subscript operator [] is that E1[E2] is
identical to (*((E1)+(E2)))."

So 'two_d_array[0][20]' becomes (ugh) '(*((*((two_d_array)+(0)))
+(20)))', evaluation of which proceeds as follows:
1. 'two_d_array' slides easily through its parentheses
'(*((*(two_d_array+(0)))+(20)))', 6.5.1,p6.
2. 'two_d_array' is not the operand to 'sizeof' nor '&', so it becomes
an expression having type pointer-to-int[20], pointing to the first
element, per 6.3.2.1,p3. 'sizeof two_d_array[0]' should confirm this.
3. '0' easily slides out of its parentheses.
4. We add '0' to the pointer result in step 2. The declaration
provided an array object with 10 array-of-int objects. The element
with offset 0 is within the bounds (+1) and we have a result with type
pointer-to-int[20]. '(*((*(result))+(20)))' per 6.5.6,p8.
5. The result of step 4 easily slides. '(*((*result)+(20)))'
6. The unary '*' operator yields a result with type int[20].
'(*((result)+(20)))', per 6.5.3.2,p4.
7. The result from step 6 slides. '(*(result+(20)))'
8. So does '20'. '(*(result+20))'
9. The result of step 6 is not an operand to 'sizeof' or '&', has
array type, and is thus an expression having type pointer-to-int,
congruent with step 2's reference.
10. There is an 'int' object within the bounds of the array object at
offset 20, a pointer to this object is the result, congruent with step
4's reference. '(*(result))'
11. The result of step 10 slides. '(*result)'
12. The unary '*' operator yields a result having type 'int' and which
is an lvalue, congruent with the reference from step 6. '(result)'
13. The result of step 12 slides. It's still an lvalue. 'result'
14. The rest is assignment to that lvalue.
Where along here is there sometimes debate, if we can digress for at
least a moment? :)
[We see to agree about constant expressions and null pointer constants]
Sure.
Yes, I did mean that I believe it should be added explicitly.  The
additional constraint results in an implementation being capable of
diagnosing it at translation-time.  That is valuable.  Undefined
behaviour by the last sentence is during evaluation.  Consider
'sizeof'.

I think I now understand what you are proposing but I had to go back and
forth though the thread finding this spot in each message to see what
I'd said and what you'd then replied.  I now don't have the energy say
much more than if you want a constraint against * being applied to a
null pointer constant, go for it.  I don't mind one way or the other.
The effort in proposing a change to the standard calls seems to demand a
more significant benefit, but I won't be doing it.
I will keep in mind your comments regarding "snipping" and the
resulting energy it can waste for another poster.

Thanks, Ben!
 
B

Ben Bacarisse

Shao Miller said:
It's not a pointer to a function-type, either. If it were, '*' would
yield a function designator with type 'void', which doesn't appear to
make sense, either. Was there a reason you chose to treat 'void *' to
most closely resemble pointer-to-an-object-type versus pointer-to-a-
function-type, here? I'm guessing that you figured it was more
obvious that 'void *' is not a pointer-to-a-function-type.

Yes. I took it for granted that you did not think it might be a
function pointer.

Why have you omitted a function designator here, again? Because it's
too obvious?
Yes.

Also, why must the two "ifs" imply that the type of operand cannot be
'void *'? Here's the text of 6.5.3.2,p4:

"The unary * operator denotes indirection. If the operand points to a
function, the result is a function designator; if it points to an
object, the result is an lvalue designating the object. If the operand
has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the
unary * operator is undefined."

Are you suggesting that according to the above, the lack of "...; if
it has type ‘‘pointer to void’’, the result has no value and the
expression is a void expression..." is what would be needed for my
suggested interpretation to be valid; the omission of such thus
resulting in no definition for the behaviour? I'm trying to pinpoint
why you consider this to not be a natural consequence of "If the
operand has type..." combined with "The (nonexistent) value of a void
expression (an expression that has type void)..." from 6.3.2.2,p1.

Let me try again. All I am saying is that *(void *)E is undefined
because a void * is neither an object pointer nor a function pointer.
If one were unsure about this, both possibilities give rise to absurd
conclusions. A void * can't be a function pointer for the rather silly
reason that void is not a function. Some people might think it is an
object pointer, but that would mean * of it would be an lvalue in
contradiction of 6.3.2.1 p1.

There is no need to go this far: types are partitioned into three
groups: incomplete types, object types and function types, but some
people might be more persuaded by looking at the consequences of it
being either a function pointer or an object pointer.
Should the constraint "The operand of the unary * operator shall have
pointer type." from 6.5.3.2,p2" be interpreted to mean "The operand of
the unary * operator shall have a pointer type suitable either for
pointing to an object or for pointing to a function." due to the
sentence "If the operand points to..." but _not_ due to the next
sentence "If the operand has type..."?

I don't think there is any reason to interpret the constraint any more
narrowly than its literal meaning.
Well, there's also the alternative to "shutting up" that is helping me
to understand your interpretation.

Of course, but I'm saying that I don't want to keep going round and
round and I can't see any way to make progress. You don't even have to
say where you stand on this, I'm going to shut up about it one way or
the other.

"Cast operators", 6.5.4:

"...Constraints

2 Unless the type name specifies a void type, the type name shall
specify qualified or
unqualified scalar type and the operand shall have scalar type.

3 Conversions that involve pointers, other than where permitted by the
constraints of
6.5.16.1, shall be specified by means of an explicit cast.

Semantics

4 Preceding an expression by a parenthesized type name converts the
value of the
expression to the named type. This construction is called a cast.89) A
cast that specifies
no conversion has no effect on the type or value of an expression.

5 If the value of the expression is represented with greater precision
or range than required
by the type named by the cast (6.3.1.8), then the cast specifies a
conversion even if the
type of the expression is the same as the named type."

Where do we see a definition of the result of casting to 'void',
here? Is it not omitted just the same as in the unary '*' operator?
Does that mean that casting to 'void' yields undefined behaviour? I
should think not for the latter, as I'm positive you'd agree.

That section does not define the result of any cast operator. All it
says is that the cast performs a conversion. There is a whole section
on conversions that gives the details. Conversion to void is in
6.3.2.2.
Ok. I concede this. A prize is not defined for the loser. They have
what they had before the contest was judged, a Ford with an empty set
of physical components. This is not quite the same as having a
prize.

It seems we disagree about even this. No matter; it was but an analogy
and they often fail to communicate the same thing to different people.
So again to cast operators, where is the result of casting to
'void' defined?

See above.

<snip>
 
B

Ben Bacarisse

Shao Miller said:
On Jul 25, 9:02 pm, Ben Bacarisse <[email protected]> wrote:
Yes.

Lets agree to differ then.

 This is
very different to what I take the phrase "popular consensus" to mean.
For example, I'd bet that the popular consensus amongst C programmers is
that

  int two_d_array[10][10];
  two_d_array[0][20] = 42;

is well-defined, but it's hard to find well-reasoned arguments from C
experts to support that view.
Aha. My take would make good use of 6.5.2.1,p2:

"The definition of the subscript operator [] is that E1[E2] is
identical to (*((E1)+(E2)))."

So 'two_d_array[0][20]' becomes (ugh) '(*((*((two_d_array)+(0)))
+(20)))', evaluation of which proceeds as follows:
1. 'two_d_array' slides easily through its parentheses
'(*((*(two_d_array+(0)))+(20)))', 6.5.1,p6.
2. 'two_d_array' is not the operand to 'sizeof' nor '&', so it becomes
an expression having type pointer-to-int[20], pointing to the first
element, per 6.3.2.1,p3. 'sizeof two_d_array[0]' should confirm this.
3. '0' easily slides out of its parentheses.
4. We add '0' to the pointer result in step 2. The declaration
provided an array object with 10 array-of-int objects. The element
with offset 0 is within the bounds (+1) and we have a result with type
pointer-to-int[20]. '(*((*(result))+(20)))' per 6.5.6,p8.
5. The result of step 4 easily slides. '(*((*result)+(20)))'
6. The unary '*' operator yields a result with type int[20].
'(*((result)+(20)))', per 6.5.3.2,p4.
7. The result from step 6 slides. '(*(result+(20)))'
8. So does '20'. '(*(result+20))'
9. The result of step 6 is not an operand to 'sizeof' or '&', has
array type, and is thus an expression having type pointer-to-int,
congruent with step 2's reference.
10. There is an 'int' object within the bounds of the array object at
offset 20, a pointer to this object is the result, congruent with step
4's reference. '(*(result))'
11. The result of step 10 slides. '(*result)'
12. The unary '*' operator yields a result having type 'int' and which
is an lvalue, congruent with the reference from step 6. '(result)'
13. The result of step 12 slides. It's still an lvalue. 'result'
14. The rest is assignment to that lvalue.
Where along here is there sometimes debate, if we can digress for at
least a moment? :)

I don't think there is much debate. If there is any, I think it is
about what array to use as the bounds.

I got lost in your explanation because you have phrases like "having
type pointer-to-int[20]" and "a result with type int[20]". I'm not sure
what these mean. The last is a C type but it does not occur in the
evaluation so I think you meant something else by it.

<snip>
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
474,095
Messages
2,570,616
Members
47,232
Latest member
helpplease!

Latest Threads

Top