Valgrind 显示无效的读/写错误,但我不使用 new 或 calloc,只使用向量和固定数组



我的项目中有一个类,它主要存储std::vector元素,应该能够将对象的二进制表示存储到磁盘或从磁盘加载对象。当一个对象加载一个文件时,它会将元素的数量读取到一个变量中,然后将存储的元素读取到数组中。从这个数组中,它将元素推入向量成员中。我让C++处理内存,就像在中一样,我不显式调用callocnew。虽然它工作得很好,但Valgrind给了我一些关于无效读写的错误信息,我不理解。它们似乎源于类析构函数,但由于类只有"动态"的std::vector元素,我不应该在其中做任何事情,或者我应该做什么?

这是一个最小的工作示例:

#include <vector>
#include <string>
#include <fstream>
#include <iostream>
class Test {
public:
Test() {}
~Test() {}
void add(std::string s) { v.push_back(s); }
std::vector<std::string>& get() { return v; }
void store(char const* path) {
std::ofstream file(path, std::ios_base::out | std::ios_base::binary);
unsigned int n = v.size();
file.write((char*) &n, sizeof(unsigned int));
file.write((char*) v.data(), n*sizeof(std::string));
file.close();
}
void read(char const* path) {
std::ifstream file(path, std::ios_base::in | std::ios_base::binary);
unsigned int n;
file.read((char*) &n, sizeof(unsigned int));
std::string in_v[n];
file.read((char*) in_v, n*sizeof(std::string));
v.clear();
for (unsigned int i = 0; i < n; i++) {
v.push_back(in_v[i]);
}
if (!file) { throw std::runtime_error("reading failed"); }
}
private:
std::vector<std::string> v;
};
std::ostream& operator<<(std::ostream& os, Test& t) {
for (unsigned int i = 0; i < t.get().size(); i++) {
os << t.get()[i] << std::endl;
}
return os;
}
int main(int argc, char *argv[]) {
Test a;
a.add("foo");
a.add("bar");
std::cout << a << std::endl;
a.store("file");
//Test b;
//b.read("file");
//std::cout << "restored:" << std::endl << b << std::endl;
return 0;
}

如果我在valgrind中编译并运行它,一切都如预期一样工作,并且没有检测到泄漏:

==24891== Memcheck, a memory error detector
==24891== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==24891== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==24891== Command: ./dummy
==24891== 
foo
bar
==24891== 
==24891== HEAP SUMMARY:
==24891==     in use at exit: 72,704 bytes in 1 blocks
==24891==   total heap usage: 7 allocs, 6 frees, 81,528 bytes allocated
==24891== 
==24891== LEAK SUMMARY:
==24891==    definitely lost: 0 bytes in 0 blocks
==24891==    indirectly lost: 0 bytes in 0 blocks
==24891==      possibly lost: 0 bytes in 0 blocks
==24891==    still reachable: 72,704 bytes in 1 blocks
==24891==         suppressed: 0 bytes in 0 blocks
==24891== Rerun with --leak-check=full to see details of leaked memory
==24891== 
==24891== For counts of detected and suppressed errors, rerun with: -v
==24891== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

但是,一旦我取消对main()函数中最后一行的注释,所有的都仍然有效,但现在valgrind打印了一些消息,我不知道为什么。

foo
bar
restored:
foo
bar
==4004== Invalid read of size 4
==4004==    at 0x4F04610: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.21)
==4004==    by 0x4026CE: void std::_Destroy<std::string>(std::string*) (stl_construct.h:93)
==4004==    by 0x402534: void std::_Destroy_aux<false>::__destroy<std::string*>(std::string*, std::string*) (stl_construct.h:103)
==4004==    by 0x4021FD: void std::_Destroy<std::string*>(std::string*, std::string*) (stl_construct.h:126)
==4004==    by 0x401D76: void std::_Destroy<std::string*, std::string>(std::string*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==4004==    by 0x401B5B: std::vector<std::string, std::allocator<std::string> >::~vector() (stl_vector.h:424)
==4004==    by 0x4016E5: Test::~Test() (dummy.cpp:9)
==4004==    by 0x4015B0: main (dummy.cpp:48)
==4004==  Address 0x5aa8c90 is 16 bytes inside a block of size 28 free'd
==4004==    at 0x4C2A184: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==4004==    by 0x4F04603: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.21)
==4004==    by 0x4026CE: void std::_Destroy<std::string>(std::string*) (stl_construct.h:93)
==4004==    by 0x402534: void std::_Destroy_aux<false>::__destroy<std::string*>(std::string*, std::string*) (stl_construct.h:103)
==4004==    by 0x4021FD: void std::_Destroy<std::string*>(std::string*, std::string*) (stl_construct.h:126)
==4004==    by 0x401D76: void std::_Destroy<std::string*, std::string>(std::string*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==4004==    by 0x401B5B: std::vector<std::string, std::allocator<std::string> >::~vector() (stl_vector.h:424)
==4004==    by 0x4016E5: Test::~Test() (dummy.cpp:9)
==4004==    by 0x4015A4: main (dummy.cpp:56)
==4004== 
==4004== Invalid write of size 4
==4004==    at 0x4F04616: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.21)
==4004==    by 0x4026CE: void std::_Destroy<std::string>(std::string*) (stl_construct.h:93)
==4004==    by 0x402534: void std::_Destroy_aux<false>::__destroy<std::string*>(std::string*, std::string*) (stl_construct.h:103)
==4004==    by 0x4021FD: void std::_Destroy<std::string*>(std::string*, std::string*) (stl_construct.h:126)
==4004==    by 0x401D76: void std::_Destroy<std::string*, std::string>(std::string*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==4004==    by 0x401B5B: std::vector<std::string, std::allocator<std::string> >::~vector() (stl_vector.h:424)
==4004==    by 0x4016E5: Test::~Test() (dummy.cpp:9)
==4004==    by 0x4015B0: main (dummy.cpp:48)
==4004==  Address 0x5aa8c90 is 16 bytes inside a block of size 28 free'd
==4004==    at 0x4C2A184: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==4004==    by 0x4F04603: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.21)
==4004==    by 0x4026CE: void std::_Destroy<std::string>(std::string*) (stl_construct.h:93)
==4004==    by 0x402534: void std::_Destroy_aux<false>::__destroy<std::string*>(std::string*, std::string*) (stl_construct.h:103)
==4004==    by 0x4021FD: void std::_Destroy<std::string*>(std::string*, std::string*) (stl_construct.h:126)
==4004==    by 0x401D76: void std::_Destroy<std::string*, std::string>(std::string*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==4004==    by 0x401B5B: std::vector<std::string, std::allocator<std::string> >::~vector() (stl_vector.h:424)
==4004==    by 0x4016E5: Test::~Test() (dummy.cpp:9)
==4004==    by 0x4015A4: main (dummy.cpp:56)
==4004== 
==4004== Invalid free() / delete / delete[] / realloc()
==4004==    at 0x4C2A184: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==4004==    by 0x4F04603: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.21)
==4004==    by 0x4026CE: void std::_Destroy<std::string>(std::string*) (stl_construct.h:93)
==4004==    by 0x402534: void std::_Destroy_aux<false>::__destroy<std::string*>(std::string*, std::string*) (stl_construct.h:103)
==4004==    by 0x4021FD: void std::_Destroy<std::string*>(std::string*, std::string*) (stl_construct.h:126)
==4004==    by 0x401D76: void std::_Destroy<std::string*, std::string>(std::string*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==4004==    by 0x401B5B: std::vector<std::string, std::allocator<std::string> >::~vector() (stl_vector.h:424)
==4004==    by 0x4016E5: Test::~Test() (dummy.cpp:9)
==4004==    by 0x4015B0: main (dummy.cpp:48)
==4004==  Address 0x5aa8c80 is 0 bytes inside a block of size 28 free'd
==4004==    at 0x4C2A184: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==4004==    by 0x4F04603: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.21)
==4004==    by 0x4026CE: void std::_Destroy<std::string>(std::string*) (stl_construct.h:93)
==4004==    by 0x402534: void std::_Destroy_aux<false>::__destroy<std::string*>(std::string*, std::string*) (stl_construct.h:103)
==4004==    by 0x4021FD: void std::_Destroy<std::string*>(std::string*, std::string*) (stl_construct.h:126)
==4004==    by 0x401D76: void std::_Destroy<std::string*, std::string>(std::string*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==4004==    by 0x401B5B: std::vector<std::string, std::allocator<std::string> >::~vector() (stl_vector.h:424)
==4004==    by 0x4016E5: Test::~Test() (dummy.cpp:9)
==4004==    by 0x4015A4: main (dummy.cpp:56)
==4004== 
==4004== 
==4004== HEAP SUMMARY:
==4004==     in use at exit: 72,704 bytes in 1 blocks
==4004==   total heap usage: 11 allocs, 12 frees, 90,296 bytes allocated
==4004== 
==4004== LEAK SUMMARY:
==4004==    definitely lost: 0 bytes in 0 blocks
==4004==    indirectly lost: 0 bytes in 0 blocks
==4004==      possibly lost: 0 bytes in 0 blocks
==4004==    still reachable: 72,704 bytes in 1 blocks
==4004==         suppressed: 0 bytes in 0 blocks
==4004== Rerun with --leak-check=full to see details of leaked memory
==4004== 
==4004== For counts of detected and suppressed errors, rerun with: -v
==4004== ERROR SUMMARY: 6 errors from 3 contexts (suppressed: 0 from 0)

这些错误是从哪里来的?我认为这是read方法的问题,但为什么valgrind在创建a("dumm.cpp:48")时显示错误,而之前没有任何错误?

此行-

file.read((char*) in_v, n*sizeof(std::string));

导致以后在路上出现未定义的行为。您正试图将一些字符读取到一个std::string对象中,而这个对象并不是为它设计的。这样做,您可能正在运行内部数据(可能是指针),访问它们将导致UB

相反,您应该使用std::vector<char>reserve足够的字节来容纳您从文件中读取的字符串,并读取到vec.data()

最新更新