Re: UB while dealing with invalid raw pointers, the std::uninitialized_fill
case
* 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.
[snip]
and accordingly, would that mean that the standard needs
to be modified to state these actions (copying and comparing of invalid
pointers) as well-defined?
I dont't think so.
If the standard was all too clear about everything then we'd have
nothing to discuss.
That doesn't really seem a good reason to keep a self-contradicting standard (if
it really is the case). I'd like to think that you're just kidding :-)
He he. :-)
Only partially... ;-)
Cheers & hth.,
- Alf
--
blog at <url: http://alfps.wordpress.com>