2013-09-03 2 views
1

사용자 정의 수의 행에 대해 파스칼의 삼각형을 계산, 표시 및 저장하는 프로그램을 Delphi로 작성했습니다. 그것은 결국 액세스 위반이 발생한다는 것을 제외하고는 잘 동작합니다 (삼각형을 표시하고 저를 저장할 수 있습니다)! 다음은 메시지입니다.성공적인 실행 후에 델파이에서 액세스 위반이 발생했습니다.

'Project1.exe'모듈의 주소가 004031DB 인 액세스 위반이 있습니다. 주소 00000000 읽기.

절차에서 2D 동적 배열이 있지만 끝에 메모리를 해제합니다 (: = nil). 왜 여전히 내게 액세스 위반을주고 있습니까? 매우 좌절!

아카이브에 대한 답변을 검색했지만 적절한 답변을 찾을 수 없습니다. 어떤 도움이라도 대단히 감사하겠습니다. 여기

코드 (코드의 비트가 나는 조금 주저했다입니다.

procedure TForm1.btnPTClick(Sender: TObject); 
var 
    I, J, K, N, MidCol: integer; 
    PT: array of array of integer; 
    Row: string; 
begin 
    K := StrToInt(lblNumRows.Text); 
    N := StrToInt(lblNumRows.Text);//# values in row = row number 

    try 
     //initiatlize the array 
     SetLength(PT, K, (N*2)-1); 
     for I := 0 to K-1 do 
     for J := 0 to (N*2-1) do 
      PT[I,J] := 0; 

     MidCol := (N*2 div 2)-1;//MidCol already 0-based 
     for I := 0 to K-1 do 
     begin 
      if (I = 0) then 
      PT[I,MidCol] := 1//first row gets 1 in the middle column 
       else if I = 1 then 
       begin 
        PT[I,MidCol-1] := 1; 
        PT[I,MidCol+1] := 1; //first and last value in second = 1 
       end 
      else //if any other row 
       begin 

       //Middle column 
       PT[I, MidCol] := PT[I-1,MidCol-1] + PT[I-1,MidCol+1]; 
       //right triangle 
       for J := MidCol+1 to (N*2-1) do 
        begin 
        if (PT[I-1, J-1]=1) then//if already at the end of prev row 
         begin 
         PT[I,J] := 1; 
         break; 
         end 
        else 
         PT[I,J] := PT[I-1,J-1] + PT[I-1,J+1]; 
        end; 
       //left triangle 
       for J := MidCol-1 downto 0 do 
        begin 
        if (PT[I-1, J+1] = 1) then //if already at the end of prev row 
         begin 
         PT[I,J] := 1; 
         break; 
         end 
        else 
         PT[I,J] := PT[I-1,J-1] + PT[I-1,J+1]; 
        end; 
       end; 
     end; 

     //now add the pyramid to the memo 
     Application.ProcessMessages; 
     for I := 0 to K-1 do 
      begin 
      Row := ''; 
      for J := 0 to N*2-1 do 
       begin 
       if (PT[I,J] = 0) then Row := Row + ' ' 
       else Row := Row + IntToStr(PT[I,J]); 
       end; 
      Memo.Lines.Add(Row); 
      end; 

    finally 
    SetLength(PT, 0, 0); 
    end; 
end; 
+3

코드를 보지 않고 말하는 것은 완전히 불가능합니다. 당신은 out-of-bound 배열 인덱스에 쓰지 않으시겠습니까? 어쩌면 이전에 해제 된 (또는 아직 생성되지 않은) '객체'를 사용할 수 있습니까? –

+2

@ user1505202 : 언어가 관리하는 객체이므로 배열에서 : = nil을 제거하고 더 이상 필요하지 않을 때 적절하게 처리합니다. – AlexSC

+0

동일한 객체를 두 번 해제하려고하거나 이미 오류가 발생했기 때문에 오류가 발생했을 수 있습니다. 해방? –

답변

8
Read of address 00000000 

이것은 당신이 전무하다 포인터를 사용하여 메모리에 액세스를 시도하고 있음을 나타냅니다 알려면 왜 코드가 필요합니까? 현재 코드 만 있으므로 설명 할 수 있습니다.

디버거에서 프로그램을 실행하고 RTL/VCL 코드에서 오류가 발생할 경우 디버그 DCU를 활성화합니다. 디버거가 예외 발생시 중단되도록 구성되어 있는지 확인하십시오. 프로그램을 실행하고 오류를 트리거하십시오. 또는. 디버거는 어떤 nil 객체가 참조 해제되고 있는지 보여줍니다. 그렇다면 그 참조가 무의미한 이유를 찾아야합니다.

답변에 추가 한 코드에는 문제를 확실히 설명 할 수있는 버퍼 오버런이 있습니다. 귀하의 SetLength를 잘못되었습니다 및 읽어야합니다 : 그래서 힙 손상의 바운드 아웃

SetLength(PT, K, N*2); 

코드는 메모리에 기록합니다. 배열 경계에서 런타임 검사를 생성하도록 컴파일러에 요청해야합니다. 컴파일러의 범위 검사 옵션을 사용합니다. 당신이 그렇게했다면, 당신은이 오류를 스스로 발견했을 것입니다.

컴파일러가 자동으로 숨겨진 블록을 삽입하므로 try/finally 블록이 필요하지 않습니다. 하나가 충분할 때 두 가지 필요는 없습니다. 동적 배열은 변수가 범위를 벗어날 때 메모리가 삭제되는 관리되는 유형입니다.

+0

고마워요, @ 데이비드. 필요한 모든 것은 당신이 언급 한 교정이었습니다. 이제 작동합니다. 오류 없음. 이상하게도 실행을 완료하고 주어진 수의 행에 대해 올바른 삼각형을 표시하고 저장하도록 허용 한 다음 액세스 위반 오류가 발생했습니다.그러나 범위 검사를 켜면 성공적인 실행을 허용하지 않고 오류가 발생했습니다. – Serge

+0

고전적인 힙 손상입니다. 범위를 벗어난 쓰기는 나중에 해당 메타 데이터를 사용하는 힙 작업 중에 만 켜지는 힙 메타 데이터를 파괴합니다. 이 경우, 할당 해제를 종료하십시오. 범위 검사는 절대적으로 필요합니다. 그것을 사용했는지 확인하십시오. 마지막으로, 당신이 판단하는 대답을 가장 잘 받아 들여야합니다. –

+0

@DavidHeffernan의 답변이 가장 좋습니다. – Serge

1

F7 키를 눌러 디버거에서 프로젝트를 시작합니다. 주 메뉴에서 "오류 찾기 ..."옵션을 찾습니다 (델파이 7에서는 검색 메뉴 아래에 있음) 예외 : 004031DB에서 주소를 입력하십시오. 예외가 발생한 정확한 행을 표시합니다. Read of address 00000000은 일반적으로 nil 값을 가진 포인터를 사용하고 있음을 나타냅니다.

+0

고마워, @ Stijn. 당신이 델파이 XE에서 제안한 것을하려고 노력하고 있습니다 ...하지만 그렇게 할 수는 없습니다. "추적 추적"또는 "단계 추적"을 의미 했습니까? – Serge

+0

@user - 이것은 일반적인 경우에 대한 좋은 조언이지만 도움이되지는 않습니다. 귀하의 경우와 같이 메모리 덮어 쓰기는 즉각적인 문제를 일으킬 필요가 없습니다. –

관련 문제