vendredi 4 novembre 2022

std::vector::push_back() changes std::vector::begin() and std::vector::end() iterator addresses [duplicate]

I'm learning c++ iterators.

While poking around with various excercises and algorithms, I was astonished by finding out that .begin() and .end() change address after adding elements. This breaks things like boundary checking of an iterator stored before a push_back().

I'm even more baffled that the different addresses still apparently resolve to the correct payload.

I replicated a basic example that just does basic allocation and show how the addresses change:

#include <cstdio>
#include <vector>

int main()
{
    printf("std vector push back changes begin and end\n");

    auto show_vector_stats = []( std::vector<int> &iras32_source )
    {
        printf("Begin: %p | End: %p | Diff: %d | Size: %d\n", iras32_source.begin(), iras32_source.end(), iras32_source.end() -iras32_source.begin(), iras32_source.size() );
    };

    //Create a vector
    std::vector<int> as32_my_array;
    show_vector_stats( as32_my_array );
    //add first element
    as32_my_array.push_back( 999 );
    show_vector_stats( as32_my_array );
    //Store iterator and index of first element
    int index_first_element = 0;
    std::vector<int>::iterator iterator_first_element = as32_my_array.begin();
    printf("Index from Iterator: %d\n", iterator_first_element -as32_my_array.begin());
    //Store more elements
    as32_my_array.push_back( 2 );
    show_vector_stats( as32_my_array );

    for (int s32_cnt = 0; s32_cnt < 10; s32_cnt++)
    {
        as32_my_array.push_back( 101 +s32_cnt);
    }
    show_vector_stats( as32_my_array );
    //Access by previously stored index and iterators
    printf("Access by index: %d | Address of element: %p\n", as32_my_array[index_first_element], &as32_my_array[index_first_element] );
    printf("Access by iterator: %d | Address of element: %p\n", *iterator_first_element, iterator_first_element );
    printf("Index from Iterator: %d\n", iterator_first_element -as32_my_array.begin());

    return 0;
}

OUTPUT:

std vector push back changes begin and end
Begin: 00000000 | End: 00000000 | Diff: 0 | Size: 0
Begin: 00fc1a80 | End: 00fc1a84 | Diff: 1 | Size: 1
Index from Iterator: 0
Begin: 00fc1b70 | End: 00fc1b78 | Diff: 2 | Size: 2
Begin: 00fcac28 | End: 00fcac58 | Diff: 12 | Size: 12
Access by index: 999 | Address of element: 00fcac28
Access by iterator: 999 | Address of element: 00fc1a80
Index from Iterator: -9322

Process returned 0 (0x0)   execution time : 0.050 s
Press any key to continue.

I was under the impression that modern iterators were a drop-in superior and more modern version of indexes. But some things like checking a previous iterator against a more recent .begin() will return a wrong index, like in the example above, begin()-iterator returns 0 the first time, and returns -9322 after some more elements are added...

The difference between .end() and .begin() shows the correct vector size. Likewise, in a loop without changes, the difference iterator-begin() will show the correct index.

The documentations doesn't mention that begin() will return a different address potentially with every push_back(). Only that it's empty at the beginning, like the example above shows. https://cplusplus.com/reference/vector/vector/begin/

Are iterators ONLY supposed to be used for read only loops? Am I missing something, like I'm I resolving addresses wrong?

Aucun commentaire:

Enregistrer un commentaire