1

I can't convince myself that it is safe to store a pointer to a vector.end() (i.e whatever vector end() iterator returns)

Data is

std::vector<unsigned char> data;
auto end = &(*data.end());
block b(end, size);
blocks.push_back(b);
data.insert(data.end(), (unsigned char*) ptr_data, (unsigned char*) ptr_data + size);

My question is. Can this address change ( &*(data.end() ), with a condition that vector capacity pre-allocate and the client will never insert more than vector supposes to store.

So it should be a continuous region in memory, so my assumption that vector will not do any memove operation and therefore I can store pointers that will point to different elements. (so here block store a pointer to whatever data.end() point to (before insertion to data vector) and metadata about variable size data inside that data vector.

Thank you.

Johnny Bonelli
  • 101
  • 1
  • 8
  • 3
    `auto end = &(*data.end())` -- It is undefined behavior to dereference the `end` iterator. For example, this code causes an `assert()` to fire when run under Visual Studio. [See this](https://stackoverflow.com/questions/26965621/behavior-when-dereferencing-the-end-of-a-vector-of-strings) – PaulMcKenzie Dec 19 '19 at 19:49
  • 1
    Just store the offset instead of a pointer or iterator and you won't have to worry about it – Kevin Dec 19 '19 at 19:53
  • 1
    Instead of `auto end = &(*data.end());` use `auto end = data.data() + data.size();` – Galik Dec 19 '19 at 19:53
  • that was a concern : ( I guess it all depends on how vector pre-allocate capacity. if memory pre-allocated at given capacity by the allocator, it shouldn't point to something outside) – Johnny Bonelli Dec 19 '19 at 19:55
  • @Kevin you right it probably a better solution. But I was not really sure about invalidation for fixed capacity vector. Vector does own memory management so I was not sure what it guarantees. – Johnny Bonelli Dec 19 '19 at 19:58

1 Answers1

3

It should be safe to store an iterator such as std::vector::end()as long as it doesn't get invalidated. There is a long list of rules for invalidation, but the gist of it (as it relates to your example) is that doing this is okay, until you hit push_back():

blocks.push_back(b);

This call invalidates the iterator because it adjusts its size. You will need to get blocks.end() again for it to be valid.


One thing to note is that you probably shouldn't be dereferencing end() though. It is unsafe. However, the use case you have is very similar to this problem with regular arrays. That addresses regular arrays, an not vectors, but I'd imagine it'd be very similar.

The long story short is if you take the address of the 1 past end of the array element, it may or may be considered undefined behavior and is considered by some as a defect in the standard.

Instead, you don't really need to store the address of the dereferenced iterator, just store the iterator:

auto end = data.end();

Iterators are designed to work similarly to pointers, so there is no real reason to get the address of one here.