Câu trả lời ngắn gọn là do tối ưu hóa trình biên dịch. Câu trả lời dài là:
Trong mã Pascal
của bạn, số nguyên I
có hai (thực tế là ba) mục đích. Đầu tiên, nó là các vòng điều khiển biến điều khiển (hoặc bộ đếm vòng lặp), nghĩa là nó điều khiển số lần vòng lặp được chạy. Thứ hai, nó hoạt động như chỉ mục cho mảng a
. Và trong vòng lặp đầu tiên nó cũng hoạt động như giá trị được gán cho các phần tử mảng. Khi được biên dịch thành mã máy, các vai trò này được xử lý bởi các thanh ghi khác nhau.
Nếu tối ưu hóa được thiết lập trong cài đặt trình biên dịch, trình biên dịch tạo ra mã mà decrements biến điều khiển từ một giá trị khởi đầu theo hướng xuống zero, nếu nó có thể làm như vậy, mà không thay đổi kết quả cuối cùng. Điều này có, bởi vì một so sánh với một giá trị khác không có thể tránh được, do đó nhanh hơn.
Trong sau tháo dỡ gian hàng vòng lặp đầu tiên, bạn có thể thấy rằng vai trò của biến I
được xử lý như:
- Đăng ký
eax
hoạt động như kiểm soát vòng lặp biến và giá trị so là giao cho mảng các yếu tố
- Đăng ký
edx
là con trỏ đến phần tử mảng (được tăng lên với 4 (byte) trên mỗi lượt)
tháo:
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
Kể từ eax
có hai vai trò, nó phải đếm lên.Lưu ý rằng nó yêu cầu ba lệnh cho mỗi vòng lặp để quản lý việc đếm vòng lặp: inc eax
, cmp eax, $00000100
và jnz $005db69d
.
Trong tháo khỏi vòng lặp thứ hai , vai trò của biến I
được xử lý Tương tự như trong vòng lặp đầu tiên, ngoại trừ I
không được gán cho các yếu tố. Do đó điều khiển vòng lặp chỉ hoạt động như một bộ đếm vòng lặp và có thể chạy xuống dưới.
- Đăng ký
eax
là kiểm soát vòng lặp biến
- Đăng ký
edx
là con trỏ đến phần tử mảng (tăng lên với 4 (bytes) mỗi lượt)
tháo:
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
Lưu ý rằng trong trường hợp này chỉ cần hai lệnh để quản lý đếm vòng lặp: dec eax
và jnz $005db6b5
.
Trong Delphi XE7, trong cửa sổ Đồng hồ, biến I
được hiển thị trong vòng lặp đầu tiên dưới dạng giá trị gia tăng nhưng trong vòng lặp thứ hai là E2171 Variable 'i' inaccessible here due to optimization
. Trong các phiên bản trước tôi nhớ lại nó đã cho thấy giá trị giảm dần mà tôi tin rằng bạn thấy.
Lưu ý phụ: không sử dụng * số ma thuật *: 'cho i: = 0 đến Độ dài (a) do' –
Chỉ là một thử nghiệm hành vi lạ, không phải là mã sản xuất. Chiều dài (a) cũng hoạt động theo cách này. –
Một chút googling nên đã đưa ra đống hit cho bạn biết đó là một tối ưu hóa bởi trình biên dịch Delphi mà chỉ được áp dụng nếu nó không làm thay đổi ý nghĩa của mã của bạn. –