Trong chế độ phát hành, hành vi mã không như mong đợi


131

Đoạn mã sau tạo các kết quả khác nhau trong chế độ gỡ lỗi và chế độ phát hành (sử dụng Visual Studio 2008):

int _tmain(int argc, _TCHAR* argv[])
{

    for( int i = 0; i < 17; i++ ) 
    { 
        int result = i * 16;

        if( result > 255 )
        {
            result = 255;
        }

        printf("i:%2d, result = %3d\n", i, result) ; 
    } 

    return 0;
}

Đầu ra của chế độ gỡ lỗi, như mong đợi:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 240
i:16, result = 255

Đầu ra của chế độ phát hành, trong đó kết quả i: 15 không chính xác:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 255
i:16, result = 255

Bằng cách chọn "Tối ưu hóa -> Không tối ưu hóa" trong Visual Studio ở chế độ phát hành, kết quả đầu ra sẽ chính xác. Tuy nhiên tôi muốn biết tại sao quá trình tối ưu hóa có thể dẫn đến đầu ra sai lầm.


Cập nhật:

Theo đề xuất của Mohit JainBy, in bằng:

printf("i:%2d, result = %3d, i*16=%d\n", i, result, i*16) ;

Đầu ra chế độ phát hành là chính xác:

i: 0, result =   0, i*16=0
i: 1, result =  16, i*16=16
(...)
i:14, result = 224, i*16=224
i:15, result = 240, i*16=240
i:16, result = 255, i*16=256

15
Trông giống như một lỗi biên dịch (và một lỗi khá quan trọng ở đó).
WhozCraig

1
@WhozCraig Chỉ cần cập nhật đầu ra của i * 16bài đăng, và kết quả là chính xác.
Lorris Lin

4
@juanchopanza: Từ kinh nghiệm của tôi với MS và sửa lỗi cho VS, họ đã sửa các lỗi đó sau khi họ được thông báo về chúng, nhưng không áp dụng các bản sửa lỗi đó cho các phiên bản cũ hơn của VS, vì vậy nếu một lý do nào đó buộc phải sử dụng phiên bản cũ hơn của VS, sau đó người ta bị mắc kẹt với các lỗi như vậy cho đến khi người ta có thể nâng cấp lên phiên bản mới hơn.
Kaiserludi

2
FWIW điều này hoạt động tốt với Visual Studio 2015 sắp tới
ismail

Câu trả lời:


115

Điều này thật thú vị, ít nhất là từ góc độ lịch sử. Tôi có thể tái tạo vấn đề với VC 2008 (15.00.30729.01) VC 2010 (16.00.40219.01) (nhắm mục tiêu là 32 bit x86 hoặc 64 bit x64). Sự cố không xảy ra với bất kỳ trình biên dịch nào tôi đã thử bắt đầu với VC 2012 (17.00.61030).

Lệnh tôi đã sử dụng để biên dịch: cl /Ox vc15-bug.cpp /FAsc

Do VC 2008 (và 2010) khá cũ và bản sửa lỗi đã có từ vài năm nay, tôi không nghĩ bạn có thể mong đợi bất kỳ hành động nào từ Microsoft ngoại trừ sử dụng trình biên dịch mới hơn (mặc dù có thể ai đó có thể đề xuất cách khắc phục).

Vấn đề là thử nghiệm để xác định xem giá trị có nên được 255thực hiện hay không dựa trên số vòng lặp thay vì kết quả thực tế của i * 16biểu thức. Và trình biên dịch chỉ đơn giản là đếm sai khi nó bắt đầu buộc giá trị 255. Tôi không biết tại sao điều đó xảy ra - đó chỉ là hiệu ứng mà tôi thấy:

; 6    :    for( int i = 0; i < 17; i++ ) 

  00001 33 f6        xor     esi, esi
$LL4@main:
  00003 8b c6        mov     eax, esi
  00005 c1 e0 04     shl     eax, 4

; 7    :    { 
; 8    :        int result = i * 16;
; 9    : 
; 10   :        if( result > 255 )

  // the value `esi` is compared with in the following line should be 15!
  00008 83 fe 0e     cmp     esi, 14            ; 0000000eH
  0000b 7e 05        jle     SHORT $LN1@main

; 11   :        {
; 12   :            result = 255;

  0000d b8 ff 00 00 00   mov     eax, 255       ; 000000ffH
$LN1@main:

; 13   :        }

Cập nhật : Tất cả các phiên bản VC tôi đã cài đặt sớm hơn VC 2008 đều có cùng một lỗi, ngoại trừ VC6 - biên dịch chương trình làm hỏng trình biên dịch VC6:

vc15-bug.cpp(10) : fatal error C1001: INTERNAL COMPILER ERROR

Vì vậy, đây là một lỗi tồn tại trong MSVC ở dạng này hay dạng khác trong hơn 10 năm!


Nếu bộ nhớ của tôi về thời gian lắp ráp x86 là lý do để so sánh với esi chứ không phải là eax là comp eax, 255 sẽ gây ra tình trạng đình trệ đường ống vì eax vừa được viết.
Loren Pechtel

3
Dự đoán của tôi (biến đổi): kết quả> 255, kết quả / 16> 255/16, i> 15, i <= 14
teki

Rất thú vị! Ngoài ra nếu bạn thay đổi so sánh từ result > 255để result >= 255nó cư xử một cách chính xác. Trong VS2010 thay đổi cmp esi, 14thành cmp esi, 16(và jlethành jl).
opello

16

Giả sử sự kiện được báo cáo của bạn là chính xác, đây sẽ là một lỗi trình biên dịch. Kiểm tra phiên bản mới nhất của trình biên dịch. Nếu lỗi vẫn còn, gửi báo cáo lỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.