【问题标题】:Valgrind error dereferencing an address returned by std::vectorValgrind 错误取消引用 std::vector 返回的地址
【发布时间】:2021-01-03 09:33:07
【问题描述】:

为什么 valgrind 会为这段代码返回错误?

#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
}

编译:g++ -Wall -Wpedantic -O0 -o demo demo.cpp

valgrind 错误是:

==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== 

这让我怀疑取消引用向量返回的地址是否具有未定义的行为,这是合法的代码吗?

【问题讨论】:

  • 由于重新分配问题,指向元素的指针和迭代器都很容易失效,最好保存 indexes
  • 另一种可能的解决方案是在之前设置向量的大小,并且只使用索引。或者在您的情况下,可以使用 std::iota 而不是显式循环来解决。
  • @Someprogrammerdude OP 并没有要求解决方法,但他们很清楚地询问显示的代码是否合法。
  • @cigien 好吧,我没有写答案,只有 cmets。
  • @cigien true,但欢迎提出任何建议。问题来了,因为“重新分配内存”的库根本没有明确这一点,例如g_array(来自 glib)使用类似的概念(使用realloc),我认为澄清它是非常合适的,因为它可以引入错误。我看到std::vector 的情况相同,但可能是在这种情况下它有据可查,标准中是否有任何参考?

标签: c++ vector dereference


【解决方案1】:

Valgrind 说这是“无效读取”。在push_back 操作期间,std::vector 将根据需要重新分配内存并将所有数据复制到新位置。

如果是这样,您的ptr 可能指向的内存不再分配给向量vec

所以是的,它可能是你使用它的方式。

如果你已经知道要插入的元素数量,最好先reserve内存,然后在正确的位置插入元素。

【讨论】:

  • 其实我觉得只有可能是UB。实现可能已经分配了足够的内存,因此在获取元素的地址后不会进行重新分配。
  • @cigien:是的,你是对的。我会更新
  • 另一种方法是不获取元素的地址以供将来使用,而是获取其在向量中的索引
  • 感谢您编辑答案以澄清有关 UB 的一些信息。我做了一些小的编辑来清理答案,希望没问题。
【解决方案2】:

关于问题的最后一部分关于取消引用指向向量索引的指针。

如果我错了,请纠正我,但我的理解是向量中的元素只是指向实际数据的指针。向量元素的数组必须在连续的内存中,以便它们可以被索引,但数据可以在任何地方。

扩展向量很快,因为您只需将索引移动到一个新的更大的连续内存块,而不是实际数据。第一次使用 .push_back 扩展向量时,索引数组将在内存中移动,因此在第一次具体实现之后,您必须指向它们的任何指针都将变为无效。

【讨论】:

  • 向量中的元素只是指向实际数据的指针。你能详细说明一下吗?据我了解,向量是指向连续内存区域的指针,类似于可增长的数组...数据可以在任何地方...如果您的意思是它们是指针数组,其中每个元素都在碎片化的记忆中,我觉得你错了。
  • 嗯,我说的是真的。如果您取消引用向量的名称,它将为您提供指向数据的指针数组中第一个元素的当前地址。
  • 我认为向量的主要特征之一是,您的数据类型有多大并不重要,因为当您扩展向量时,只有指向数据的指针才会被移动,所以它很快。 C++ 风格的字符串与向量非常相似,但在这种情况下存储指向单个字符的指针会很愚蠢,因此数据实际上存储在索引中。
  • 我不同意(向量不存储指针)但感谢您的回答。
  • 啊,是的,我不是说 C/C++ 风格的指针。向量的索引是数据元素的句柄或“向量”,但它们仍然指向数据,它们不是实际数据。
猜你喜欢
  • 1970-01-01
  • 2015-11-15
  • 2019-03-05
  • 2011-12-30
  • 1970-01-01
  • 2014-12-12
  • 2017-10-19
  • 1970-01-01
相关资源
最近更新 更多