2016-08-10 5 views
3

Valgrind는 힙에 남아있는 객체에 대한 느린 참조를 감지하는 데 유용합니다. 그러나 스택에서 범위를 벗어나는 변수에 대한 참조가 오래 머무르는 경우에는이 기능이없는 것 같습니다. 예를 들어 CharHolder('x')가 빨리 p1의 구성이 완료되면 소멸되므로 p1C++에서 범위를 벗어난 스택 변수에 대한 참조를 검색하는 방법은 무엇입니까?

#include <iostream> 

struct CharHolder { 
    const char ch; 
    CharHolder(char _ch) : ch(_ch) {} 
}; 

struct Printer { 
    const CharHolder& ref; 
    Printer(const CharHolder& _ref) : ref(_ref) {} 
    void print() { 
     std::cout << &ref << ": " << ref.ch << std::endl; 
    } 
}; 

int main() { 
    // g++ -O0: prints 'x' 
    // g++ -O3: prints undefined character 
    Printer p1(CharHolder('x')); 
    p1.print(); 

    // g++: prints undefined character 
    CharHolder* h = new CharHolder('x'); 
    Printer p2(*h); 
    delete h; 
    p2.print(); 
} 

첫 번째 예는, 프린터가 범위 밖 스택 변수에 대한 참조를 유지 한 것이다. p2

번째 예는, 프린터 p2 시도 print()에서 참조하기 전에 free되기를되는 더미 변수에 대한 참조를 유지 한 것이다.

Valgrind의 두 번째 예를 뿌려 :

==82331== Invalid read of size 1 
==82331== at 0x400A8E: Printer::print() 
==82331== by 0x400967: main 
==82331== Address 0x5a1c040 is 0 bytes inside a block of size 1 free'd 
==82331== at 0x4C2C2BC: operator delete(void*) 
==82331== by 0x40095F: main 

어떻게 아마도 Valgrind의 같은 도구를 사용하여 제 1 종 오류를 감지 할 수 있습니까?

답변

3

정적 분석 도구가 완벽하지 않습니다. valgrind과 같은 정적 분석 도구는 일반적인 프로그래밍 버그를 찾아내는 데 큰 성과를 거두었습니다.

그러나 그들은 그 중 100 %를 잡을 수 없습니다.

이러한 종류의 프로그래밍 버그를 가능한 한 피하려는 내 접근 방식은 계약에 따라 이러한 종류의 프로그래밍 버그가 논리적으로 불가능하다는 것을 증명하기위한 방어 프로그래밍 원칙입니다. 여기에는 다음과 같은 내용이 포함됩니다.

  1. 참조 및 포인터 대신 스마트 포인터 사용. 계약을 통해 스마트 포인터를 사용하면 범위를 벗어나는 객체에 대한 참조가 논리적으로 불가능해진다는 것을 증명할 수 있습니다.

  2. 고전적인 for (size_t i=0; i<container.size(); ++i) 접근 방식 대신 반복기와 표준 라이브러리 알고리즘을 사용합니다. 명확하게 정의 된 시작과 끝 반복자는 배열의 끝에서 논리적으로 불가능해진다. 또한 추가 보너스로 컨테이너의 선택이 전환되는 경우 어떤 이유로 코드를 변경하지 않아도됩니다.

경우에 따라서는 런타임 전용 정적 분석 도구로이를 감지 할 수 없습니다. 궁극적으로 컴파일 된 코드에는 런타임에 공식적으로 임시가 범위를 벗어나는 것으로 표시되는 내용이 전혀 포함되어 있지 않습니다. 생성 된 코드는 자동 범위 변수와 매개 변수로 전달 된 임시 변수를 모두 수용 할 수있는 충분한 스택 프레임을 할당합니다. 생성자 호출이 완료된 후에는 명시 적 호출이 생성되어 임시를 손상됨으로 표시하지 않습니다. 나는 valgrind, 또는 다른 정적 분석 도구가이를 어떻게 알 수 있는지를 보지 못합니다.

아마도 임시 클래스에 명시 적 소멸자가있는 경우 이론적으로 일반 정적 분석 도구에서 소멸자가 호출되어 클래스 인스턴스가 삭제되었음을 알 수 있습니다.

그러나 이것은 완벽한 대답이 없음을 보여줍니다. 내가 언급 한 프로그래밍 습관조차도 문제의 100 %를 예방하지 못할 것이다. 스마트 포인터를 사용할 때 순환 참조와 같이 고려해야 할 자신 만의 복잡성을 도입하기도합니다.

+6

'valgrind'는 정적 분석 도구가 아닙니다. – immibis

+0

필자가 전달한 함수 인수보다 오래 지속되는 방식으로 전달 된 참조를 저장하는 것은 좋지 않은 아이디어 인 객체 레슨 (말장난 의도) 일뿐입니다. 모든 호출에 대해 전달 된 참조는 임시 객체를 참조 할 수 있습니다. 돌아 오면 잘못된 개체를 참조하게되고 회원은 쓸모 없게됩니다. 그러한 상황에 대한 경고를 생성하기 위해 컴파일러를 만들어야 할 수도 있습니다. –

+0

@BlairHoughton - 컴파일러가 임시 참조를 매개 변수로 전달하는 함수 호출을 컴파일 할 때 호출 된 함수가 전달 된 참조를 어딘가에 저장한다는 것을 어떻게 알 수 있습니까? 소스 코드를 호출 된 함수에, 알려진 기록의 어떤 시점에서, 시간이 오래 전에 시작 되었기 때문에? –