2016-02-18 12 views
7

Sto scrivendo una semplice applicazione in Embarcadero Delphi 2010. Un semplice codice con due cicli:Delphi "per ... a" dichiarazione va da valore finale al valore iniziale

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; 

Secondo selezione, nella variabile del secondo ciclo "i" inizia dal valore 256 e passa a 0 (256, 255, 254, ..., 0), ma gli elementi dell'array sono corretti (0, 1, 2, 3, ...). Variabile "i" dichiarata solo localmente, nessuna variabile globale. Perché succede? È un comportamento normale?

+0

Nota a margine: non utilizzare * numeri magici *: 'per i: = 0 a Length (a) do' –

+0

Solo uno strano test di comportamento, non un codice di produzione. Anche la lunghezza (a) funziona in questo modo. –

+4

Un po 'di googling dovrebbe aver dato un sacco di hit che ti dicono che è un'ottimizzazione del compilatore Delphi che viene applicato solo se non altera il significato del tuo codice. –

risposta

12

La risposta breve è a causa di ottimizzazione del compilatore. La risposta lunga è:

Nel codice Pascal, il numero intero I ha due (in realtà tre) scopi. Innanzitutto, sono i loop variabile di controllo (o contatore di loop), ovvero controlla quante volte viene eseguito il ciclo. In secondo luogo, funge da indice per l'array a. E nel primo ciclo funge anche da valore assegnato agli elementi dell'array. Quando vengono compilati su un codice macchina, questi ruoli vengono gestiti da registri diversi.

Se l'ottimizzazione è impostata nelle impostazioni del compilatore, il compilatore crea un codice che decrementa la variabile di controllo da un valore iniziale basso a zero, se è possibile farlo, senza modificare il risultato finale. Questo lo fa, perché un confronto con un valore diverso da zero può essere evitato, quindi essere più veloce.

Seguendo lo smontaggio del primo ciclo, si può vedere che i ruoli delle variabili I sono gestite come:

  • Register eax agisce come variabile di controllo valoreed essere assegnate a matrice elementi
  • Il registro edx è puntatore all'elemento matrice (incrementato con 4 (byte) per turno)

smontaggio:

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 

Da eax ha due ruoli, essa deve contare verso l'alto.Si noti che per gestire il conteggio del ciclo sono necessari tre comandi per ciascun ciclo: inc eax, cmp eax, $00000100 e jnz $005db69d.

Nel smontaggio del secondo anello, i ruoli delle variabili I vengono gestiti in modo simile come nel primo ciclo, tranne I non è assegnato agli elementi. Pertanto il controllo del loop agisce solo come un contatore di loop e può essere eseguito verso il basso.

  • Register eax è controllo ad anello variabile
  • Register edx è puntatore all'elemento array (incrementato con 4 (bytes) per turno)

smontaggio:

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 

noti che in questo caso sono necessari solo due comandi per gestire il conteggio dei cicli: dec eax e jnz $005db6b5.

In Delphi XE7, nella finestra Orologi, la variabile I viene visualizzata durante il primo ciclo come valori di incremento ma durante il secondo ciclo come E2171 Variable 'i' inaccessible here due to optimization. Nelle versioni precedenti ricordo che mostrava valori decrescenti che credo tu veda.

+0

Grazie mille per la risposta dettagliata! –

8

Ho copiato il codice esatto e quando lo eseguo la variabile "i" conta normalmente in entrambi i cicli. Hai eseguito il secondo ciclo passo dopo passo? La "i" è veramente 256 all'inizio del secondo ciclo a causa della prima, ma non appena il secondo ciclo inizia "i" diventa 0 e conta normalmente a 255.

Non vedo come o perché dovrebbe contare da 256 a 0?

UPDATE: non ho nemmeno pensato a questo, ma qui è la tua spiegazione I belive: http://www.delphigroups.info/2/45/418603.html

"E 'un'ottimizzazione del compilatore - non si sta usando "io" all'interno del vostro ciclo quindi compilatore pensiero di un modo migliore per contare. Il tuo ciclo conteggio sarà ancora accurate ..."

+1

In Delphi XE4 tutto funziona correttamente. Lo ha notato solo in Delphi 2010. Il "i" è 256 alla prima interazione, non solo all'inizio, 255 alla seconda interposizione, 254 al terzo e così via ... Ma è solo nella lista di controllo. Se provo a scrivere "i" in qualche output, ogni tanto succede al normale ordine: 0, 1, 2, .... "Non vedo come o perché dovrebbe contare da 256 a 0?" - Anche io, questa è la domanda =) –

+0

Ho deciso di google semplicemente la tua domanda e ho trovato il motivo, ho aggiornato la mia risposta ora. Fino ad ora non conoscevo questa ottimizzazione del compilatore. – Vepir

+0

Provato per google, ma sempre avanti "for ... downto" help sintassi. Grazie mille =) –