2017-02-09 3 views
0

내 프로젝트에 주로 std::vector 요소를 저장하는 클래스가 있고이 클래스의 이진 표현을 저장하고로드 할 수 있어야합니다. 디스크에서 /로 객체. 객체가 파일을로드하면 변수의 요소 수를 읽은 다음 저장된 요소를 배열로 읽습니다. 이 배열에서 요소를 벡터 멤버에 푸시합니다. 내가 명시 적으로 calloc 또는 new을 호출하지 않은 것처럼 C++에서 메모리를 처리하도록했습니다. 잘 작동하지만 Valgrind는 잘못된 읽기 및 쓰기에 대한 일부 오류 메시지를 제공하므로 이해가되지 않습니다. 그것들은 클래스 소멸자에서 비롯된 것 같지만 클래스에는 "동적 인"요소 인 std::vector 요소 만 있으므로 거기에서 아무 것도 할 필요가 없습니까?Valgrind는 잘못된 읽기/쓰기 오류를 표시하지만 new 또는 calloc은 벡터 및 고정 배열 만 사용하지 않습니다.

이 작동 최소한의 예입니다 : 내가 컴파일하고 Valgrind의에서 이것을 실행하면,

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

모든 예상 누출이 감지되지로 작동합니다

==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 ("dummy.cpp : 48")의 생성에서 오류를 보여줍니까?

+1

'std :: string in_v [n]; '은 VLA이며 트리거 된 것일 수 있습니다. 배열 크기가 알려지지 않은 경우라면'std :: vector'를 사용해야합니다. – NathanOliver

+2

'file.read ((char *) in_v, n * sizeof (std :: string));'look _really_ dodgy –

+0

@NathanOliver 조언을 주셔서 감사합니다, 그냥''v.resize (n);과''file.read (char *) v.data(), n * sizeof (std :: string)); 이것도 잘 작동하지만 valgrind 오류가 발생합니다. 메서드의 끝에 std :: stiring * in_v = new std :: string [n]과 delete []를 명시 적으로 할당하면 동일합니다. – mable

답변

1

이 줄은 -

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

도로에 나중에 정의되지 않은 동작을 일으키는 원인이된다. 일부 문자를 std :: string 객체로 읽으려고 시도하고 있습니다. 이렇게, 당신은 아마 내부 데이터 (아마 포인터)를 통해 실행하고,

대신 그들을 UB의 원인이됩니다 액세스, 당신은 파일 읽기 먹은 문자열을 개최 std::vector<char>, reserve 충분한 바이트를 사용하고 vec.data()로 읽어야한다