Инструкция 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 " внутри своего цикла поэтому компилятор придумал лучший способ подсчета . Ваш счетчик цикла все равно будет точно..."