简体   繁体   中英

Valgrind error dereferencing an address returned by std::vector

Why does valgrind returns an error for this piece of code?

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> vec;
    int *ptr;

    for (int i = 0; i < 1000; i++)
    {
        vec.push_back(i);
        if (i == 100)
        {
            ptr = &vec[i];
        }
    }
    std::cout << ptr << "\n";  // Print address of -> Ok
    std::cout << *ptr << "\n"; // Print content of -> Ok but with a valgrind error
}

Compiled with: g++ -Wall -Wpedantic -O0 -o demo demo.cpp

The valgrind error is:

==3982== Invalid read of size 4
==3982==    at 0x1093A7: main (in /home/david/demo)
==3982==  Address 0x4dae1e0 is 400 bytes inside a block of size 512 free'd
==3982==    at 0x483E1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==3982==    by 0x109EB4: __gnu_cxx::new_allocator<int>::deallocate(int*, unsigned long) (in /home/david/demo)
==3982==    by 0x109B5B: std::allocator_traits<std::allocator<int> >::deallocate(std::allocator<int>&, int*, unsigned long) (in /home/david/demo)
==3982==    by 0x10970F: std::_Vector_base<int, std::allocator<int> >::_M_deallocate(int*, unsigned long) (in /home/david/demo)
==3982==    by 0x109A3B: void std::vector<int, std::allocator<int> >::_M_realloc_insert<int const&>(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&) (in /home/david/demo)
==3982==    by 0x10964F: std::vector<int, std::allocator<int> >::push_back(int const&) (in /home/david/demo)
==3982==    by 0x109354: main (in /home/david/demo)
==3982==  Block was alloc'd at
==3982==    at 0x483CE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==3982==    by 0x10A0FF: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (in /home/david/demo)
==3982==    by 0x109F73: std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (in /home/david/demo)
==3982==    by 0x109DAD: std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (in /home/david/demo)
==3982==    by 0x1098BC: void std::vector<int, std::allocator<int> >::_M_realloc_insert<int const&>(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&) (in /home/david/demo)
==3982==    by 0x10964F: std::vector<int, std::allocator<int> >::push_back(int const&) (in /home/david/demo)
==3982==    by 0x109354: main (in /home/david/demo)
==3982== 

It makes me doubt whether dereferencing an address returned by a vector has undefined behavior, is this legal code?

Valgrind says it's an "invalid read". During the push_back operation the std::vector will reallocate memory if required and copy all the data to anew location.

If so, it is possible that your ptr is pointing to memory which is no longer allocated to the vector vec .

So yes, it might be UB the way you are using it.

It's better to reserve the memory first if you already know the number of elements you are going to insert, and then simply insert the elements at the correct location.

Regarding the last part of the question about dereferencing pointers to vector indexes.

Please correct me if I am wrong but its my understanding that the elements in a vector are just pointers to the actual data. The array of vector elements must be in contiguous memory so they can be indexed but the data can be anywhere.

Expanding a vector is quick because you only have to move the indexes to a new bigger block of contiguous memory, not the actual data. The first time you expand a vector with.push_back the array of indexes WILL move in memory so any pointers you had to them will become invalid, after the first time its implementation specific.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM