Alf P. Steinbach /Usenet <alf.p.steinbach+usenet@gmail.com>, on
* Francesco S. Carta, on 03.09.2010 15:06:
Alf P. Steinbach /Usenet <alf.p.steinbach+usenet@gmail.com>, on
03/09/2010
13:28:40, wrote:
* Francesco S. Carta, on 03.09.2010 12:52:
Hi there,
as far as I've been able to understand, if a raw pointer contains an
invalid
value (that is, it does not point to any valid object of the type it
is a
pointer to) then some of the actions performed on these pointers will
lead to UB.
Hm, well you need to define "invalid" more precisely, e.g. as "is not
valid". ;-) Or even more precisely, "can not be dereferenced without
UB". For example, 0 is a valid pointer value, as is 1+p where p points
to the last element in an array.
Neither C++98 nor C++0x does, as far as I know, define "invalid
pointer", but C++0x defines "valid pointer" as pointing to a byte in
memory or being zero, in C++0x ?2.9.2/3. The definition in C++0x is
perhaps too permissive. If taken literally the validity of a pointer
would in general not be deducible but would depend on whether the
address in question had been remapped by the HW, e.g. p would be
/valid/
immediately after delete p unless the delete affected the validity of
the address itself (e.g. by changing paging or segment setup).
But OK...
OK, I've read your self-follow-up, just for the records. Correctly
defining an
invalid pointer seems to be impossible, but we have some agreed cases
of valid
and invalid pointer values:
- the null-pointer value is a valid and non-dereferenceable value;
- the address of a valid object is a valid pointer value;
- the address of a valid object becomes an invalid pointer value after
the
object gets destroyed;
- the value of an uninitialized pointer is an invalid pointer value and,
according to the following, it also is a singular pointer value:
Yah, mostly that was my reasoning in my old "pointers tutorial"
(referenced from my blog, right hand column somewhere).
I introduced the concept of "RealGood" pointers there.
But unfortunately that term did not catch on.
[snip]
Here we come to the point, assume this program, which should be
well-defined and
well-behaving:
//-------
#include <iostream>
#include <memory>
using namespace std;
int main() {
size_t n = 4;
int* start = static_cast<int*>(
operator new(n * sizeof(int))
);
int* end = start + n;
uninitialized_fill(start, end, 42);
for(int* i = start; i < end; ++i) {
cout << *i << endl;
}
operator delete(start);
return 0;
}
//-------
By the time "start" gets initialized, it points to an uninitialized
storage area
big enough to hold an int, but since that storage is uninitialized,
the pointer
is currently invalid (we cannot dereference it without invoking UB).
"end" is an
invalid pointer too.
Let's now enter the uninitialized_fill template function.
It gets called with this pseudo-signature:
uninitialized_fill<int*, int>(...)
which means that in the "Expected" section cited above, we have:
for (; first != last; ++first)
where "first == start" and "last == end", and all of them are of type
"class
ForwardIterator = int*"
Following from all the above, we should have a standard algorithm that
invokes
UB by comparing two invalid pointers.
Where is my reasoning flawed?
The pointers are not invalid. They can be dereferenced. What you can't
do is to invoke an rvalue conversion on *p, because that would use an
indeterminate value. p itself is valid, *p is a valid reference, (*p)+2,
for example, is bad.
All right... I think I'm starting to understand. The difference between
Now my question is, would the following test also lead to an lvalue
to rvalue
conversion on "p", therefore leading to UB?
int* p = new int;
delete p;
int* q = new int;
if(q != p) {
//...
}
Yes, this invokes rvalue conversion and UB.