Q&A

  • for 문의 이상행동.... -.-;;
for문을 돌리던 중.. 아주 이상한 경우를 만났씀다....
제가 머에 홀린 건지..... 아님... 먼가 큰 실수를 하고... 허우대고 있는 건지....
좀 봐주시길....

-------- 이상하게 도는 포문의 예 ---------------
for j := 0 to 30 do
begin
      MaxArray[j] := pfData[j];
      MinArray[j] := PfData[j];
end;

for i:= 0 to rowCount - 1 do
begin
      try
      begin                                                // Begin of Try
            for j := 0 to 30 do                           // <- 이상항 부분.
            begin                                          // begin of For  
                 If pfdata[i] > MaxArray[j] then
                     MaxArray[j] := pfData[i];
                 If pfData[i] < MinArray[j] then
                     MinArray[j] := pfData[i];
            end;                                           // end of For
           AvgVal := AvgVal + pfData[i];
       end                                                 // end of Try
       except
           result := False;
       end;                                                // end of Try Except;
end;            

요걸 돌려 보믄... J 인자를 사용하는 포문이 두개가 있는데여....
위에 것은 잘 돌아 갑니다....  근데 아래 안쪽 포문의 J 값을 Watch 해 보면.... -.-;; 31에서 시작합니다. 그러면서 downto 포문 돌아 가드시....
하나씩 줄더라구여..... 게다가 더 황당한건.... MaxArray나 minArray는 0에서 30번까지 할당된 어레인데.... MaxArray[31] (J가 31 부터 시작하는 이상한 짓을 하므로....)을 억세스 할텐데도.... 바이올레이션 에러가 않난다는 것이죠....
게다가.... 이게 거꾸로 도나 해서.... for문의 to를 downto로 바꿔보았더니....
^^ 그냥 않돌더라구여..... (않돌아야 정상이져.... 0에서 30으로는 내려갈 수 없으니까여.... -.-;;) 글구 아래 것도 좀 참조해 주세여.....

------------------- 잘 돌아가는 포문의 예 -------------------
요 아래 포문은.... 위에서 J 값 Watch가 먼가 이상해서..... 중간에서 j 값을 파일로 쓰게끔 해 본겁니다..... (중간에....Writeln(j)에서 써주는 거져...)
근데.... -.-;; 파일을 읽어 보니까.... 0-30까지 잘 돌았더라구여....T.T
먼가 이상해서.... 다시 J를 Watch 걸구 해보니까.....
중간에 WriteLn 있는 경우에는.... j가 0에서 30까지 잘 돌아 갑니다....
근데... 없는 경우에는 잘 않돌아 갑니다....
왜 그렇져?

AssignFile(F,'Texttt.txt');
Rewrite(f);

for i:= 0 to rowCount - 1 do
begin
      try
      begin
          for j := 0 to 30 do
          begin
                writeln(f, intTostr(j));
                If pfdata[i] > MaxArray[j] then
                     MaxArray[j] := pfData[i];
                If pfData[i] < MinArray[j] then
                     MinArray[j] := pfData[i];
          end;
         AvgVal := AvgVal + pfData[i];
      end
      except
           result := False;
      end;
end;
CloseFile(f);
6  COMMENTS
  • Profile
    이경문 2002.03.28 11:48
    이렇게 나오는 원인을 설명드리기 전에 어셈블리 코드를 먼저 설명드리겠습니다.
    N 번반복해야 하는 코드는 어셈으로 다음과 같이 표현됩니다.

    mov cx, N
    repeat:
    ;어쩌구 저쩌구
    dec dc
    jnz repeat

    즉 intel CPU에서 단순 반복문은 cx나 ecx를 이용하여 감소를 하면서 처리하면 속도가 빨라 집니다. 이는 컴파일러를 작성할 때 유명한 최적화방법중의 하나입니다.

    이것에 델파이라는 컴파일러에서도 적용이 됩니다.
    말씀하신 소스를 어셈블리로 분석해 보았는데 그 결과
    31번을 돌기 위하여 count를 위하여 ecx라는 register를 사용하였고(위에서 설명된 바와 같이 초기값이 31이 됨) 이것이 마치 j라는 변수로 보여진 것 같습니다.
    그리고 실제 pddata, MinArray, MaxArray라는 변수를 access하는 것은 범용 register가 사용되었었습니다.

    즉 for j := 0 to 30 do.... 라고 했을 때
    단순반복을 위해 ecx register에 31값이 들어 가고
    배열변수의 포인터를 access하기 위해 eax, ebx, edx pointer가 먼저 초기화된다는 거죠.
    델파이에서 마치 j라는 변수는 ecx인것처럼 보여진 것 갓군요.
    실제 처리되는 것은 MaxArray[0], MinArray[0], pfdata[0]부터 처리됩니다.

    쉽게 말해서 pseudo 어셈블리 코드로 풀어 쓴다면 다음과 같이 표현될 수 있습니다.

    ecx := 31; // count만을 처리함
    ebx := &pfdata[0]; // 시작포인터
    eax := @MaxArray[0]; // 시작포인터
    edx := @MinArray[0]; // 시작포인터

    repeat:

    If ebx^ > eax^ then
      eax^ := ebx^;
    If ebx^ < edx^ then
      edx^:= ebx^;

    inc(ebx);
    inc(eax);
    inc(edx);
    dec(ecx)
    jnz repeat

    어떻게 보면 델파이의 버그라고 할 수도 있고
    어떻게 보면 델파이 컴파일러 최적화의 단면이라고도 볼 수 있습니다.
    이해가 되셨는지요... ^^



  • Profile
    장석정 2002.03.28 01:07
    정확히는 모르겠지만....
    정말 델파이 버그 같네여.... 참고로 저는 델파이 4를 사용하고 있습니다.
    아래 코드 가지고 테스트 해 봐도....
    비슷한 짓을 합니다.... ^^;
    단일 루프 일때는 괜찮은거 같은데....
    중첩 루프 일때 그러는거 같더라구여....
    왜 그러는지 모르지만..... 요 아래 코드에서는 바깥쪽 for문에서 비슷한 짓거리를 하더군여.....
    근데... 아래 코드를 가지고 테스트 하면....
    메모 창에 값은 제대루 나옴니다.
    나온 값은 코드 아래쪽 확인해 주세염....

    -----  테스트 해본 코드 ------------------------
    procedure TForm1.Button1Click(Sender: TObject);
    var
       i, j : integer;
       intarr : array[0..30] of integer;
    begin

    for i := 0 to 30 do
    begin
       intarr[i] := i;
    end;

       for i := 0 to 1 do
       begin
           for j := 0 to 30 do
           begin               // (downto로 동작한다면 실제 들어 있는 값은 거꾸로 나오겠져...
               Memo1.Lines.Add('[J에 들어 있는 값 :'+ IntToStr(intArr[j])+'] : ' + IntToStr(j));
           end;
       end;
    end;

    ---------결과 값-----------------------
    [J에 들어 있는 값 :0] : 0
    [J에 들어 있는 값 :1] : 1
    [J에 들어 있는 값 :2] : 2
    [J에 들어 있는 값 :3] : 3
    [J에 들어 있는 값 :4] : 4
    [J에 들어 있는 값 :5] : 5
    [J에 들어 있는 값 :6] : 6
    [J에 들어 있는 값 :7] : 7
    [J에 들어 있는 값 :8] : 8
    [J에 들어 있는 값 :9] : 9
    [J에 들어 있는 값 :10] : 10
    [J에 들어 있는 값 :11] : 11
    [J에 들어 있는 값 :12] : 12
    [J에 들어 있는 값 :13] : 13
    [J에 들어 있는 값 :14] : 14
    [J에 들어 있는 값 :15] : 15
    [J에 들어 있는 값 :16] : 16
    [J에 들어 있는 값 :17] : 17
    [J에 들어 있는 값 :18] : 18
    [J에 들어 있는 값 :19] : 19
    [J에 들어 있는 값 :20] : 20
    [J에 들어 있는 값 :21] : 21
    [J에 들어 있는 값 :22] : 22
    [J에 들어 있는 값 :23] : 23
    [J에 들어 있는 값 :24] : 24
    [J에 들어 있는 값 :25] : 25
    [J에 들어 있는 값 :26] : 26
    [J에 들어 있는 값 :27] : 27
    [J에 들어 있는 값 :28] : 28
    [J에 들어 있는 값 :29] : 29
    [J에 들어 있는 값 :30] : 30
    [J에 들어 있는 값 :0] : 0
    [J에 들어 있는 값 :1] : 1
    [J에 들어 있는 값 :2] : 2
    [J에 들어 있는 값 :3] : 3
    [J에 들어 있는 값 :4] : 4
    [J에 들어 있는 값 :5] : 5
    [J에 들어 있는 값 :6] : 6
    [J에 들어 있는 값 :7] : 7
    [J에 들어 있는 값 :8] : 8
    [J에 들어 있는 값 :9] : 9
    [J에 들어 있는 값 :10] : 10
    [J에 들어 있는 값 :11] : 11
    [J에 들어 있는 값 :12] : 12
    [J에 들어 있는 값 :13] : 13
    [J에 들어 있는 값 :14] : 14
    [J에 들어 있는 값 :15] : 15
    [J에 들어 있는 값 :16] : 16
    [J에 들어 있는 값 :17] : 17
    [J에 들어 있는 값 :18] : 18
    [J에 들어 있는 값 :19] : 19
    [J에 들어 있는 값 :20] : 20
    [J에 들어 있는 값 :21] : 21
    [J에 들어 있는 값 :22] : 22
    [J에 들어 있는 값 :23] : 23
    [J에 들어 있는 값 :24] : 24
    [J에 들어 있는 값 :25] : 25
    [J에 들어 있는 값 :26] : 26
    [J에 들어 있는 값 :27] : 27
    [J에 들어 있는 값 :28] : 28
    [J에 들어 있는 값 :29] : 29
    [J에 들어 있는 값 :30] : 30
  • Profile
    돌멩이 2002.03.27 23:14
    크게 문제 될건 없다고 생각합니다.

    실제 값이 잘못된것이 아니라
    디버깅 주에 가져오는 값이 잘못된 것입니다.

    이상하다는 For문 안에서
    Showmessage(IntToStr(j));
    해 보시면 인자가 제대로 되고 있다는 것을 알수 있을 것입니다.

    이 문제는 델3부터 있든 문제 였습니다.
    제가 델3부터 사용했기 때문에 이전에는 모르지만요..

    도움이 되었으면 합니다.
  • Profile
    이승혁 2002.03.27 22:39
    혹시 델6 쓰시지 않나여?

    저도 비슷한 문제때문에 한참 고민했었는데...
    Low, High를 했는데 0부터 지정한 마지막 값까지 나오는게 아니라
    마지막 값부터 시작하고... to가 아니라 downto로 작동하고...
    그러면서 범위를 벗어나는데 신기하게도 에러가 안나고...
    델파이 버그같슴다...

    그래서 어쩔 수 없이 편법으로 repeat를 썼습니다...
    그건 잘 되더군요...

    델파이 새 버전이 나오면서 점점 더 이상해져서 고민임다...
    델파이를 버려야 하는건지 예전 버전을 써야 하는건지...


    글고
    델6 업데이트 팩2 업데이트 하실 분들은 참고하세여...
    Undo가 무지 이상하게 작동하고(Undo 분명히 했고, 화면에도 반영됐는데
    컴파일하면 에러나고 저장하고 다시 불러오면 이전 내용 그대로 불러옴)
    팩1까지는 없었던 에디터 버그들이 종종 발견됩니다...
    가능하면 업데이트 안하셨으면 하는 소망이 있는데...
  • Profile
    김규한 2002.03.27 22:36
    요거 대신에 Application.ProcessMessage;
    라구 함 해보세요.
    그리고, try... except end구문에서 try와 except사이에는
    begin.. end 앙하셔도 됩니다. ^^

    ProcessMessage란 저도 잘은 모르지만,
    이 메소드를 호출함으로써 윈도우가 현재 메시지 큐에 있는 메시지를 처리
    할 수 있도록 한다구 나와 있군요.
    ProcessMessage는 윈도우 메시지 큐에 아무것도 남지 않을 때까지
    루프를 돌면서 메시지를 처리하고 모든 처리가 끝나면 제어권을 어플리케이션
    에 넘긴다라구도요 ^^;

    만약 안되면? 저도 몰라요 ㅜㅡ;
    그럼.


  • Profile
    김규한 2002.03.27 22:41
    참고로..

    Application.ProcessMessage;라구
    치고 케럿을 옮겨서 F1 누르면 자세한 도움말이 나와 있네요.
    while문으로 루프돌리면서 메세지들을 무시하거나 무시하지 않는
    예제까지요 ^^;

    그럼 즐코요.