meds
meds

Reputation: 22956

vector pointer locations guaranteed?

Suppose I have a vector of ints,

std::vector<int> numbers;

that is populated with a bunch of values, then I say do this (where an entry exists at 43)

int *oneNumber = &numbers[43];

Is oneNumber guaranteed to always be pointing at the int at index 43, even if say I resize numbers to something like numbers.resize(46)?

I'm not 100% sure what expected behaviour is here, I know vectors are guaranteed to be contiguous but not sure if that continuity would also mean all the indices in the vector will remain in the same place throughout its life.

Upvotes: 8

Views: 3495

Answers (7)

bobobobo
bobobobo

Reputation: 67266

As all the others have said, when you call .resize() on a vector your pointers become invalidated because the (old array) may be completely deallocated, and an entirely new one may be re-allocated and your data copied into it.

One workaround for this is don't store pointers into an STL vector. Instead, store integer indices.

So in your example,

std::vector<int> numbers;
int *oneNumber = &numbers[43]; // no. pointers invalidated after .resize or possibly .push_back.
int oneNumberIndex = 43 ;      // yes. indices remain valid through .resize/.push_back

Upvotes: 4

benlong
benlong

Reputation: 678

Once you changed the capacity of the vector, the data was copied to another memory block, and the origin data is deleted.

Upvotes: 2

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153945

Pointers, references, and iterators to std::vector elements are guaranteed to stay put as long as you only append to the std::vector and the size of the std::vector doesn't grow beyond its capacity() at the time the pointer, reference, or iterator was obtained. Once it gets resized beyond the capacity() all pointers, references, and iterators to this std::vector become invalidated. Note that things are invalidated as well when inserting somewhere else than the end of the std::vector.

If you want to have your objects stay put and you only insert new elements at the end or the beginning, you can use std::deque. Pointers and references to elements in the std::deque get only invalidated when you insert into the middle of the std::deque or when removing from the middle or when removing the referenced object. Note that iterators to elements in the std::deque get invalidated every time you insert an element into the std::deque or remove any element from it.

Upvotes: 4

pinerd314159
pinerd314159

Reputation: 544

When you use a vector's resize() or reserve() function to increase the capacity of the vector, it may need to reallocate memory for the array-backing. If it does reallocate, the new memory will not be located at the same address, so the address stored in oneNumber will no longer point to the right place.

Again, this depends on how many elements the vector is currently being used to store and the requested size. Depending on the specifics, the vector may be able to resize without reallocating, but you should definitely not assume that this will be the case.

Upvotes: 2

chrisaycock
chrisaycock

Reputation: 37928

Your paranoia is correct. Resizing a std::vector can cause its memory location to change. That means your oneNumber is now pointing to an old memory location that has been freed, and so accessing it is undefined behavior.

Upvotes: 4

Mysticial
Mysticial

Reputation: 471369

Is oneNumber guaranteed to always be pointing at the int at index 43

Yes, this is guaranteed by the standard.

even if say I resize numbers to something like numbers.resize(46)?

No. Once you resize, add, or remove anything to the vector, all addresses and iterators to it are invalidated. This is because the vector may need to be reallocated with new memory locations.

Upvotes: 9

Adrian Cornish
Adrian Cornish

Reputation: 23876

No - the vector can be reallocated when it grows. Usually once the vector doubles in size.

From the C++11 standard

1 Remarks: Causes reallocation if the new size is greater than the old capacity. If no
reallocation happens, all the iterators and references before the insertion point
remain valid. If an exception is thrown other than by the copy constructor, move 
constructor, assignment operator, or move assignment operator of T or by any 
InputIterator operation there are no effects. If an exception is thrown by the move 
constructor of a non-CopyInsertable T, the effects are unspecified.

Upvotes: 2

Related Questions