Инструкция Delphi "for ... to" выполняется от конечного значения до начального

Я пишу простое приложение в Embarcadero Delphi 2010. Простой код с двумя циклами:

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;

согласно списку наблюдения, во втором цикле переменная " i " начинается со значения 256 и переходит в 0 (256, 255, 254, ..., 0), но элементы массива верны (0, 1, 2, 3, ...). Переменная " i " объявлена только локально, глобальных переменных нет. Почему это происходит? Это нормальное поведение?

2 ответов


краткий ответ: из-за оптимизации компилятора. Длинный ответ:

в своем Pascal код, целое число I имеет две (фактически три) цели. Во-первых, это петли переменной управления (или счетчик цикла), то есть, он контролирует, сколько раз цикл выполняется. Во-вторых, он действует как индекс для массива a. И в первом цикле он также действует как значение, назначенное элементам массива. При компиляции в машинный код, эти роли обрабатываются по разным регистрам.

если оптимизация задана в настройках компилятора, компилятор создает код, который уменьшает управляющую переменную от начального значения до нуля,если это возможно, не изменяя конечный результат. Это так, потому что сравнение с ненулевым значением можно избежать, таким образом, быстрее.

в следующей разборке первый круг, вы можете видеть, что роли переменной несколько as:

  • зарегистрироваться eax выступает в качестве переменной цикла и значение будет присвоено элементам массива
  • зарегистрироваться edx указатель на элемент массива (с шагом 4 (байт) за ход)

разборки:

Unit25.pas.34: for I := 0 to 255 do
005DB695 33C0             xor eax,eax             // init
005DB697 8D9500FCFFFF     lea edx,[ebp-000400]
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,             // same
Unit25.pas.34: for I := 0 to 255 do
005DB6A3 3D00010000       cmp eax,000100       // comparison with end of loop
005DB6A8 75F3             jnz 5db69d           // if not, run next turn

С eax имеет две роли, он должен рассчитывать вверх. Обратите внимание, что для управления подсчетом циклов требуется три команды для каждого цикла:inc eax, cmp eax, 000100 и jnz 5db69d.

в разборках второй цикл роли переменной I обрабатываются аналогично первому циклу, за исключением I не присваивается элементам. Поэтому управление циклом действует только как счетчик циклов и может быть запущено вниз.

  • зарегистрироваться eax - это переменная цикла
  • зарегистрироваться edx указатель на элемент массива (с шагом 4 (байт) в поворот)

разборки:

Unit25.pas.39: for I := 0 to 255 do
005DB6AA B800010000       mov eax,000100       // init loop counter
005DB6AF 8D9500FCFFFF     lea edx,[ebp-000400]
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,    // 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 5db6b5  // jnz = jump if not zero

обратите внимание, что в этом случае для управления подсчетом циклов необходимы только две команды:dec eax и jnz 5db6b5.

в Delphi XE7, в окне часы, переменная I отображается во время первого цикла как инкрементные значения, но во время второго цикла как E2171 Variable 'i' inaccessible here due to optimization. В более ранних версиях я помню, что он показывал уменьшающиеся значения, которые, я думаю, вы видите.


Я скопировал ваш точный код, и когда я запустил его, переменная "i"обычно считается в обоих циклах. Вы пробежали второй цикл шаг за шагом? "I" на самом деле 256 в начале второго цикла из-за первого, но как только начинается второй цикл, "i" становится 0, и он обычно считается до 255.

Я не вижу, как или почему он будет считать от 256 до 0?

обновление: Я даже не думал об этом, но вот твое объяснение. belive:http://www.delphigroups.info/2/45/418603.html

"это оптимизация компилятора - вы не используете" I " внутри своего цикла поэтому компилятор придумал лучший способ подсчета . Ваш счетчик цикла все равно будет точно..."