Converting between C++ std::vector and C array without copying

59,332

Solution 1

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.

Solution 2

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

Solution 3

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.

Solution 4

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.

Share:
59,332
D R
Author by

D R

Updated on July 06, 2022

Comments

  • D R
    D R almost 2 years

    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);
    
  • Steve Jessop
    Steve Jessop over 14 years
    "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
    Steve Jessop over 14 years
    "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.
  • James McNellis
    James McNellis over 14 years
    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.
  • Drew Hall
    Drew Hall over 14 years
    @Steve: Good point. Just be sure you reserve() before you get the pointer! :)
  • James McNellis
    James McNellis over 14 years
    @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.
  • Steve Jessop
    Steve Jessop over 14 years
    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...
  • D R
    D R over 14 years
    Thanks for your answer. What about the reverse conversion? How can I initialize an std::vector<int> from a C array int* without copying?
  • James McNellis
    James McNellis over 14 years
    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<int> v(&pv[0], &pv[4]);, using the example you added to your question.
  • Steve Jessop
    Steve Jessop over 14 years
    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"...
  • Joseph Garvin
    Joseph Garvin almost 12 years
    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.
  • James McNellis
    James McNellis almost 12 years
    @JosephGarvin: The array_ref<T> and string_ref<T> class templates 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<T> class template in the Boost-licensed CxxReflect library that has been most useful).
  • Bulletmagnet
    Bulletmagnet over 8 years
    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.