Home > Net >  Using a std::vector<std::array<T,N>> as a flat contiguous array of T
Using a std::vector<std::array<T,N>> as a flat contiguous array of T

Time:02-23

A std::vector stores its elements contiguously in memory. An std::array<T,N> is just a wrapper around N contiguous T elements, stored directly in the object itself.

Hence I am wondering if a std::vector<std::array<T,N>> of size n can also be seen as an array of T of size N*n.

Consider the following code (also here):

int main() {
    // init a vector of 10 arrays of 3 elements each
    std::vector<std::array<int,3>> v(10);

    // fill it
    for (int i=0; i<10;   i) {
        v[i] = {3*i,3*i 1,3*i 2};
    }

    // take the first array
    std::array<int,3>* a_ptr = v.data();

    // take the first element of the array
    int* i_ptr = a_ptr->data();

    // dereference the pointer for 3*10 elements
    for (int i=0; i<30;   i) {
        std::cout << i_ptr[i] << ',';
    }
    return 0;
}

It appears to work for my setup, but is the code legit or is it undefined behavior?

If I were to replace std::array<int,3> with a struct:

    struct S { 
        int i,j,k;
    };

    std::vector<S> v(10);
    S* s_ptr = v.data();
    int* i_ptr = &S.i;

Would it work the same?

What would be the alternatives that would not trigger undefined behavior?

Context: I have a std::vector<std::array<int,3>> and I want to serialize it to send it over a network.

CodePudding user response:

No, but yes.

No, the C standard does not let you treat structs with arrays in them, or even structs with uniform elements, packed together as a single contiguous larger array of the base type.

Yes, in that enough in world production code requires this to work that no compiler is going to break it from working any time soon.

Things to watch out for include packing. Add static asserts that the size of the structs and arrays is what you expect. Also, avoid being fancy with object lifetime or going "backwards" from an index besides the first one; the reachability rules of C are slightly more likrly to bite you if you do strange things.

Another concern is that these constructs are under somewhat active investigation; the fiasco that std vector cannot be implemented in standard C , for example, or std::launder and bit_cast. When a real standard way to do what you want develops, switching to it might be a good idea, because the old technique will become less likely to be supported.

CodePudding user response:

From a purely pedantic point-of-view, the code has undefined behavior starting with the iteration with i equal to 3:

i_ptr 3 points one-past-the-array in the first element of v and so you are not allowed to dereference it.

Pointer arithmetic beyond that, i.e. i_ptr 4, has undefined behavior because pointer arithmetic is only allowed inside an array and one-past-the-array.

The int arrays of different elements of v do not form a larger combined array of ints. They cannot be treated as such.

The above holds even if std::array has the same size as int[3] and therefore has no other padding or members aside from the array.


In case of the struct version, undefined behavior occurs already when dereferencing i_ptr 1, the one-past-the-object pointer of v[0].i.

  • Related