70

I would like to be able to convert between std::vector and its underlying C array int* without explicitly copying the data.

Does std::vector provide access to the underlying C array? I am looking for something like this

vector<int> v (4,100)
int* pv = v.c_array();

EDIT:

Also, is it possible to do the converse, i.e. how would I initialize an std::vector from a C array without copying?

int pv[4] = { 4, 4, 4, 4};
vector<int> v (pv);
James McNellis
  • 348,265
  • 75
  • 913
  • 977
D R
  • 21,936
  • 38
  • 112
  • 149
  • There is a 'problem' with this: int pv[4] = { 4, 4, 4, 4}; vector v (pv); it actually copies the contents of pv into v ... you just have to be aware of that – fho Oct 08 '10 at 08:32

5 Answers5

94

You can get a pointer to the first element as follows:

int* pv = &v[0];

This pointer is only valid as long as the vector is not reallocated. Reallocation happens automatically if you insert more elements than will fit in the vector's remaining capacity (that is, if v.size() + NumberOfNewElements > v.capacity(). You can use v.reserve(NewCapacity) to ensure the vector has a capacity of at least NewCapacity.

Also remember that when the vector gets destroyed, the underlying array gets deleted as well.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 8
    "so long as you do not add additional elements to the vector" - without reserving space first. If you `reserve()`, then you can add elements up to the capacity you reserved, and guarantee that references and iterators are still valid. – Steve Jessop Nov 14 '09 at 04:05
  • 2
    @Steve: Good point. Just be sure you reserve() before you get the pointer! :) – Drew Hall Nov 14 '09 at 04:23
  • @Steve: That's true, though I think I'd find it a tad disconcerting if I saw code inserting elements into a vector _and_ using pointers to elements in the vector that were obtained before the insertion. Still, I've modified that paragraph to try and more clearly state when reallocation happens. – James McNellis Nov 14 '09 at 05:14
  • Thanks for your answer. What about the reverse conversion? How can I initialize an `std::vector` from a C array `int*` without copying? – D R Nov 14 '09 at 22:37
  • 7
    The reverse isn't possible; the STL containers manage their own memory. You can't create a vector and have it manage some array that you allocated elsewhere. The easiest way to _copy_ an array into a vector would be to use `std::vector v(&pv[0], &pv[4]);`, using the example you added to your question. – James McNellis Nov 15 '09 at 00:52
  • 5
    I think it's actually too bad that vector doesn't have a consuming constructor that would let it take ownership of an existing array provided the length. Makes interop with C libraries harder. – Joseph Garvin Jun 26 '12 at 17:09
  • 1
    @JosephGarvin: The [`array_ref` and `string_ref` class templates](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3334.html) are very helpful for this purpose (neither is standard--yet, and I don't know of any open source implementations with those exact interfaces, but I have an [`enhanced_cstring` class template](http://cxxreflect.codeplex.com/SourceControl/changeset/view/1e9bfce95843#cxxreflect%2fcore%2fstring.hpp) in the Boost-licensed CxxReflect library that has been most useful). – James McNellis Jun 26 '12 at 17:11
  • I prefer to use `&v.front()` (which is the same as `&*v.begin()` but looks less redundant). This saves having to add 0 to `begin()` before dereferencing. The compiler may or may not be able to optimize away the addition. – Bulletmagnet Aug 19 '15 at 12:15
36

In c++11, you can use vector::data() to get C array pointer.

m.elahi
  • 691
  • 6
  • 9
21
int* pv = &v[0]

Note that this is only the case for std::vector<>, you can not do the same with other standard containers.

Scott Meyers covers this topic extensively in his books.

Ðаn
  • 10,934
  • 11
  • 59
  • 95
  • "you can not do the same with other standard containers" - IIRC you will be able to do it with string in C++0x, and in practice pretty much every implementation does actually guarantee that string's storage is contiguous. – Steve Jessop Nov 14 '09 at 04:07
  • 3
    You can get a read-only array containing the elements of a `std::string` using its `c_str()` or `data()` members. Because of this, while the standard doesn't require strings to be stored contiguously in memory, it would be very odd and inefficient not to do so. – James McNellis Nov 14 '09 at 04:21
  • 1
    I assume the standard envisaged strings might be implemented as a rope-like thing, so that appending and sub-stringing are faster. Access would be slightly slower (like `deque` vs `vector`), and `c_str()` would incur a hefty penalty the first time it's called. As it turned out, implementers all seem to have weighed up the trade-off and wanted nothing to do with it... – Steve Jessop Nov 14 '09 at 15:21
  • 1
    I think so too. It would be really useful when you're writing portable code, to compile and test it with a variety of different common and not-so-common implementation details, beyond what pedantic compilers warn about. But look what happened when C compilers started actually using strict aliasing rules - half the code breaks, and everyone gets very confused except for the kind of smug standards-lawyer who hangs out on SO ;-) Too few programmers are actually pedantic enough to be able to use such a thing - you still get people advising that more than -O2 on gcc is "dangerous"... – Steve Jessop Nov 15 '09 at 16:17
17

If you have very controlled conditions, you can just do:

std::vector<int> v(4,100);
int* pv = &v[0];

Be warned that this will only work as long as the vector doesn't have to grow, and the vector will still manage the lifetime of the underlying array (that is to say, don't delete pv). This is not an uncommon thing to do when calling underlying C APIs, but it's usually done with an unnamed temporary rather than by creating an explicit int* variable.

Drew Hall
  • 28,429
  • 12
  • 61
  • 81
0

One way of protecting yourself against size changes is to reserve the maximal space (or larger) that you will need:

std::vector<int> v(4,100); //Maybe need 
v.reserve(40);             //reallocate to block out space for 40 elements

This will ensure that push_backs won't cause reallocation of the existing data.

user1270710
  • 553
  • 1
  • 6
  • 11