2015-05-30 21 views
10

Tôi đang cố gắng hiểu rõ hơn về cách trình biên dịch tạo mã cho các biểu thức undefined ví dụ: cho đoạn mã sau:Trình gỡ lỗi trình gỡ lỗi của biểu thức không xác định

int main() 
{ 
    int i = 5; 
    i = i++; 
    return 0; 
} 

Đây là mã lắp ráp tạo ra bởi gcc 4.8.2 (Tối ưu hóa là tắt -O0 và tôi đã chèn số dòng của riêng mình cho mục đích tham khảo):

(gdb) disassemble main 
Dump of assembler code for function main: 
(1) 0x0000000000000000 <+0>: push %rbp 
(2) 0x0000000000000001 <+1>: mov %rsp,%rbp 
(3) 0x0000000000000004 <+4>: movl $0x5,-0x4(%rbp) 
(4) 0x000000000000000b <+11>: mov -0x4(%rbp),%eax 
(5) 0x000000000000000e <+14>: lea 0x1(%rax),%edx 
(6) 0x0000000000000011 <+17>: mov %edx,-0x4(%rbp) 
(7) 0x0000000000000014 <+20>: mov %eax,-0x4(%rbp) 
(8) 0x0000000000000017 <+23>: mov $0x0,%eax 
(9) 0x000000000000001c <+28>: pop %rbp 
(10) 0x000000000000001d <+29>: retq 
End of assembler dump. 

Việc thực thi mã này dẫn đến giá trị i còn lại với giá trị (được xác minh bằng tuyên bố printf()) tức là i dường như không bao giờ được tăng lên. Tôi hiểu rằng các trình biên dịch khác nhau sẽ đánh giá/biên dịch các biểu thức không xác định theo các cách khác nhau và điều này có thể chỉ là cách mà gcc thực hiện nó, tức là tôi có thể nhận được kết quả khác với trình biên dịch khác.

đối với mã lắp ráp Với, như tôi hiểu:

Bỏ qua dòng - 1-2 thiết lập của con trỏ stack/cơ sở, vv dòng 3/4 - là như thế nào giá trị của được gán đến i.

Có ai có thể giải thích những gì đang xảy ra trên dòng 5-6 không? Dường như i cuối cùng sẽ được gán lại giá trị (dòng 7), nhưng hoạt động tăng (yêu cầu cho hoạt động tăng bài đăng i++) chỉ đơn giản là bị bỏ qua/bỏ qua bởi trình biên dịch trong trường hợp?

Trả lời

8

Ba dòng chứa câu trả lời của bạn:

lea 0x1(%rax),%edx 
mov %edx,-0x4(%rbp) 
mov %eax,-0x4(%rbp) 

các hoạt động tăng không được bỏ qua. lea là số gia tăng, lấy giá trị từ %rax và lưu trữ giá trị gia tăng trong %edx. %edx được lưu trữ nhưng sau đó được ghi đè bằng dòng kế tiếp sử dụng giá trị ban đầu từ %eax.

Chìa khóa để hiểu mã này là biết cách hoạt động của lea. Nó là viết tắt của load effective address, vì vậy trong khi nó trông giống như một con trỏ dereference, nó thực sự chỉ cần toán học cần thiết để có được địa chỉ cuối cùng của [bất cứ], và sau đó giữ địa chỉ, thay vì giá trị tại địa chỉ đó. Điều này có nghĩa là nó có thể được sử dụng cho bất kỳ biểu thức toán học nào có thể được biểu diễn một cách hiệu quả bằng cách sử dụng các chế độ địa chỉ, như là một thay thế cho các opcodes toán học. Nó thường được sử dụng như một cách để có được một nhân và thêm vào một hướng dẫn duy nhất cho lý do này. Đặc biệt, trong trường hợp này, nó được sử dụng để tăng giá trị và di chuyển kết quả đến một thanh ghi khác trong một lệnh, trong đó thay vào đó, inc sẽ ghi đè lên vị trí đó.

5

Dòng 5-6, là i++. Các lea 0x1(%rax),%edxi + 1mov %edx,-0x4(%rbp) viết rằng trở lại i. Tuy nhiên dòng 7, các mov %eax,-0x4(%rbp) viết giá trị ban đầu trở lại vào i. Mã này trông giống như:

(4) eax = i 
(5) edx = i + 1 
(6) i = edx 
(7) i = eax 
+0

Related: [sequence point] (http://stackoverflow.com/q/3575350/2564301) – usr2564301

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