开发者

std::copy and std::vector problem

开发者 https://www.devze.com 2023-01-24 14:54 出处:网络
I understand why this causes a segfault: #include <algorithm> #include <vector> using namespace std;

I understand why this causes a segfault:

#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;
    int iArr[5] = {1, 2, 3, 4, 5};
    int *p = iArr;

    copy(p, p+5, v.begin());

    return 0;
}

But why does this not cause a segfault?

#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;
    int iArr[5] = {1, 2, 3, 4, 5};
    int *p = iArr;

    v.reserve(1);
    copy(p, p+5, v.b开发者_Python百科egin());

    return 0;
}


Both are wrong as you are copying to empty vector and copy requires that you have space for insertion. It does not resize container by itself. What you probably need here is back_insert_iterator and back_inserter:

copy(p, p+5, back_inserter(v));


This is undefined behavior - reserve() allocates a buffer for at least one element and the element is left uninitialized.

So either the buffer is big enough and so you technically can access elements beyond the first one or it is not big enough and you just happen to not observe any problems.

The bottom line is - don't do it. Only access elements that are legally stored in the vector instance.


But why does this not cause a segfault?

Because the stars aligned. Or you were running in debug and the compiler did something to "help" you. Bottom line is you're doing the wrong thing, and crossed over in to the dark and nondeterministic world of Undefined Behavior. You reserve one spot in the vector and then try to cram 5 elements in to the reserve-ed space. Bad.

You have 3 options. In my personal order of preference:

1) Use a back_insert_iterator which is designed for just this purpose. It is provided by #include <iterator>. The syntax is a bit funky, but fortunately a nice sugar-coated shortcut, back_inserter is also provided:

#include <iterator>
// ...
copy( p, p+5, back_inserter(v) );

2) assign the elements to the vector. I prefer this method slightly less simply because assign is a member of vector, and that strikes me as slightly less generic than using somethign from algorithm.

v.assign(p, p+5);

3) reserve the right number of elements, then copy them. I consider this to be a last ditch effort in case everything else fails for whatever reason. It relies on the fact that a vector's storage is contiguous so it's not generic, and it just feels like a back-door method of getting the data in to the vector.


This is wrong! it is undefined behavior to access memory you don't own, even if it works in an example. The reason, I think, is that std::vector would reserve more than one element.


Because you were unlucky. Accessing memory not allocated is UB.


Most likely because an empty vector doesn't have any memory allocated at all, so you are trying to write to a NULL pointer which normally leads to an instant crash. In the second case it has at least some memory allocated, and you are most likely overwriting the end of an array which may or may not lead to a crash in C++.

Both are wrong.


It would be wrong, by the way, even to copy 1 element to the vector that way (or to reserve 5 then copy that way).

The reason it most likely does not segfault is that the implementor felt it would be inefficient to allocate the memory for just 1 element just in case you wanted to grow it later, so maybe they allocated enough for 16 or 32 elements.

Doing reserve(5) first then writing into 5 elements directly would probably not be Undefined Behaviour but would be incorrect because vector will not have a logical size of 5 yet and the copy would almost be "wasted" as vector will claim to still have the size of 0.

What would be valid behaviour is reserve(5), insert an element, store its iterator somewhere, insert 4 more elements and look at the contents of the first iterator. reserve() guarantees that the iterators do not become invalidated until the vector exceeds that size or a call such as erase(), clear(), resize() or another reserve() is made.

0

精彩评论

暂无评论...
验证码 换一张
取 消