首页 > 解决方案 > valgrind:多个 std::vector::resize 调用

问题描述

我一直在一个大型 C++ 代码库中追踪一个单独的问题。出于某种原因,我无法理解以下 Valgrind 行为。有人可以在这里阐明一下吗?

代码是:

% cat foo.cxx
#include <cstring>
#include <string>
#include <vector>

int main() {
  std::vector<char> v;
#ifdef RESIZE9
  v.resize(9);
#endif
  v.resize(10);

  std::string s(10, 'x');
  std::strcpy(&v[0], s.c_str());

  return 0;
}

这是预期的 Valgrind 输出:

% g++ foo.cxx && valgrind ./a.out
==21886== Memcheck, a memory error detector
==21886== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==21886== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==21886== Command: ./a.out
==21886==
==21886== Invalid write of size 1
==21886==    at 0x4838DD7: strcpy (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==21886==    by 0x1092CA: main (in /tmp/a.out)
==21886==  Address 0x4d84c8a is 0 bytes after a block of size 10 alloc'd
==21886==    at 0x4835DEF: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==21886==    by 0x109BCD: __gnu_cxx::new_allocator<char>::allocate(unsigned long, void const*) (in /tmp/a.out)
==21886==    by 0x109AD5: std::allocator_traits<std::allocator<char> >::allocate(std::allocator<char>&, unsigned long) (in /tmp/a.out)
==21886==    by 0x109997: std::_Vector_base<char, std::allocator<char> >::_M_allocate(unsigned long) (in /tmp/a.out)
==21886==    by 0x1095E8: std::vector<char, std::allocator<char> >::_M_default_append(unsigned long) (in /tmp/a.out)
==21886==    by 0x1093CC: std::vector<char, std::allocator<char> >::resize(unsigned long) (in /tmp/a.out)
==21886==    by 0x10926A: main (in /tmp/a.out)
==21886==
==21886==
==21886== HEAP SUMMARY:
==21886==     in use at exit: 0 bytes in 0 blocks
==21886==   total heap usage: 2 allocs, 2 frees, 72,714 bytes allocated
==21886==
==21886== All heap blocks were freed -- no leaks are possible
==21886==
==21886== For counts of detected and suppressed errors, rerun with: -v
==21886== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

但现在考虑以下几点:

% g++ -DRESIZE9 foo.cxx && valgrind ./a.out
==21904== Memcheck, a memory error detector
==21904== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==21904== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==21904== Command: ./a.out
==21904==
==21904==
==21904== HEAP SUMMARY:
==21904==     in use at exit: 0 bytes in 0 blocks
==21904==   total heap usage: 3 allocs, 3 frees, 72,731 bytes allocated
==21904==
==21904== All heap blocks were freed -- no leaks are possible
==21904==
==21904== For counts of detected and suppressed errors, rerun with: -v
==21904== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

系统是 Debian/10.9,具有:

% g++ --version
g++ (Debian 8.3.0-6) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

% valgrind --version
valgrind-3.14.0

标签: c++valgrind

解决方案


虽然我无法确认这是一个完整的(跨平台)“解决方案”,但在调整大小操作后添加一行来显示向量的实际容量可能会有所帮助:

#include <cstring>
#include <string>
#include <vector>
#include <iostream>

//#define RESIZE9 1

int main()
{
    std::vector<char> v;
    #ifdef RESIZE9
    v.resize(9);
    #endif
    v.resize(10);

    std::cout << v.capacity() << std::endl; // Show the actual allocated size

    std::string s(10, 'x');
    std::strcpy(&v[0], s.c_str());

    return 0;
}

按原样运行此代码(Visual Studio、MSVC、Windows 10、64 位)显示容量为10(并非意外)。但是,当该#define RESIZE9 1行未注释时,显示的容量(在两次调整大小调用后)为13.

我相信,增加额外的容量是在标准的要求范围内:只要新分配的向量有足够的容量来适应新的大小,就不会损坏任何东西。分配 4 个额外字节(而不仅仅是一个)最有可能优化内存管理。


推荐阅读