2016-02-18 14 views
7

Tôi đang viết một ứng dụng đơn giản trong Delphi 2010. Một mã đơn giản với hai chu kỳ:Delphi "cho ... để" tuyên bố chạy từ giá trị dấu chấm hết cho giá trị đầu

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; 

Theo dõi List, trong biến chu kỳ thứ hai "i" bắt đầu từ giá trị 256 và đi tới 0 (256, 255, 254, ..., 0), nhưng các phần tử của mảng là chính xác (0, 1, 2, 3, ...). Biến "i" chỉ được khai báo cục bộ, không có biến toàn cầu. Tại sao điều này xảy ra? Đó là hành vi bình thường?

+0

Lưu ý phụ: không sử dụng * số ma thuật *: 'cho i: = 0 đến Độ dài (a) do' –

+0

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. –

+4

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. –

Trả lời

12

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, $00000100jnz $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 eaxjnz $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.

+0

Cảm ơn bạn rất nhiều vì đã trả lời chi tiết! –

8

Tôi đã sao chép mã chính xác của bạn và khi tôi chạy nó biến "i" đếm bình thường trong cả hai cho chu kỳ. Bạn đã từng bước chạy chu trình thứ hai chưa? "I" thực sự là 256 khi bắt đầu chu kỳ thứ hai vì chu kỳ thứ nhất nhưng ngay khi chu kỳ thứ hai bắt đầu "i" trở thành 0 và nó đếm bình thường là 255.

Tôi không thấy như thế nào hoặc tại sao nó sẽ đếm từ 256 đến 0?

UPDATE: Tôi thậm chí không nghĩ về điều này, nhưng đây là lời giải thích của bạn I belive: http://www.delphigroups.info/2/45/418603.html

"Đó là một tối ưu hóa trình biên dịch - bạn không sử dụng "tôi" bên trong vòng lặp của bạn do đó trình biên dịch nghĩ ra cách tốt hơn để đếm. Số vòng lặp của bạn vẫn sẽ chính xác ... "

+1

Trong Delphi XE4 tất cả đều hoạt động tốt. Nhận thấy nó chỉ trong Delphi 2010. "i" là 256 ở đầu tiên interation, không chỉ ở đầu, 255 ở interation thứ hai, 254 ở thứ ba và vv ... Nhưng nó chỉ trong Danh sách Watch. Nếu tôi cố gắng viết "i" ở một số đầu ra, mọi thứ thỉnh thoảng lại gây ra trật tự bình thường: 0, 1, 2, .... "Tôi không hiểu tại sao nó lại đếm được từ 256 đến 0?" - Tôi cũng vậy, đây là câu hỏi về =) –

+0

Tôi quyết định chỉ đơn giản là google câu hỏi của bạn và tìm thấy lý do cho nó, tôi cập nhật câu trả lời của tôi ngay bây giờ. Bản thân tôi không biết về tối ưu hóa trình biên dịch này cho đến bây giờ. – Vepir

+0

Đã cố gắng để google nó, nhưng luôn luôn đi vào "cho ... downto" cú pháp giúp đỡ. Cảm ơn rất nhiều =) –

Các vấn đề liên quan