is &(vector[0]) always valid?

J

John Salmon

I was under the impression that the following was always valid:

std::vector<T> v;
..
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of

&(*(v.begin() + 0))

which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

Thanks,
John Salmon
 
G

Gianni Mariani

John said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

It may work on your platform, however the C++ standard says it's
undefined. Hence, it may work for you today but it may also break tomorrow.
If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

Right.
 
A

Alan Johnson

John said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of

&(*(v.begin() + 0))

which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

Thanks,
John Salmon

v[0] is NOT valid for an empty vector, and therefore &v[0] is not valid
either. You are right about the operational semantics, but neither is
that expression valid. If the vector is empty, then begin() == end(),
and you are therefore "dereferencing" the end() iterator.

As for the alternate expression you propose, using empty() rather than
size() would be more idiomatic, and might be more efficient, depending
on the implementation:

T *p = v.empty() ? 0 : &(v[0]) ;
 
C

Carl Barron

John Salmon said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.
its not valid if v.size()==0.
template <class T,class A>
inline T *data(std::vector<T,A> &v)
{
return v.empty() ? 0: &v[0];
}
is an easy work around. Recent drafts of the standard provide
vector with a data() member function which does this, but your
implementation probably does not.
 
K

Kai-Uwe Bux

John said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

If v is empty, once you evaluate v[0] you have undefined behavior. More
importantly, an implementation of std::vector<> is perfectly free and maybe
even well-advised to do something like:

T& operator[] ( size_type i ) {
assert( i < this->size() );
return( this->data );
}

Because of the assert(), your program could crash depending on compilation
options.
If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of

&(*(v.begin() + 0))

which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

Why do you insist on using T* anyway? What is the reason not to use
std::vector<T>::iterator?


Best

Kai-Uwe Bux
 
R

Roland Pibinger

So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

What would be the address of the first element of an empty vector?
 
A

Andrew Koenig

I was under the impression that the following was always valid:
std::vector<T> v;
T *p = &(v[0]);
But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

Whoever told you that is correct.
I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

Sorry to dash your hopes, but...
If the above isn't ok, I'd have to write:
T *p = (v.size()!=0)? &(v[0]) : 0;
Right.


I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of

&(*(v.begin() + 0))

which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

You are. v.begin() + 0 is always equivalent to v.begin(), but &(*v.begin())
is undefined if v is empty.
 
F

Francis Glassborow

John Salmon said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

You were correctly informed. The problem is that v[0] has undefined
behaviour if there are no elements. That means that &(v[0]) has
undefined behaviour. This is not that unreasonable because an empty
vector does not have a location for any element so there will be no
address.

Changing this would have required a special rule for &(v[0]). Instead
you must write something like:

T *p = v.size() ? &(v[0]) : 0;

If there is any possibility that v is an empty vector. (There are
several variations on the above definition but I tend to use a limited
subset of the member functions of vector rather than remember the exact
spelling of all of them.)
 
J

James Kanze

John said:
I was under the impression that the following was always valid:
std::vector<T> v;
.
T *p = &(v[0]);
But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.
I'm hoping that the above expression is fine, even if v is empty,

It's not. It's undefined behavior, and will core dump on some
implementations. (It core dumps with g++ 4.0.1, for example, or
with VC++ 8.)
but that if you try to dereference p, you're in trouble.
If the above isn't ok, I'd have to write:
T *p = (v.size()!=0)? &(v[0]) : 0;
I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of
&(*(v.begin() + 0))

No. The expression a[n] is the equivalent of *(v.begin() + 0)
(which is presumably the equivalent of *v.begin()). If
v.begin() == v.end(), however, *v.begin() is undefined behavior.
which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

I'd say that the ice has already broken. Your code causes a
run-time error (core dump, or its moral equivalent under
Windows) with two of the three compilers I regularly use.
So, can I write:
T *p = &(v[0]);
or do I have to write:
T *p = (v.size()!=0)? &(v[0]) : 0;

The latter.

Given the particular guarantee for std::vector, I wonder if an
additional member function, data() might not be in order. A
function which could be required to return a null pointer if the
vector is empty.
 
H

Howard Hinnant

I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of

&(*(v.begin() + 0))

which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

You have to write the latter (unfortunately). It is the dereference
caused by v[0] which is the trouble maker.

Fwiw the working draft for C++0X has added a new member function to
vector so that you no longer have to do this:

T *p = v.data();

Reference LWG 464:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#464

Your vendor may or may not yet support this new member as C++0X isn't
official yet.

-Howard
 
J

Jorgen Grahn

["Followup-To:" header set to comp.lang.c++.]

I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

Or when v.empty().

..
or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

That, or:

T *p = v.empty()? 0: &(v[0]);

std::vector<T>::size() is constant time, I think, but that's not the case
for other containers. So when you really want to test for (non-)emptiness,
it's good to make a habit of using empty().

/Jorgen
 
W

whyglinux

John said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of

&(*(v.begin() + 0))

which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

Thanks,
John Salmon

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:[email protected] ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

Yes, it's undefined.

v[0] is equivalent to *v.begin(). Whether v is an empty vector or not,
v.begin() returns a valid iterator. But if v is empty, v.begin(), like
v.end(), is not a valid dereferenceable iterator, so *v.begin() is
undefined.

Note though, &(*(v.begin() + 0)) is not, as you said, "operationally
equivalent" to v.begin() because v.begin() returns an object of the
vector<T>'s iterator type while &(*(v.begin() + 0)) should return a
type of pointer to T (since operator * is overloaded here), and the
iterator type needs not to be the same as a pointer to T.
 
L

Lynn

std::vector said:
.
T *p = &(v[0]);

This works in Visual C++ 2003. It crashes when the size is
zero in Visual C++ 2005. I fixing this all over our code right
now.
But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

Yes, true.
If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

Yes, this should work OK.

Lynn
 
D

david.hinkes

John said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);


I've often had this question myself. Even if this worked, isn't it
_very_ dangerous? Consider the following:

std::vector<T> v(1);

T dummy;

v.push_back(dummy);

T * p = &(v[0]);


v.push_back(dummy);

v.push_back(dummy);

// does p still point to anything valid at this point? Since the
push_back operations, perhaps
// internally the vector resized itself. I wouldn't expect that the
vector object keep the same internal
// memory.

T first_element = p[0]; //is p still valid?

// Thoughts?

Dave
 
J

James Dennett

John said:
I was under the impression that the following was always valid:

std::vector<T> v;
.
T *p = &(v[0]);


I've often had this question myself. Even if this worked, isn't it
_very_ dangerous? Consider the following:

std::vector<T> v(1);

T dummy;

v.push_back(dummy);

T * p = &(v[0]);


v.push_back(dummy);

v.push_back(dummy);

// does p still point to anything valid at this point? Since the
push_back operations, perhaps
// internally the vector resized itself. I wouldn't expect that the
vector object keep the same internal
// memory.

T first_element = p[0]; //is p still valid?

// Thoughts?

This is spelled out quite clearly; after a push_back that
can cause a vector to grow beyond its capacity, iterators
and pointers to its old elements are invalidated. In your
example, p is invalidated by the first call to v.push_back
after p was initialized.

It's dangerous is people don't know the rules on when
iterators and pointers are invalidated, but those rules
are vital to safe use of containers in any case.

Further discussion on this line is probably best removed
from comp.std.c++; followups set.

-- James
 
A

Alf P. Steinbach

* John Salmon:
So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

The latter.

§23.1/7 "If the container is empty, then 'begin() == end()'".
§24.1/5 Explaining that one-past-the-end iterators are past-the-end
values, then "The library never assumes that past-the-end values are
dereferencable".

Hence v[0] by equivalence to &(*(begin()+0)) performs an invalid
dereferencing when the vector is empty. In more practical terms, the
vector need not have a buffer if it's empty. But I suspect that with
most compiler and standard library implementations (all?) you can get
away with it if the vector's /capacity/ is greater than zero.
 
C

Clark S. Cox III

John said:
I was under the impression that the following was always valid:

std::vector<T> v;
..
T *p = &(v[0]);

But I was recently told that care was needed in case the vector was
empty, i.e., when v.size() == 0.

You were told correctly. Calling v[x], where x is >= v.size() is
undefined behavior; period.
I'm hoping that the above expression is fine, even if v is empty, but
that if you try to dereference p, you're in trouble.

If the above isn't ok, I'd have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

I took a look in the C++98 standard, and couldn't find anything to
confirm my hope. I see from Table 68 that the expression has the
"operational sematics" of

&(*(v.begin() + 0))

which sure *seems* like it might in turn be "operationally equivalent"
to v.begin(), which sure seems safe, even for an empty vector. But I
now feel myself to be on very thin ice.

So, can I write:

T *p = &(v[0]);

or do I have to write:

T *p = (v.size()!=0)? &(v[0]) : 0;

You have to write the later (or some variation thereof).
 
J

James Kanze

Alf P. Steinbach said:
* John Salmon:
So, can I write:
T *p = &(v[0]);
or do I have to write:
T *p = (v.size()!=0)? &(v[0]) : 0;
The latter.
§23.1/7 "If the container is empty, then 'begin() == end()'".
§24.1/5 Explaining that one-past-the-end iterators are past-the-end
values, then "The library never assumes that past-the-end values are
dereferencable".
Hence v[0] by equivalence to &(*(begin()+0)) performs an invalid
dereferencing when the vector is empty. In more practical terms, the
vector need not have a buffer if it's empty. But I suspect that with
most compiler and standard library implementations (all?) you can get
away with it if the vector's /capacity/ is greater than zero.

Changing the capacity doesn't make it work with g++ nor with
VC++. There are thus at least two major implementations (the
two most widely used?) where it actually does fail at runtime.
 
P

peter koch

James said:
Alf P. Steinbach said:
* John Salmon:
So, can I write:
T *p = &(v[0]);
or do I have to write:
T *p = (v.size()!=0)? &(v[0]) : 0;
The latter.
§23.1/7 "If the container is empty, then 'begin() == end()'".
§24.1/5 Explaining that one-past-the-end iterators are past-the-end
values, then "The library never assumes that past-the-end values are
dereferencable".
Hence v[0] by equivalence to &(*(begin()+0)) performs an invalid
dereferencing when the vector is empty. In more practical terms, the
vector need not have a buffer if it's empty. But I suspect that with
most compiler and standard library implementations (all?) you can get
away with it if the vector's /capacity/ is greater than zero.

Changing the capacity doesn't make it work with g++ nor with
VC++. There are thus at least two major implementations (the
two most widely used?) where it actually does fail at runtime.

Which is logical. I do not know of a hardware platform where
&(*begin()) would physically access memory, so I'd expect &(*begin())
to return a pointer in all cases when the implementation does not
perform some kind of explicit check. And that check would be related
to size, not capacity.

/Peter
 

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

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top