문제

그래서 도움이 필요해요.저는 C++로 프로젝트를 진행하고 있습니다.그러나 나는 어떻게든 내 힙을 손상시킬 수 있었다고 생각합니다.이것은 내가 추가했다는 사실을 기반으로합니다. std::string 클래스에 다른 클래스의 값을 할당 std::string:

std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;

스택 덤프로 인해 시스템이 충돌합니다.그래서 기본적으로 나는 멈추다 모든 코드와 메모리 관리 관련 내용을 살펴보고 내가 어디를 망쳤는지 알아보세요.코드베이스는 여전히 작으므로(약 1000줄) 쉽게 수행할 수 있습니다.

그래도 이런 일들이 너무 부담스러워서 거기에 던져야겠다는 생각이 들었습니다.나는 Linux 시스템을 사용하고 있으며 valgrind, 그리고 내가 무엇을 하고 있는지 완전히 알지 못하는 동안, 그것은 std::string의 소멸자가 유효하지 않은 free입니다.나는 Google 검색에서 '힙 손상'이라는 용어를 얻었음을 인정해야 합니다.이런 종류의 일반적인 목적의 기사도 감사하겠습니다.

(이전에 rm -rf ProjectDir, C#에서 다시 수행하세요 :D)

편집하다:명확하게 밝히지는 않았지만 제가 요청하는 것은 이러한 종류의 기억 문제를 진단하는 방법에 대한 조언입니다.나는 std::string 내용이 옳다는 것을 알고 있으므로 내가 한 일입니다(또는 버그이지만 선택에는 문제가 없습니다).나는 내가 작성한 코드를 확인할 수 있을 것이라고 확신하며 여러분의 매우 똑똑한 사람들은 곧 문제를 발견할 것입니다. 그러나 나는 이런 종류의 코드 분석을 말하자면 내 '도구 상자'에 추가하고 싶습니다.

도움이 되었습니까?

해결책

다음은 문제를 해결할 수 있는 상대적으로 저렴한 메커니즘입니다.

  1. 내 모습을 지켜봐주세요 힙 손상 질문 - 답변이 흔들리는 대로 업데이트 중입니다.첫 번째는 균형을 맞추는 것이었습니다. new[] 그리고 delete[], 하지만 당신은 이미 그렇게 하고 있습니다.
  2. 주다 발그린드 좀 더 가보자;이는 훌륭한 도구이며 Windows에서만 사용할 수 있었으면 좋겠습니다.나는 당신의 프로그램 속도를 약 절반 정도만 늦추는데, 이는 Windows에 상응하는 것에 비해 꽤 좋습니다.
  3. 사용을 생각해 보세요. Google 실적 도구 대체 malloc/new로.
  4. 모든 개체 파일을 정리하고 다시 시작하셨나요?아마도 make 파일은 ..."최적화되지 않은"
  5. 넌 아니야 assert()코드에서 충분합니다.보지도 않고 그걸 어떻게 알겠어요?치실처럼 누구도 assert()그들의 코드에서는 충분합니다.개체에 대한 유효성 검사 함수를 추가하고 메서드 시작 및 메서드 끝에서 이를 호출합니다.
  6. 당신은 컴파일 -벽?그렇지 않다면 그렇게 하십시오.
  7. 다음과 같은 린트 도구를 찾으십시오. PC-Lint.귀하와 같은 작은 앱이 PC-lint 데모 페이지에 표시되므로 구매가 불가능합니다!
  8. 포인터를 삭제한 후 포인터를 NULL로 처리하고 있는지 확인하세요.매달린 포인터를 좋아하는 사람은 없습니다.선언되었지만 할당되지 않은 포인터와 동일한 작업입니다.
  9. 배열 사용을 중단하세요.사용 벡터 대신에.
  10. 원시 포인터를 사용하지 마십시오.사용 스마트 포인터.사용하지 마세요 auto_ptr!그 것은...놀라운;그 의미는 매우 이상합니다.대신 다음 중 하나를 선택하세요. 스마트 포인터 강화, 또는 뭔가 로키 도서관.

다른 팁

한때 우리는 valgrind, purify 등의 모든 일반적인 기술을 피하는 버그를 겪었습니다.충돌은 메모리가 많고 입력 데이터 세트가 큰 시스템에서만 발생했습니다.

결국 우리는 디버거 감시 지점을 사용하여 이를 추적했습니다.여기서 절차를 설명하려고 합니다.

1) 실패의 원인을 찾아라.예제 코드를 보면 "exampleString"에 대한 메모리가 손상되어 쓸 수 없는 것 같습니다.이 가정을 계속해보자.

2) "exampleString"이 문제 없이 사용되거나 수정된 ​​마지막 위치에 중단점을 설정합니다.

3) 'exampleString'의 데이터 멤버에 watch point를 추가합니다.내 버전의 g++에서는 문자열이 다음 위치에 저장됩니다. _M_dataplus._M_p.우리는 이 데이터 멤버가 언제 변경되는지 알고 싶습니다.이에 대한 GDB 기술은 다음과 같습니다.

(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb)  watch *$3
Hardware watchpoint 1: *$3

여기서는 분명히 g++ 및 gdb와 함께 Linux를 사용하고 있지만 메모리 감시 지점은 대부분의 디버거에서 사용할 수 있다고 생각합니다.

4) 감시 포인트가 트리거될 때까지 계속합니다.

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where

GDB where 명령은 수정 결과를 보여주는 역추적을 제공합니다.이는 완전히 합법적인 수정이므로 계속 진행하세요. 또는 운이 좋다면 메모리 손상으로 인한 수정이 될 것입니다.후자의 경우 이제 코드를 검토할 수 있습니다. 정말 문제를 일으키고 해결되기를 바랍니다.

버그의 원인은 음수 인덱스를 사용한 배열 액세스였습니다.인덱스는 배열 크기를 모듈로 하는 'int'에 대한 포인터의 캐스트 결과였습니다.이 버그는 valgrind et al.에 의해 놓쳤습니다.해당 도구에서 실행될 때 할당된 메모리 주소는 "> MAX_INT"이므로 음수 인덱스가 발생하지 않았습니다.

아, 문제를 디버깅하는 방법을 알고 싶다면 간단합니다.먼저 죽은 닭을 구하세요.그 다음에, 흔들어 시작해.

진지하게, 나는 이런 종류의 버그를 추적하는 일관된 방법을 찾지 못했습니다.잠재적인 문제가 너무 많기 때문에 간단히 검토할 수 있는 체크리스트가 없습니다.그러나 다음을 권장합니다.

  1. 디버거에 익숙해지세요.
  2. 의심스러워 보이는 것이 있는지 확인하기 위해 디버거를 여기저기 돌아다니기 시작하세요.특히 그 동안 무슨 일이 일어나고 있는지 확인하십시오. exampleString = hello; 선.
  3. 실제로 충돌이 발생하는지 확인하십시오. exampleString = hello; 일부 둘러싸는 블록을 종료할 때는 그렇지 않습니다(소멸자가 실행될 수 있음).
  4. 수행 중인 포인터 마술을 확인하세요.포인터 연산, 캐스팅 등
  5. 모든 할당 및 할당 취소를 확인하여 일치하는지 확인하세요(이중 할당 취소 없음).
  6. 스택의 개체에 대한 참조나 포인터를 반환하지 않는지 확인하세요.

시도해 볼 다른 것들도 많이 있습니다.나는 다른 사람들도 아이디어를 내놓을 것이라고 확신합니다.

시작할 수 있는 곳:

Windows에서 Visual C++6을 사용하는 경우(요즘에는 아무도 사용하지 않기를 바랍니다) std::string은 스레드로부터 안전하지 않으며 이런 일이 발생할 수 있습니다.

다음은 메모리 누수 및 손상의 일반적인 원인을 많이 설명하는 기사입니다.

이전 직장에서는 이 문제를 해결하기 위해 Compuware Boundschecker를 사용했습니다.상업용이고 비용이 많이 들기 때문에 선택 사항이 아닐 수도 있습니다.

다음은 유용할 수 있는 몇 가지 무료 라이브러리입니다.

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

도움이 되길 바랍니다.메모리 손상은 짜증나는 곳입니다!

힙 손상일 수도 있지만 스택 손상일 가능성도 마찬가지입니다.짐의 말이 맞아요.좀 더 많은 맥락이 필요합니다.이 두 소스 라인은 우리에게 고립된 내용을 많이 알려주지 않습니다.이 문제를 일으키는 원인은 얼마든지 있을 수 있습니다(이것이 C/C++의 진정한 즐거움입니다).

코드를 게시하는 것이 편하다면 모든 코드를 서버에 올려 놓고 링크를 게시할 수도 있습니다.나는 당신이 그런 식으로 더 많은 조언을 얻게 될 것이라고 확신합니다(일부는 의심할 여지없이 귀하의 질문과 관련이 없습니다).

이 코드는 단순히 내 프로그램이 실패한 예입니다(스택에 할당되었습니다, Jim).나는 실제로 '내가 뭘 잘못했는지'를 찾는 것이 아니라 '내가 뭘 잘못했는지 어떻게 진단하는지'를 찾고 있습니다.사람에게 낚시와 그 모든 것을 가르치십시오.질문을 보았지만 충분히 명확하지 않았습니다.편집 기능을 주셔서 감사합니다.:')

또한 실제로 std::string 문제를 해결했습니다.어떻게?이를 벡터로 바꾸고 컴파일한 다음 문자열을 다시 바꿉니다.그것 ~였다 거기에서 지속적으로 충돌이 발생했는데... 그럴 수 없었음에도 불구하고 문제가 해결되었습니다.거기 뭔가 불쾌한 것이 있는데, 뭔지 잘 모르겠습니다.그래도 힙에 메모리를 수동으로 할당한 경우를 확인하고 싶었습니다.

 this->map = new Area*[largestY + 1];
 for (int i = 0; i < largestY + 1; i++) {
     this->map[i] = new Area[largestX + 1];
 }

그리고 삭제합니다:

for (int i = 0; i < largestY + 1; i++) {
    delete [] this->map[i];
}
delete [] this->map;

이전에는 C++로 2D 배열을 할당한 적이 없습니다.작동하는 것 같습니다.

또한 실제로 std::string 문제를 해결했습니다.어떻게?이를 벡터로 바꾸고 컴파일한 다음 문자열을 다시 바꿉니다.그곳에서 지속적으로 충돌이 발생했고, 그럴 수 없었음에도 불구하고 문제가 해결되었습니다.거기 뭔가 불쾌한 것이 있는데, 뭔지 잘 모르겠습니다.

정말 닭을 흔든 것 같습니다.당신이 모른다면 지금은 작동하지만 여전히 손상되어 나중에 다시 물릴 가능성이 거의 보장됩니다(복잡성을 더 추가한 후에).

정화를 실행합니다.

건드리지 말아야 할 메모리를 방해하거나, 해제하지 않음으로써 메모리가 누출되거나, 이중 해제 등을 하는 경우 이를 보고하는 거의 마법에 가까운 도구입니다.

이는 기계어 코드 수준에서 작동하므로 소스 코드가 없어도 됩니다.

제가 참여했던 벤더 컨퍼런스 콜 중 가장 즐거웠던 것 중 하나는 Purify가 코드에서 메모리 누수를 발견했을 때였습니다. 우리는 "foo() 함수에서 메모리를 해제하지 않을 가능성이 있습니까?"라고 묻고 다음과 같은 말을 들을 수 있었습니다. 그들의 목소리에 놀랐다.

그들은 우리가 신들을 디버깅하고 있다고 생각했지만 우리가 그들의 코드를 사용하기 전에 Purify를 실행할 수 있도록 비밀을 그들에게 알려주었습니다.:-)

http://www-306.ibm.com/software/awdtools/purify/unix/

(꽤 비싸지만 무료로 평가판을 다운로드할 수 있습니다)

내가 자주 사용하는 디버깅 기술 중 하나는(가장 극단적인 이상한 경우를 제외하고) 분할하고 정복하는 것입니다.프로그램이 현재 특정 오류로 인해 실패하는 경우 어떤 방식으로든 프로그램을 반으로 나누고 여전히 동일한 오류가 있는지 확인하세요.분명히 비결은 프로그램을 어디에 나눌 것인지 결정하는 것입니다!

귀하의 예는 오류가 발생한 위치를 판단하기에 충분한 컨텍스트를 표시하지 않습니다.다른 사람이 귀하의 예를 시도해 본다면 잘 작동할 것입니다.따라서 프로그램에서 우리에게 보여주지 않은 추가 항목을 최대한 제거하고 작동하는지 확인하십시오.그렇다면 실패하기 시작할 때까지 한 번에 조금씩 다른 코드를 다시 추가하십시오.그렇다면 방금 추가한 것이 문제일 가능성이 높습니다.

프로그램이 다중 스레드인 경우에는 더 큰 문제가 발생할 수 있습니다.그렇지 않다면 이런 식으로 범위를 좁힐 수 있어야 합니다.행운을 빌어요!

Boundschecker 또는 Purify와 같은 도구 외에 이와 같은 문제를 해결하는 가장 좋은 방법은 코드를 잘 읽고 작업 중인 코드에 익숙해지는 것입니다.

메모리 손상은 해결하기 가장 어려운 문제 중 하나이며 일반적으로 이러한 유형의 문제는 디버거에서 몇 시간/일을 소비하고 "이봐, 포인터 X가 삭제된 후에 사용되고 있습니다!"와 같은 것을 알아차림으로써 해결됩니다.

도움이 된다면 경험을 쌓으면서 더 잘할 수 있는 것입니다.

배열에 대한 메모리 할당은 올바른 것 같지만 배열에 액세스하는 모든 위치도 확인하세요.

내가 볼 수 있듯이 귀하의 코드에는 오류가 없습니다.말했듯이 더 많은 맥락이 필요합니다.

아직 시도하지 않았다면 gdb(gcc 디버거)를 설치하고 -g를 사용하여 프로그램을 컴파일하십시오.이는 gdb가 사용할 수 있는 디버깅 기호로 컴파일됩니다.gdb가 설치되면 프로그램(gdb)으로 실행하세요. 이것 gdb 사용에 유용한 치트키트입니다.

버그를 생성하는 함수에 중단점을 설정하고 exampleString의 값이 무엇인지 확인합니다.또한 exampleString에 전달하는 매개변수에 대해서도 동일한 작업을 수행합니다.최소한 std::strings가 유효한지 알려주어야 합니다.

나는에게서 답을 찾았다. 이 기사 포인터에 대한 좋은 가이드가 될 것입니다.

내가 알 수 있는 한 귀하의 코드는 정확합니다.exampleString이 설명과 같은 클래스 범위를 갖는 std::string이라고 가정하면 그런 식으로 초기화/할당할 수 있어야 합니다.아마도 다른 문제가 있는 것일까요?어쩌면 실제 코드 조각이 맥락을 파악하는 데 도움이 될 수도 있습니다.

질문:exampleString은 new로 생성된 문자열 객체에 대한 포인터입니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top