Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing std::vector elements via pointers vs end()

I need to be able to access (read-only, no resizing involved or anything like that) the elements of a an std::vector via pointers. E.g.,

std::vector<int> foo(10);
int *ptr_begin = &foo[0];

So far so good, this is guaranteed to work in the current standard (23.3.6.1):

The elements of a vector are stored contiguously, meaning that if v is a vector where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size().

So we can access all elements of the vector using pointers, since they are stored in a contiguous chunk of memory. But what about the one-past-the-last element? I mean, it is legal to perform this operation?

int *ptr_end = ptr_begin + foo.size()

(Note, I am not trying to access the past-the-last value, merely to define a pointer to it - an equivalent to foo.end() if you like). The standard mentions only accessing elements via pointer arithmetics, but clearly here we are not accessing any element.

(As a side note, the definition of the existence of a past-the-last something seems tightly connected to the base concept of array (see for instance 5.7/5), but throughout the standard it seems like the concepts of contiguous storage and array are used interchangeably. Am I reading wrong?)

like image 373
bluescarni Avatar asked May 13 '14 15:05

bluescarni


1 Answers

Yes, as long as you only use ptr_end in comparisons* and don't attempt to deference it, this is fine. Quoting from § 5.7 in the C++11 draft standard regarding additive operations with pointers (emphasis mine):

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

Similar provisions are listed for the relational operators in § 5.9:

If two pointers point to elements of the same array or one beyond the end of the array, the pointer to the object with the higher subscript compares higher.

As to whether vector's buffer counts as an array for the purposes of the above, § 8.3.4 states:

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

which is consistent with what § 23.3.6.1 has to say about vector:

The elements of a vector are stored contiguously


Since pointers are iterators, this sort of thing is a handy trick for using standard library algorithms with an arbitrary block of memory as the input. For example, say you want to use the lower_bound algorithm, but your data is stored in an MFC CArray:

CArray<int> someInts;
// populate and check for empty
int* begin = someInts.GetData();
int* end = begin + someInts.GetSize(); // for bounds-checking only; don't dereference
int* answer = std::lower_bound(begin, end, 100);

*There are a couple other operations that are legal too; e.g. since you know your vector isn't empty, you could subtract one to get a pointer to the last element. The important thing is don't dereference.

like image 155
dlf Avatar answered Sep 24 '22 08:09

dlf