C ++에서 세분화 오류 수정


95

Windows 및 Unix 용 크로스 플랫폼 C ++ 프로그램을 작성 중입니다. 창 쪽에서는 코드가 컴파일되고 문제없이 실행됩니다. Unix 측에서는 컴파일되지만 실행하려고하면 세그멘테이션 오류가 발생합니다. 내 첫 직감은 포인터에 문제가 있다는 것입니다.

세분화 오류를 찾아 수정하는 좋은 방법은 무엇입니까?

답변:


135
  1. 을 사용하여 애플리케이션을 컴파일 -g하면 바이너리 파일에 디버그 기호가 있습니다.

  2. gdbgdb 콘솔을 여는 데 사용 합니다.

  3. file콘솔에서 애플리케이션의 바이너리 파일을 사용 하고 전달합니다.

  4. run애플리케이션을 시작하는 데 필요한 모든 인수를 사용 하고 전달합니다.

  5. 세그멘테이션 오류 를 발생시키기위한 조치를 취하십시오 .

  6. 입력 bt에서 gdb의 스택 추적 얻기 위해 콘솔 분할 오류를 .


g의 맥락에서 컴파일된다는 것은 무엇을 의미 CMake합니까?
Schütze

2
디버그 빌드 유형을 활성화합니다. 한 가지 방법은 cmake -DCMAKE_BUILD_TYPE=Debug.
Antonin Décimo 19-06-14

36

때로는 충돌 자체가 문제의 실제 원인이 아닙니다. 아마도 이전 시점에서 메모리가 박살 났지만 손상이 나타나기까지 시간이 걸렸습니다. 포인터 문제 (배열 경계 검사 포함)에 대한 많은 검사가있는 valgrind를 확인하십시오 . 충돌이 발생한 라인뿐만 아니라 문제 가 시작 되는 위치를 알려줍니다 .


19

문제가 발생하기 전에 가능한 한 피하십시오.

  • 최대한 자주 코드를 컴파일하고 실행하십시오. 결함이있는 부품을 찾는 것이 더 쉬울 것입니다.
  • 저수준 / 오류가 발생하기 쉬운 루틴을 캡슐화하여 메모리로 직접 작업 할 필요가 거의 없도록하십시오 (프로그램 모델링에주의)
  • 테스트 스위트를 유지하십시오. 현재 작동중인 항목, 더 이상 작동하지 않는 항목 등에 대한 개요를 확인하면 문제가 어디에 있는지 파악하는 데 도움이됩니다 ( Boost 테스트 는 가능한 해결책이며 직접 사용하지는 않지만 문서는 어떤 종류인지 이해하는 데 도움이 될 수 있습니다. 정보가 표시되어야 함).

디버깅에 적절한 도구를 사용하십시오. Unix에서 :

  • GDB 는 프로그램 충돌 위치를 알려줄 수 있으며 어떤 컨텍스트에서 볼 수 있습니다.
  • Valgrind 는 많은 메모리 관련 오류를 감지하는 데 도움이됩니다.
  • GCC를 사용하면 mudflap 을 GCC, Clang과 함께 사용할 수 있으며 10 월부터 MSVC 를 실험적으로 사용하면 Address / Memory Sanitizer를 사용할 수 있습니다 . Valgrind가 감지하지 못하는 오류를 감지 할 수 있으며 성능 손실이 더 적습니다. -fsanitize=address플래그 로 컴파일하여 사용합니다 .

마지막으로 평소의 것을 추천합니다. 프로그램이 더 읽기 쉽고, 유지 보수가 가능하며, 명확하고 깔끔할수록 디버깅이 가장 쉽습니다.


5

Unix에서는 valgrind문제를 찾는 데 사용할 수 있습니다. 무료이며 강력합니다. 직접 수행하려면 newdelete연산자를 오버로드하여 0xDEADBEEF각각의 새 객체 전후에 1 바이트가있는 구성을 설정할 수 있습니다 . 그런 다음 각 반복에서 일어나는 일을 추적하십시오. 이것은 모든 것을 잡는 데 실패 할 수 있지만 (그 바이트를 만질 수도 있다는 보장은 없습니다) 과거에는 Windows 플랫폼에서 저에게 효과적이었습니다.


1
글쎄, 이것은 1이 아니라 4 바이트가 될 것입니다 ...하지만 원칙은 괜찮습니다.
Jonas Wagner

1
비 간섭 힙 디버거에 연결할 수 있습니까? :-)
fredoverflow

그것을 위해 가십시오. 우리는 모두 여기에서 다른 사람들을 돕는 것이므로 도움이 될 수있는 모든 것이 추가되어야합니다.
wheaties

오버로드 만 new하고하는 것은 delete매우 유용 할 수 있습니다 사용은 -fsanitize=address컴파일러 문제에 대한 런타임 감지에 컴파일 및 디버깅을 쉽게 길을 만드는 화면으로 자동으로 메모리를 덤프로 더 나은 옵션입니다.
Tarick Welling

3

예, 포인터에 문제가 있습니다. 제대로 초기화되지 않은 것을 사용하고있을 가능성이 높지만 이중 해제 등으로 메모리 관리를 엉망으로 만들 수도 있습니다.

초기화되지 않은 포인터를 지역 변수로 사용하지 않으려면 의미있는 값으로 초기화 할 수있을 때 가능한 한 늦게 (항상 가능한 것은 아님) 선언 해보십시오. 코드를 검사하여 사용되기 전에 가치가있을 것임을 확신하십시오. 그것에 어려움이 있다면 널 포인터 상수 (일반적으로 NULL또는로 작성 됨 0) 로 초기화 하고 확인하십시오.

초기화되지 않은 포인터를 멤버 값으로 사용하지 않으려면 생성자에서 올바르게 초기화되고 복사 생성자 및 할당 연산자에서 올바르게 처리되는지 확인하십시오. init다른 초기화를 할 수 있지만 메모리 관리를 위해 함수에 의존하지 마십시오 .

클래스에 복사 생성자 또는 할당 연산자가 필요하지 않은 경우이를 전용 멤버 함수로 선언하고 정의하지 않을 수 있습니다. 명시 적으로 또는 암시 적으로 사용되는 경우 컴파일러 오류가 발생합니다.

해당되는 경우 스마트 포인터를 사용하십시오. 여기에서 가장 큰 장점은 그것들을 고수하고 일관되게 사용하면 쓰기를 완전히 피할 수 delete있고 아무것도 이중 삭제되지 않는다는 것입니다.

가능하면 C 스타일의 문자열과 배열 대신 C ++ 문자열과 컨테이너 클래스를 사용하십시오. 사용 고려.at(i)[i]경계 검사를 강제 할 수 있으므로 보다는을 것이 좋습니다 . [i]적어도 디버그 모드에서에서 경계를 확인하도록 컴파일러 또는 라이브러리를 설정할 수 있는지 확인하십시오 . 세분화 오류는 완벽하게 좋은 포인터 위에 가비지를 쓰는 버퍼 오버런으로 인해 발생할 수 있습니다.

이러한 작업을 수행하면 세분화 오류 및 기타 메모리 문제의 가능성이 상당히 줄어 듭니다. 의심 할 여지없이 모든 것을 고치는 데 실패 할 것입니다. 그래서 문제가 없을 때 valgrind를 사용하고, 문제가 없을 때 valgrind와 gdb를 사용해야합니다.


1

나는 이와 같은 문제를 해결하는 데 사용할 방법론을 모릅니다. 나는 당신의 프로그램의 행동이 정의되지 않았다는 바로 그 문제 때문에 하나를 생각해내는 것이 가능하지 않을 것이라고 생각합니다 (SEGFAULT가 일종의 UB로 인해 발생하지 않은 경우는 모릅니다) .

문제가 발생하기 전에 피할 수있는 모든 종류의 "방법론"이 있습니다. 한 가지 중요한 것은 RAII입니다.

그 외에도 최고의 심령 에너지를 던져야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.