Topic: Is reading a dangling pointer defined?


Author: thp@cs.ucr.edu
Date: Fri, 14 May 2004 16:44:08 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
: On Fri, 7 May 2004 17:52:26 +0000 (UTC), thp@cs.ucr.edu wrote:
:
:> John Potter <jpotter@falcon.lhup.edu> wrote:
:> : On Thu, 6 May 2004 17:30:48 +0000 (UTC), thp@cs.ucr.edu wrote:
:> :
:> :> But IIRC the actual prohibition (via undefined behavior) is stated in
:> :> terms of reading/writing invalid values from/to lvalues, and "this" is
:> :> not an lvalue.
:> :
:> : The words are, "The effect of using an invalid pointer value ... is
:> : undefined."  Nothing about lvalue there.
:
:> Where does it say that?
:
: 3.7.3.2/4.

Thanks.

[...]
:> I have to point out that both K&R and TC++PL imply that "pointers" are
:> "lvalues", and in the case at hand "foo()" is not an lvalue.  (But I
:> think we've had that conversation here before.)
:
: I would agree that a "pointer" is a variable.  The key part in the above
: is "pointer value".  It is amusing that 5.3.1/1 states that the unary *
: operator must be applied to a pointer.  That makes *foo() ill-formed if
: pointers are lvalues.  I think we need to allow a bit of slop here.

Unfortunately, that bit of slop seems to cause needless confusion
especially among beginners.  Many people use the term "address" when
speaking of a pointer value and the term "pointer" when speaking of an
address-valued object.

Tom Payne













































---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Fri, 7 May 2004 17:52:26 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
: On Thu, 6 May 2004 17:30:48 +0000 (UTC), thp@cs.ucr.edu wrote:
:
:> But IIRC the actual prohibition (via undefined behavior) is stated in
:> terms of reading/writing invalid values from/to lvalues, and "this" is
:> not an lvalue.
:
: The words are, "The effect of using an invalid pointer value ... is
: undefined."  Nothing about lvalue there.

Where does it say that?  In fact, the standard supports the use of
post-destruction "this" as an argument to placement-new.  (It would
take me a while to find it, but James Kanze pointed it out to me, and
I have read it.)

:> AFAIK, in
:>
:>             int* foo() { int i=0; return i; }
:
: I assume that you intended return &i;

Oops! Yes.

:>             int main() { *foo(); }
:
:> "foo()" is an invalid pointer rvalue whose evaluation yields defined
:> behavior.
:
: I guess that
:   foo();
: may have defined behavior; however, there is no doubt that
:   if (foo() == 0)
: has undefined behavior.

I think that's correct.

: The above dereference is also undefined.

That's certainly the question.  Does the prohibition on "use" of
"invalid pointers" include dereferencing a dangling pointer rvalue,
however it was produced.

I have to point out that both K&R and TC++PL imply that "pointers" are
"lvalues", and in the case at hand "foo()" is not an lvalue.  (But I
think we've had that conversation here before.)

: One thing that undefined behavior allows is an implementation of
: an interpreter which detects it and aborts the program.  I think
: the abort would be conforming in both the == and dereference cases.

That would be my preference in both cases, but the possibility of
run-time checking seems a touchy issue in discussions of the C and/or
C++ standards, even in cases where the overhead is visibly negligible.

Tom Payne

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 10 May 2004 07:35:55 +0000 (UTC)
Raw View
On Fri, 7 May 2004 17:52:26 +0000 (UTC), thp@cs.ucr.edu wrote:

> John Potter <jpotter@falcon.lhup.edu> wrote:
> : On Thu, 6 May 2004 17:30:48 +0000 (UTC), thp@cs.ucr.edu wrote:
> :
> :> But IIRC the actual prohibition (via undefined behavior) is stated in
> :> terms of reading/writing invalid values from/to lvalues, and "this" is
> :> not an lvalue.
> :
> : The words are, "The effect of using an invalid pointer value ... is
> : undefined."  Nothing about lvalue there.

> Where does it say that?

3.7.3.2/4.

> In fact, the standard supports the use of
> post-destruction "this" as an argument to placement-new.  (It would
> take me a while to find it, but James Kanze pointed it out to me, and
> I have read it.)

A pointer to a destructed object is still a valid non-dereferenceable
pointer.  You can look at the value.  We are talking about a pointer
which has an invalid value because the memory went away.

> I have to point out that both K&R and TC++PL imply that "pointers" are
> "lvalues", and in the case at hand "foo()" is not an lvalue.  (But I
> think we've had that conversation here before.)

I would agree that a "pointer" is a variable.  The key part in the above
is "pointer value".  It is amusing that 5.3.1/1 states that the unary *
operator must be applied to a pointer.  That makes *foo() ill-formed if
pointers are lvalues.  I think we need to allow a bit of slop here.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Sun, 2 May 2004 19:45:35 +0000 (UTC)
Raw View
llewelly <llewelly.at@xmission.dot.com> wrote:
[...]
: But there is an open DR, 312
:    (http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#312)
:    about the meaning of using an invalid pointer. The standard never
:    defines what it means to 'use' a pointer.
:
: Except for the wording in 3.7.3.2/4, AFAICT, the standard never
:    defines what an 'invalid pointer' is. And that's what I would
:    like to see. A definition of 'invalid pointer'. Then it would no
:    longer be necessary for 3.7.3.2/4 to mention 'use' of an invalid
:    pointer, and no longer necessary to define 'use'.

Among the candidates for "uses" are:
 - comparisons, arithmetic, and function operations on pointer values.
 - storing a pointer value into a pointer-valued object
 - reading a pointer value from a pointer-valued object (i.e., lvalue-
   to-rvalue conversion)
 - dereferencing a pointer value
 - lvalue-to-rvalue conversion of a dereferenced pointer

There was a recent thread on comp.lang.c++.mod to the effect that
dereferencing the null pointer value yields defined behavior.  It
would be simplest if forming invalid lvalues doesn't yield undefined
behavior, but converting them to rvalues does.

Tom Payne

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Sun, 2 May 2004 20:27:58 +0000 (UTC)
Raw View
On Sat, 1 May 2004 16:48:32 +0000 (UTC), llewelly.at@xmission.dot.com
(llewelly) wrote:

> jpotter@falcon.lhup.edu (John Potter) writes:

> > On Fri, 30 Apr 2004 05:48:05 +0000 (UTC), thp@cs.ucr.edu wrote:

> >> I've been told that merely reading a pointer object that points to a
> >> now-deleted object yields undefined behavior.  If that's so, I'd
> >> appreciate a reference to the appropriate clause.

> > 3.7.3.2/4 may be what you seek.  An rvalue conversion is a use.

> Would you please say where you found this? Does it mean DR 325 may be
>     closed as NAD?

You must be talking about Core issue 312.

The first example is not an rvalue conversion and I agree that it is not
a use.  The second part seems to be confused.  It gives an example of
dereferencing a deleted pointer to call the destructor of the deleted
item and claims that is required in clearing a container of pointers.
It is the destructor of the pointer not the pointee that is called by
clear.  Since the destructor of a pointer is a noop, it is not an use.

struct S {   // our list node for list of pointers
    S* next;
    int* p;
    };
typedef int* IntStar;
int main () {
    int* p;
    S s = { &s, new int(0) };
    delete s.p;
    } // s.p is destroyed here with well defined behavior
      // Invalid p destroyed here with well defined behavior

The most sensible thing that I can find is in the iterator clause.
24.1/5 nicely says that the only operation allowed on a singular
iterator is assignment of a new value.  The singular iterator is
compared to an unitialized pointer which is invalid like a dangling
pointer.

Until the issue is resolved, common sense will be used.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: llewelly.at@xmission.dot.com (llewelly)
Date: Mon, 3 May 2004 04:47:24 +0000 (UTC)
Raw View
jpotter@falcon.lhup.edu (John Potter) writes:

> On Sat, 1 May 2004 16:48:32 +0000 (UTC), llewelly.at@xmission.dot.com
> (llewelly) wrote:
>
>> jpotter@falcon.lhup.edu (John Potter) writes:
>
>> > On Fri, 30 Apr 2004 05:48:05 +0000 (UTC), thp@cs.ucr.edu wrote:
>
>> >> I've been told that merely reading a pointer object that points to a
>> >> now-deleted object yields undefined behavior.  If that's so, I'd
>> >> appreciate a reference to the appropriate clause.
>
>> > 3.7.3.2/4 may be what you seek.  An rvalue conversion is a use.
>
>> Would you please say where you found this? Does it mean DR 325 may be
>>     closed as NAD?
>
> You must be talking about Core issue 312.

Yes, I was talking about issue 312. 325 is unrelated, and calling it
    a DR is just a bad habit of mine. Thank you for taking the
    trouble to understand my post despite typos. :-)

> The first example is not an rvalue conversion and I agree that it is not
> a use.  The second part seems to be confused.  It gives an example of
> dereferencing a deleted pointer to call the destructor of the deleted
> item and claims that is required in clearing a container of pointers.
> It is the destructor of the pointer not the pointee that is called by
> clear.  Since the destructor of a pointer is a noop, it is not an use.
>
> struct S {   // our list node for list of pointers
>     S* next;
>     int* p;
>     };
> typedef int* IntStar;
> int main () {
>     int* p;
>     S s = { &s, new int(0) };
>     delete s.p;
>     } // s.p is destroyed here with well defined behavior
>       // Invalid p destroyed here with well defined behavior
>
> The most sensible thing that I can find is in the iterator clause.
> 24.1/5 nicely says that the only operation allowed on a singular
> iterator is assignment of a new value.  The singular iterator is
> compared to an unitialized pointer which is invalid like a dangling
> pointer.

Yes, this makes sense, but it is in an example, which I thought was
    non-normative. And it seems to be the only reference to a
    'singular value of a pointer'. singular is defined for
    iterators. 24.1/[12] says iterators are a generalization of
    pointers, but I seem to recall there was some dispute here about
    that.

There is somewhat related library issue 407 which is about whether or
    not singular iterators can be destroyed, related to example 2 in
    issue 312, though as you say, a trivial destructor is not a use,
    so perhaps it is only an issue for iterators with non-trivial
    destructors.

> Until the issue is resolved, common sense will be used.

I can't recall that it has ever caused a problem in real code. I've
    never had weird behavior due to an invalid/singular pointer being
    destroyed or assigned a new value.

There are two other operations I sometimes need to perform on invalid
    pointers: address-of and binding a reference to it.

    int* p;
    int** pp= &p; //Should be well-defined.
    int*& r= p; //Should also be well-defined.

These don't involve rvalue conversions, so they aren't uses by your
    definition. So I like your definition of 'use'. (And it seems
    natural in view of my admittedly limited understanding of typical
    c++ implementations.) It does seem like a natural way to resolve
    issue 312 . But you didn't say where in the standard you found
    wording which explicitly says an rvalue conversion is a use. So
    does such wording need to be added?


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Mon, 3 May 2004 05:32:12 +0000 (UTC)
Raw View
llewelly <llewelly.at@xmission.dot.com> wrote:
[...]
: I can't recall that it has ever caused a problem in real code. I've
:    never had weird behavior due to an invalid/singular pointer being
:    destroyed or assigned a new value.

: There are two other operations I sometimes need to perform on invalid
:    pointers: address-of and binding a reference to it.
:
:    int* p;
:    int** pp= &p; //Should be well-defined.
:    int*& r= p; //Should also be well-defined.

: These don't involve rvalue conversions, so they aren't uses by your
:    definition. So I like your definition of 'use'. (And it seems
:    natural in view of my admittedly limited understanding of typical
:    c++ implementations.) It does seem like a natural way to resolve
:    issue 312.

: But you didn't say where in the standard you found
:    wording which explicitly says an rvalue conversion is a use. So
:    does such wording need to be added?

IMHO, whether or not the object is of pointer type, rvalue conversion,
i.e., reading the object's value, is the only operation that should be
sensitive to the object's value.  Taking the object's address and/or
assigning the object a new value shouldn't be sensitive to the
object's current value.  But I don't know whether or where the
Standard says that.

Tom Payne





















---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net (James Kuyper)
Date: Mon, 3 May 2004 15:07:41 +0000 (UTC)
Raw View
thp@cs.ucr.edu wrote in message news:<c6truk$duk$1@glue.ucr.edu>...
> John Potter <jpotter@falcon.lhup.edu> wrote:
> : On Fri, 30 Apr 2004 05:48:05 +0000 (UTC), thp@cs.ucr.edu wrote:
> :
> :> I've been told that merely reading a pointer object that points to a
> :> now-deleted object yields undefined behavior.  If that's so, I'd
> :> appreciate a reference to the appropriate clause.
> :
> : 3.7.3.2/4 may be what you seek.  An rvalue conversion is a use.
>
> Thanks.  So, if I understand correctly:
..
>    - Dereferencing a dangling pointer rvalue yields an lvalue and
>      has defined runtime behavior.

No - dereferencing a pointer is also a use of that pointer's value. In
fact, it's the prototypical use of a pointer. The single most common
mistake when reading that clause is to think it applies only to
dereferencing the pointer.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 3 May 2004 16:35:23 +0000 (UTC)
Raw View
On Sat, 1 May 2004 16:47:30 +0000 (UTC), thp@cs.ucr.edu wrote:

> John Potter <jpotter@falcon.lhup.edu> wrote:
> : On Fri, 30 Apr 2004 05:48:05 +0000 (UTC), thp@cs.ucr.edu wrote:

> :> I've been told that merely reading a pointer object that points to a
> :> now-deleted object yields undefined behavior.  If that's so, I'd
> :> appreciate a reference to the appropriate clause.

> : 3.7.3.2/4 may be what you seek.  An rvalue conversion is a use.

> Thanks.  So, if I understand correctly:

>    - The lvalue-to-rvalue conversion of a pointer lvalue denoting a
>      pointer object that contains a dangling value yields undefined
>      behavior.  (In practice, the actual conversion is a syntactic
>      operation that takes place at compile time.  It generates code
>      that gets executed at runtime.)

I think an lvalue to rvalue conversion is a memory read.

>    - Dereferencing a dangling pointer rvalue yields an lvalue and
>      has defined runtime behavior.

Since you can't get a dangling rvalue without undefined behavior,
this is meaningless.

>    - But lvalue-to-rvalue conversion of a dereferenced dangling
>      pointer yields undefined behavior.

And more meaningless.

I thought about giving examples of what the implementation might
do to detect use of a dangling pointer, but that would just be
some form of defining the undefined behavior.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 3 May 2004 16:35:29 +0000 (UTC)
Raw View
On Sat, 1 May 2004 16:52:39 +0000 (UTC), thp@cs.ucr.edu wrote:

> It appears that, contrary to popular opinion (and my former opinion),
> dereferencing an invalid pointer rvalue is not a "use" of that
> pointer.

I know of no way to get an invalid pointer rvalue without undefinded
behavior.

> Apparently, the thing that I have to bear in mind and teach my
> students is that going from a pointer object to the rvalue of the
> object it points to involves three steps:

>   1) lvalue-to-rvalue conversion of an lvalue that designates that
>      pointer object

If you have undefined behavior here, the rest is nonsense.

>   2) dereferencing the resulting pointer rvalue

>   3) lvalue-to-rvalue conversion of the lvalue resulting from
>      dereferencing that pointer rvalue.

> Both 1) and 3) involve runtime behavior that is undefined.  But 2)
> involves no runtime behavior and, therefore, no undefined behavior,
> e.g., "*(int*)0" is a well-defined lvalue whose lvalue-to-rvalue
> conversion would yield undefined behavior.  Thus

You are using the null pointer value which is a valid not invalid
value.  Dereferencing a null pointer is undefined behavior which has
nothing to do with invalid pointer values.

>    int main() {
>      if ( 0 ) *(int*)0;
>    }

> has defined behavior

By that logic, *(int*)0 = 42 has defined behavior because it does
not perform an lvalue to rvalue conversion on the lvalue resulting
from the dereference.

Obtaining an invalid pointer rvalue is always undefined behavior.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net (James Kuyper)
Date: Mon, 3 May 2004 16:36:10 +0000 (UTC)
Raw View
llewelly.at@xmission.dot.com (llewelly) wrote in message news:<86oep68b6x.fsf@Zorthluthik.local.bar>...
..
> Yes, this makes sense, but it is in an example, which I thought was
>     non-normative. And it seems to be the only reference to a
>     'singular value of a pointer'. singular is defined for
>     iterators. 24.1/[12] says iterators are a generalization of
>     pointers, but I seem to recall there was some dispute here about
>     that.

It's easy to check: a pointer satisfies every single requirement
needed to qualify as a random-access iterator. This is a matter of
design, not a coincidence.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: llewelly.at@xmission.dot.com (llewelly)
Date: Tue, 4 May 2004 15:30:39 +0000 (UTC)
Raw View
kuyper@wizard.net (James Kuyper) writes:

> llewelly.at@xmission.dot.com (llewelly) wrote in message news:<86oep68b6x.fsf@Zorthluthik.local.bar>...
> ..
>> Yes, this makes sense, but it is in an example, which I thought was
>>     non-normative. And it seems to be the only reference to a
>>     'singular value of a pointer'. singular is defined for
>>     iterators. 24.1/[12] says iterators are a generalization of
>>     pointers, but I seem to recall there was some dispute here about
>>     that.
>
> It's easy to check: a pointer satisfies every single requirement
> needed to qualify as a random-access iterator. This is a matter of
> design, not a coincidence.

This isn't what was disputed. The disputed statement was 'iterators
    are generalization of pointers'. See: http://xrl.us/by4y . No issue
    was filed as far as I know, but it was suggested. (And contrary to
    what I posted earlier, the dispute was in comp.lang.c++.moderated,
    not here.)




---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Tue, 4 May 2004 18:02:04 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
: On Sat, 1 May 2004 16:52:39 +0000 (UTC), thp@cs.ucr.edu wrote:
:
:> It appears that, contrary to popular opinion (and my former opinion),
:> dereferencing an invalid pointer rvalue is not a "use" of that
:> pointer.
:
: I know of no way to get an invalid pointer rvalue without undefinded
: behavior.

Hmmmm.  Does:

         delete this;
         *this;

invoke undefined behavior.

:> Apparently, the thing that I have to bear in mind and teach my
:> students is that going from a pointer object to the rvalue of the
:> object it points to involves three steps:
:
:>   1) lvalue-to-rvalue conversion of an lvalue that designates that
:>      pointer object
:
: If you have undefined behavior here, the rest is nonsense.

Yup.

:>   2) dereferencing the resulting pointer rvalue
:
:>   3) lvalue-to-rvalue conversion of the lvalue resulting from
:>      dereferencing that pointer rvalue.
:
:> Both 1) and 3) involve runtime behavior that is undefined.  But 2)
:> involves no runtime behavior and, therefore, no undefined behavior,
:> e.g., "*(int*)0" is a well-defined lvalue whose lvalue-to-rvalue
:> conversion would yield undefined behavior.  Thus
:
: You are using the null pointer value which is a valid not invalid
: value.  Dereferencing a null pointer is undefined behavior which has
: nothing to do with invalid pointer values.

See comp.lang.c++.moderated, "dereferencing a null pointer is not
undefined.", for credible claims to the contrary.  You're much more
qualified than I to evaluate/refute those claims.

:>    int main() {
:>      if ( 0 ) *(int*)0;
:>    }
:
:> has defined behavior
:
: By that logic, *(int*)0 = 42 has defined behavior because it does
: not perform an lvalue to rvalue conversion on the lvalue resulting
: from the dereference.

I think that that the claim that "*(int*)0=42" has defined behavior
would be closer to the claim that "if(1)*(int*)0;" has defined
behavior.  Even so, I think that evaluating "*(int*)0=42" yields
undefined behavior because it attempts to assign a value to an invalid
lvalue, while "if(1)*(int*)0;" has defined value by the arguments
cited in the comp.lang.c++.moderated thread cited above.

Tom Payne


























---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Wed, 5 May 2004 16:48:24 +0000 (UTC)
Raw View
On Tue, 4 May 2004 18:02:04 +0000 (UTC), thp@cs.ucr.edu wrote:

> John Potter <jpotter@falcon.lhup.edu> wrote:
> : On Sat, 1 May 2004 16:52:39 +0000 (UTC), thp@cs.ucr.edu wrote:

> :> It appears that, contrary to popular opinion (and my former opinion),
> :> dereferencing an invalid pointer rvalue is not a "use" of that
> :> pointer.

> : I know of no way to get an invalid pointer rvalue without undefinded
> : behavior.

> Hmmmm.  Does:

>          delete this;
>          *this;

> invoke undefined behavior.

Hmmmmmmmmmmm.  Interesting.  We have an rvalue which did not come from
an lvalue.  No lvalue to rvalue conversion.  Does:

   this;

invoke undefined behavior?  Since it is an rvalue, it could exist in
an address register on those machines which use them and have trap
values.  It seems that the rvalue expression "this" invokes undefined
behavior when that value is invalid.

> See comp.lang.c++.moderated, "dereferencing a null pointer is not
> undefined.", for credible claims to the contrary.

Looks like it may still be an open question.  The issues are not yet
closed.

> :>    int main() {
> :>      if ( 0 ) *(int*)0;
> :>    }

> :> has defined behavior

> : By that logic, *(int*)0 = 42 has defined behavior because it does
> : not perform an lvalue to rvalue conversion on the lvalue resulting
> : from the dereference.

> I think that that the claim that "*(int*)0=42" has defined behavior
> would be closer to the claim that "if(1)*(int*)0;" has defined
> behavior.

Yes.  No undefined behavior for code which is not executed.

> "if(1)*(int*)0;" has defined value by the arguments
> cited in the comp.lang.c++.moderated thread cited above.

I wonder what dereferencing means on a machine with address registers.
If an lvalue were a reference, we would have just another name for a
pointer rvalue and the "lvalue" could be placed in an address register.
I guess the "lvalue" zero must be allowed in those registers.  Nothing
new here.  Null and past the end values are valid rvalues.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net (James Kuyper)
Date: Wed, 5 May 2004 16:48:33 +0000 (UTC)
Raw View
llewelly.at@xmission.dot.com (llewelly) wrote in message news:<86u0yxyptu.fsf@Zorthluthik.local.bar>...
> kuyper@wizard.net (James Kuyper) writes:
>
> > llewelly.at@xmission.dot.com (llewelly) wrote in message news:<86oep68b6x.fsf@Zorthluthik.local.bar>...
> > ..
> >> Yes, this makes sense, but it is in an example, which I thought was
> >>     non-normative. And it seems to be the only reference to a
> >>     'singular value of a pointer'. singular is defined for
> >>     iterators. 24.1/[12] says iterators are a generalization of
> >>     pointers, but I seem to recall there was some dispute here about
> >>     that.
> >
> > It's easy to check: a pointer satisfies every single requirement
> > needed to qualify as a random-access iterator. This is a matter of
> > design, not a coincidence.
>
> This isn't what was disputed. The disputed statement was 'iterators
>     are generalization of pointers'. See: http://xrl.us/by4y . No issue
>     was filed as far as I know, but it was suggested. (And contrary to
>     what I posted earlier, the dispute was in comp.lang.c++.moderated,
>     not here.)

I don't see the distinction you're making. All pointers are iterators.
Some iterators are pointers; some are not. That's precisely what
"iterators are a generalization of pointers" means to me. Does it mean
something else to you?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Thu, 6 May 2004 17:30:48 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
: On Tue, 4 May 2004 18:02:04 +0000 (UTC), thp@cs.ucr.edu wrote:
:
:> John Potter <jpotter@falcon.lhup.edu> wrote:
:> : On Sat, 1 May 2004 16:52:39 +0000 (UTC), thp@cs.ucr.edu wrote:
:
:> :> It appears that, contrary to popular opinion (and my former opinion),
:> :> dereferencing an invalid pointer rvalue is not a "use" of that
:> :> pointer.
:
:> : I know of no way to get an invalid pointer rvalue without undefinded
:> : behavior.
:
:> Hmmmm.  Does:
:
:>          delete this;
:>          *this;
:
:> invoke undefined behavior.
:
: Hmmmmmmmmmmm.  Interesting.  We have an rvalue which did not come from
: an lvalue.  No lvalue to rvalue conversion.  Does:
:
:   this;
:
: invoke undefined behavior?  Since it is an rvalue, it could exist in
: an address register on those machines which use them and have trap
: values.  It seems that the rvalue expression "this" invokes undefined
: behavior when that value is invalid.

If I understand the concept of "trap value" correctly, it is (as you
imply) a programming-language manifestation of the fact that some
registers trap when an invalid value is read from or written to them.
But IIRC the actual prohibition (via undefined behavior) is stated in
terms of reading/writing invalid values from/to lvalues, and "this" is
not an lvalue.

AFAIK, in

            int* foo() { int i=0; return i; }
            int main() { *foo(); }

"foo()" is an invalid pointer rvalue whose evaluation yields defined
behavior.  If so, the next question would be whether evaluating
"*foo()" yields defined behavior.

:> See comp.lang.c++.moderated, "dereferencing a null pointer is not
:> undefined.", for credible claims to the contrary.
:
: Looks like it may still be an open question.  The issues are not yet
: closed.
:
:> :>    int main() {
:> :>      if ( 0 ) *(int*)0;
:> :>    }
:
:> :> has defined behavior
:
:> : By that logic, *(int*)0 = 42 has defined behavior because it does
:> : not perform an lvalue to rvalue conversion on the lvalue resulting
:> : from the dereference.
:
:> I think that that the claim that "*(int*)0=42" has defined behavior
:> would be closer to the claim that "if(1)*(int*)0;" has defined
:> behavior.
:
: Yes.  No undefined behavior for code which is not executed.
:
:> "if(1)*(int*)0;" has defined value by the arguments
:> cited in the comp.lang.c++.moderated thread cited above.
:
: I wonder what dereferencing means on a machine with address registers.

Whatever "dereferencing" means should include that case.

: If an lvalue were a reference, we would have just another name for a
: pointer rvalue and the "lvalue" could be placed in an address register.

That the way I think of it, i.e., dereferencing is in effect
"pointer-to-reference conversion".  Before dereferencing the register
contains a pointer to whatever; after dereferencing that same register
contains a reference to whatever.  No bits get changed.  The program
simply views the bit pattern in that register as having slightly
different significance.

: I guess the "lvalue" zero must be allowed in those registers.  Nothing
: new here.  Null and past the end values are valid rvalues.

FWIW, I think of references as the run-time values of lvalue.
(lvalues, being expressions, exist only at compile time but they
have runtime values.)

Tom Payne





































---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Thu, 6 May 2004 22:30:19 +0000 (UTC)
Raw View
On Thu, 6 May 2004 17:30:48 +0000 (UTC), thp@cs.ucr.edu wrote:

> But IIRC the actual prohibition (via undefined behavior) is stated in
> terms of reading/writing invalid values from/to lvalues, and "this" is
> not an lvalue.

The words are, "The effect of using an invalid pointer value ... is
undefined."  Nothing about lvalue there.

> AFAIK, in
>
>             int* foo() { int i=0; return i; }

I assume that you intended return &i;

>             int main() { *foo(); }

> "foo()" is an invalid pointer rvalue whose evaluation yields defined
> behavior.

I guess that
   foo();
may have defined behavior; however, there is no doubt that
   if (foo() == 0)
has undefined behavior.  The above dereference is also undefined.

One thing that undefined behavior allows is an implementation of
an interpreter which detects it and aborts the program.  I think
the abort would be conforming in both the == and dereference cases.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Fri, 30 Apr 2004 05:48:05 +0000 (UTC)
Raw View
I've been told that merely reading a pointer object that points to a
now-deleted object yields undefined behavior.  If that's so, I'd
appreciate a reference to the appropriate clause.

Thanks,
Tom Payne

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Fri, 30 Apr 2004 13:59:43 +0000 (UTC)
Raw View
On Fri, 30 Apr 2004 05:48:05 +0000 (UTC), thp@cs.ucr.edu wrote:

> I've been told that merely reading a pointer object that points to a
> now-deleted object yields undefined behavior.  If that's so, I'd
> appreciate a reference to the appropriate clause.

3.7.3.2/4 may be what you seek.  An rvalue conversion is a use.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: llewelly.at@xmission.dot.com (llewelly)
Date: Fri, 30 Apr 2004 16:34:11 +0000 (UTC)
Raw View
thp@cs.ucr.edu writes:

> I've been told that merely reading a pointer object that points to a
> now-deleted object yields undefined behavior.  If that's so, I'd
> appreciate a reference to the appropriate clause.
[snip]

ISTM the relevant section is 3.7.3.2/4 :

    # If the argument given to a deallocation function in the standard
    # library is a pointer that is not the null pointer value (4.10), the
    # deallocation function shall deallocate the storage referenced by the
    # pointer, rendering invalid all pointers referring to any part of the
    # deallocated storage. The effect of using an invalid pointer value
    # (including passing it to a deallocation function) is
    # undefined. (33)

Footnote (33) :

    # On some implementations, it causes a system-generated runtime
    # fault.

5.3.5/4 contains equivalent wording:

    # [ ... ] If the delete-expression calls the implementation
    # deallocation function (3.7.3.2), and if the operand of the
    # delete expression is not the null pointer constant, the
    # deallocation function will deallocate the storage referenced by
    # the pointer thus rendering the pointer invalid. [Note: The value
    # of a pointer that refers to deallocated storage is
    # indeterminate.]

But there is an open DR, 312
    (http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#312)
    about the meaning of using an invalid pointer. The standard never
    defines what it means to 'use' a pointer.

Except for the wording in 3.7.3.2/4, AFAICT, the standard never
    defines what an 'invalid pointer' is. And that's what I would
    like to see. A definition of 'invalid pointer'. Then it would no
    longer be necessary for 3.7.3.2/4 to mention 'use' of an invalid
    pointer, and no longer necessary to define 'use'.



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hyrosen@mail.com (Hyman Rosen)
Date: Fri, 30 Apr 2004 16:34:17 +0000 (UTC)
Raw View
thp@cs.ucr.edu wrote:
> I've been told that merely reading a pointer object that points to a
> now-deleted object yields undefined behavior.  If that's so, I'd
> appreciate a reference to the appropriate clause.

3.7.3.2/4

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net (James Kuyper)
Date: Fri, 30 Apr 2004 16:34:26 +0000 (UTC)
Raw View
thp@cs.ucr.edu wrote in message news:<c6qs8f$jg0$1@glue.ucr.edu>...
> I've been told that merely reading a pointer object that points to a
> now-deleted object yields undefined behavior.  If that's so, I'd
> appreciate a reference to the appropriate clause.

3.7.3.2p4: "If the argument given to a deallocation function in the
standard library is a pointer that is not the null pointer value
(4.10), the deallocation function shall deallocate the storage
referenced by the pointer, rendering invalid all pointers referring to
any part of the _deallocated storage_. The effect of using an invalid
pointer value (including passing it to a deallocation function) is
undefined. 23)"

Note: "using" doesn't just mean dereferencing it. Any use of the value
of an invalid pointer has undefined behavior, including simply copying
it to a new location. However, note that it is the value that matters,
not the copying:

int *pi = new int;
int *qi;
delete pi;
memcpy(&qi, &pi, sizeof(qi));
memcmp(&qi, &pi, sizeof(qi));
qi == pi; // undefined
qi = pi; // undefined


The memcpy() and memcmp() calls never examines the pointer value of pi
itself. memcpy() examines each of the bytes of 'pi', and uses the
value of that byte, interpreted as an unsigned char, to perform the
copy. memcmp() does the same, to perform the comparison.

The reason for this rule is that there are real machines (including
many of the most popular), that will abort a program if it attempts to
load an invalid address value into an address register. This is a
deliberate safety measure, based upon the judgement that it's probably
safer to terminate immediately any program malfunctioning that
severely, than to let it continue running. The "=" and "==" operators
might reasonably be implemented by passing the address values through
an address register. There are real machines where "delete pi;" could
be implemented in such a way that it might end up deallocating the
last portion of a block of memory which it then returns to the
operating system; addresses pointing into that block would become
invalid addresses, at least for this process. Clause 3.7.3.2p4 is the
one that would allow such an implementation to be conforming.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Fri, 30 Apr 2004 16:34:32 +0000 (UTC)
Raw View
thp@cs.ucr.edu wrote:

> I've been told that merely reading a pointer object that points to a
> now-deleted object yields undefined behavior.  If that's so, I'd
> appreciate a reference to the appropriate clause.
>

3.8/5 (object lifetime)

Alberto

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Sat, 1 May 2004 16:47:30 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
: On Fri, 30 Apr 2004 05:48:05 +0000 (UTC), thp@cs.ucr.edu wrote:
:
:> I've been told that merely reading a pointer object that points to a
:> now-deleted object yields undefined behavior.  If that's so, I'd
:> appreciate a reference to the appropriate clause.
:
: 3.7.3.2/4 may be what you seek.  An rvalue conversion is a use.

Thanks.  So, if I understand correctly:

   - The lvalue-to-rvalue conversion of a pointer lvalue denoting a
     pointer object that contains a dangling value yields undefined
     behavior.  (In practice, the actual conversion is a syntactic
     operation that takes place at compile time.  It generates code
     that gets executed at runtime.)

   - Dereferencing a dangling pointer rvalue yields an lvalue and
     has defined runtime behavior.

   - But lvalue-to-rvalue conversion of a dereferenced dangling
     pointer yields undefined behavior.

Tom Payne

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: llewelly.at@xmission.dot.com (llewelly)
Date: Sat, 1 May 2004 16:48:32 +0000 (UTC)
Raw View
jpotter@falcon.lhup.edu (John Potter) writes:

> On Fri, 30 Apr 2004 05:48:05 +0000 (UTC), thp@cs.ucr.edu wrote:
>
>> I've been told that merely reading a pointer object that points to a
>> now-deleted object yields undefined behavior.  If that's so, I'd
>> appreciate a reference to the appropriate clause.
>
> 3.7.3.2/4 may be what you seek.  An rvalue conversion is a use.
[snip]

Would you please say where you found this? Does it mean DR 325 may be
    closed as NAD?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: thp@cs.ucr.edu
Date: Sat, 1 May 2004 16:52:39 +0000 (UTC)
Raw View
James Kuyper <kuyper@wizard.net> wrote:
: thp@cs.ucr.edu wrote in message news:<c6qs8f$jg0$1@glue.ucr.edu>...
:> I've been told that merely reading a pointer object that points to a
:> now-deleted object yields undefined behavior.  If that's so, I'd
:> appreciate a reference to the appropriate clause.
:
: 3.7.3.2p4: "If the argument given to a deallocation function in the
: standard library is a pointer that is not the null pointer value
: (4.10), the deallocation function shall deallocate the storage
: referenced by the pointer, rendering invalid all pointers referring to
: any part of the _deallocated storage_. The effect of using an invalid
: pointer value (including passing it to a deallocation function) is
: undefined. 23)"
:
: Note: "using" doesn't just mean dereferencing it. Any use of the value
: of an invalid pointer has undefined behavior, including simply copying
: it to a new location. However, note that it is the value that matters,
: not the copying:
:
: int *pi = new int;
: int *qi;
: delete pi;
: memcpy(&qi, &pi, sizeof(qi));
: memcmp(&qi, &pi, sizeof(qi));
: qi == pi; // undefined
: qi = pi; // undefined
:
:
: The memcpy() and memcmp() calls never examines the pointer value of pi
: itself. memcpy() examines each of the bytes of 'pi', and uses the
: value of that byte, interpreted as an unsigned char, to perform the
: copy. memcmp() does the same, to perform the comparison.
:
: The reason for this rule is that there are real machines (including
: many of the most popular), that will abort a program if it attempts to
: load an invalid address value into an address register. This is a
: deliberate safety measure, based upon the judgement that it's probably
: safer to terminate immediately any program malfunctioning that
: severely, than to let it continue running. The "=" and "==" operators
: might reasonably be implemented by passing the address values through
: an address register. There are real machines where "delete pi;" could
: be implemented in such a way that it might end up deallocating the
: last portion of a block of memory which it then returns to the
: operating system; addresses pointing into that block would become
: invalid addresses, at least for this process. Clause 3.7.3.2p4 is the
: one that would allow such an implementation to be conforming.

Thanks.  That gets right to the heart of the matter.

It appears that, contrary to popular opinion (and my former opinion),
dereferencing an invalid pointer rvalue is not a "use" of that
pointer.  And, come to think of it, *dereferencing* is a compile-time
operation that generates no run-time behavior, so it's unlikely that
its run-time behavior would be undefined.  But, ascertaining the value
of a pointer object (say for purposes of comparison or for copying it
to another object) is lvalue-to-rvalue conversion, which involves a
run-time fetch operation and can, therefore, have undefined behavior.
The same holds for the lvalue resulting from dereferencing an invalid
rvalue --- its lvalue-to-rvalue conversion yields undefined behavior.

Apparently, the thing that I have to bear in mind and teach my
students is that going from a pointer object to the rvalue of the
object it points to involves three steps:

  1) lvalue-to-rvalue conversion of an lvalue that designates that
     pointer object

  2) dereferencing the resulting pointer rvalue

  3) lvalue-to-rvalue conversion of the lvalue resulting from
     dereferencing that pointer rvalue.

Both 1) and 3) involve runtime behavior that is undefined.  But 2)
involves no runtime behavior and, therefore, no undefined behavior,
e.g., "*(int*)0" is a well-defined lvalue whose lvalue-to-rvalue
conversion would yield undefined behavior.  Thus

   int main() {
     if ( 0 ) *(int*)0;
   }

has defined behavior, while

   int main() {
     if ( 1 ) *(int*)0;
   }

has behavior that is undefined.

Thanks again.

Tom Payne

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]