2016-02-18 10 views
7

を開始するために、終了値から実行私はエンバカデロのDelphi 2010の2サイクルと単純なコードでシンプルなアプリ書いている:ウォッチリストによるとデルファイ「のために...」の文は、値

procedure TForm1.Button1Click(Sender: TObject); 
var 
a:array [0..255] of integer; 
i:integer; 
k,q:integer; 
begin 
k:=0; 
for I := 0 to 255 do 
begin 
    a[i]:=i; 
end; 

for I := 0 to 255 do 
begin 
    q:= a[i]; 
    k:=k+q; 
end; 
Label1.Caption:=inttostr(k); 
end; 

を、第2サイクル変数 "i"は値256から始まり0(256,255,254、...、0)になりますが、配列の要素は正しい(0,1,2,3、...)。変数 "i"はローカルにのみ宣言され、グローバル変数は宣言されません。 これはなぜ起こりますか?それは正常な行動ですか?

+0

サイドノート:*マジックナンバー*を使用しないでください:i:= 0〜Length(a)do –

+0

生産コードではなくちょっと変わった動作テストです。長さ(a)もこのように動作します。 –

+4

ちょっとした検索では、コードの意味を変えない限り、適用されるのはDelphiコンパイラによる最適化だとヒットしているはずです。 –

答えて

12

短い答えはコンパイラの最適化のためです。長い答えは:

Pascalコードでは、整数Iは2つ(実際には3つ)の目的を持っています。まず、ループの制御変数(またはループカウンタ)です。つまり、ループの実行回数を制御します。次に、配列aのインデックスとして機能します。また、最初のループでは、配列要素に割り当てられた値としても機能します。マシンコードにコンパイルされると、これらの役割は異なるレジスタによって処理されます。

最適化がコンパイラ設定で設定されている場合、コンパイラは、制御変数を開始値から0に向かって下げるコードを作成します。を変更せずに、を実行します。これはゼロ以外の値との比較を避けることができるため、より高速です。

最初のループの分解を以下で、変数Iの役割は次のように処理されることがわかります。

  • レジスタアレイに を割り当てるループ制御変数と値としてeax作用要素
  • レジスタedxは、配列要素(1ターンに4 (バイト)でインクリメント)へのポインタです。

解体:

Unit25.pas.34: for I := 0 to 255 do 
005DB695 33C0    xor eax,eax    // init 
005DB697 8D9500FCFFFF  lea edx,[ebp-$00000400] 
Unit25.pas.36: a[i]:=i; 
005DB69D 8902    mov [edx],eax   // value assignment 
Unit25.pas.37: end; 
005DB69F 40    inc eax     // prepare for next turn 
005DB6A0 83C204   add edx,$04    // same 
Unit25.pas.34: for I := 0 to 255 do 
005DB6A3 3D00010000  cmp eax,$00000100  // comparison with end of loop 
005DB6A8 75F3    jnz $005db69d   // if not, run next turn 

eaxは、2つの役割を持っているので、それが上向きにカウントしなければなりません。ループカウントを管理するには、ループごとに3つのコマンド(inc eax,cmp eax, $00000100およびjnz $005db69d)が必要です。 Iを要素に割り当てられていない以外は第二のループの分解において

は、可変Iの役割は、第一のループと同様と似て処理されます。したがって、ループ制御はループカウンタとしてのみ機能し、下向きに実行することができます。

  • レジスタeaxレジスタedxが(ターンあたり4 (バイト)でインクリメント)配列要素

分解へのポインタである

  • 可変ループ制御である:こと

    Unit25.pas.39: for I := 0 to 255 do 
    005DB6AA B800010000  mov eax,$00000100  // init loop counter 
    005DB6AF 8D9500FCFFFF  lea edx,[ebp-$00000400] 
    Unit25.pas.41: q:= a[i]; 
    005DB6B5 8B0A    mov ecx,[edx] 
    Unit25.pas.42: k:=k+q; 
    005DB6B7 03D9    add ebx,ecx 
    Unit25.pas.43: end; 
    005DB6B9 83C204   add edx,$04 // prepare for next turn 
    Unit25.pas.39: for I := 0 to 255 do 
    005DB6BC 48    dec eax  // decrement loop counter, includes intrinsic comparison with 0 
    005DB6BD 75F6    jnz $005db6b5 // jnz = jump if not zero 
    

    注この場合、ループカウントを管理するのに2つのコマンドしか必要ありません:dec eaxjnz $005db6b5

    時計ウィンドウでは、変数Iが最初のループ中にインクリメント値として表示されますが、2番目のループではE2171 Variable 'i' inaccessible here due to optimizationと表示されます。以前のバージョンでは、私はあなたが見ると信じている値が減少していることを思い出していました。

  • +0

    詳細な回答ありがとうございました! –

    8

    私はあなたの正確なコードをコピーしました。私がそれを実行したとき、変数 "i"は通常サイクルの両方でカウントされます。第2サイクルをステップごとに実行しましたか? "i"は最初のサイクルのために2番目のサイクルの開始時には実際に256ですが、2番目のサイクルが開始するとすぐに "i"が0になり、通常255になります。

    なぜそれは256から0に数えますか?

    UPDATE: 私もこの考えていないが、ここで私は信じてあなたの説明です:http://www.delphigroups.info/2/45/418603.html

    「それはコンパイラの最適化だ - あなたが使用していない 『あなたのループ 内の私は』あなたのループカウント は依然として正確です... "

    +1

    Delphi XE4ではすべて正常に動作します。 Delphi 2010でのみ気付いた。「i」は開始時に256回、第2回目に255回、第3回に254回など...ただし、それはウォッチリストのみです。いくつかの出力に "i"を書き込もうとすると、すべてが時折通常の順番(0,1,2、...)になります。「どうやって、なぜ256から0になるのでしょうか? - 私も、これは何についての質問なのですか) –

    +0

    私はあなたの質問にグーグルで答えてその理由を見つけました。私は今までこのコンパイラの最適化については知らなかった。 – Vepir

    +0

    Googleにしようとしましたが、 "for ... downto"構文ヘルプが必ず出てきます。ありがとうございます=) –

    関連する問題