2009-03-24 31 views
7

Tôi đã có đoạn mã sau:MSVC++: lạ với ints unsigned và tràn

#include <iostream> 

using namespace std; 

int main(int argc, char *argv[]) 
{ 
    string a = "a"; 
    for(unsigned int i=a.length()-1; i+1 >= 1; --i) 
    { 
     if(i >= a.length()) 
     { 
      cerr << (signed int)i << "?" << endl; 
      return 0; 
     } 
    } 
} 

Nếu tôi biên dịch trong MSVC với tối ưu hóa đầy đủ, đầu ra tôi nhận được là "-1?". Nếu tôi biên dịch trong chế độ gỡ lỗi (không có tối ưu hóa), tôi không nhận được kết quả đầu ra (dự kiến.)

Tôi nghĩ tiêu chuẩn đảm bảo rằng các số nguyên không dấu bị tràn trong một cách có thể dự đoán được, do đó khi i = (unsigned int) (- 1) , i + 1 = 0 và điều kiện vòng lặp i + 1> = 1 không thành công. Thay vào đó, thử nghiệm là bằng cách nào đó đi qua. Đây có phải là lỗi trình biên dịch hay tôi đang làm gì đó không xác định ở đâu đó?

Trả lời

8

Tôi nhớ có vấn đề này vào năm 2001. Tôi ngạc nhiên vì nó vẫn còn đó. Vâng, đây là lỗi trình biên dịch.

Các optimizer đang chứng kiến ​​

i + 1 >= 1; 

Về mặt lý thuyết, chúng ta có thể tối ưu hóa này bằng cách đặt tất cả các hằng số trên cùng một bên:

i >= (1-1); 

i là unsigned, nó sẽ luôn luôn được lớn hơn hoặc bằng không.

Xem thảo luận nhóm tin này here.

1

Tôi không chắc chắn, nhưng tôi nghĩ bạn có thể đang gặp lỗi của một lỗi.

Tôi nghi ngờ sự cố là cách trình biên dịch xử lý điều khiển for. Tôi có thể tưởng tượng tôi ưu hoa thực hiện:

for(unsigned int i=a.length()-1; i+1 >= 1; --i) // As written 

for (unsigned int i = a.length()-1; i >= 0; --i) // Noting 1 appears twice 

for (unsigned int i = a.length()-1; ; --i) // Because i >= 0 at all times 

Cho dù đó là những gì đang xảy ra là một vấn đề khác, nhưng nó có thể là đủ để gây nhầm lẫn tôi ưu hoa.

Bạn có lẽ sẽ tốt hơn bằng cách sử dụng một công thức vòng chuẩn hơn:

for (unsigned i = a.length()-1; i-- > 0;) 
4

ISO14882: 2003, phần 5, đoạn 5:

Nếu trong quá trình đánh giá một biểu thức, kết quả không được định nghĩa bằng toán học hoặc không nằm trong phạm vi giá trị biểu thị cho loại, hành vi không xác định, trừ khi biểu thức đó là biểu thức không đổi (5.19), trong trường hợp đó chương trình bị hỏng.

(Mỏ nhấn mạnh.) Vì vậy, có, hành vi không xác định. Tiêu chuẩn không đảm bảo hành vi trong trường hợp số nguyên trên/dưới.

Chỉnh sửa: Tiêu chuẩn có vẻ hơi mâu thuẫn với vấn đề ở nơi khác.

Mục 3.9.1.4 nói:

số nguyên Unsigned, tuyên bố unsigned, phải tuân thủ pháp luật của số học modulo 2 n trong đó n là số bit trong các đại diện giá trị của kích thước cụ thể của số nguyên.

Nhưng phần 4.7.2 và .3 nói:

2) Nếu loại đích là unsigned, giá trị kết quả là số nguyên unsigned nhất đồng dư với số nguyên nguồn (modulo 2 n trong đó n là số bit được sử dụng để biểu diễn loại không dấu). [Lưu ý: Trong biểu diễn bổ sung của hai, chuyển đổi này là khái niệm và không có thay đổi trong mẫu bit (nếu không có cắt ngắn). ]

3) Nếu loại đích được ký, giá trị không thay đổi nếu nó có thể được biểu diễn trong loại đích (và chiều rộng trường bit); nếu không, giá trị được xác định thực hiện.

(tôi nhấn mạnh).

+0

Hmm. Những người khác (trên các trang web khác nhau) đang trích dẫn phần 4.7 của tiêu chuẩn: http://dev.feuvan.net/docs/isocpp/conv.html Họ đang sử dụng điều này để cho rằng nó được xác định. –

+0

Từ 3.9.1 4, và chú thích của nó, tôi có ấn tượng rằng các số nguyên không dấu là một ngoại lệ: vì 1 được thêm vào trong số học của mod 2^n, kết quả không thể nằm ngoài phạm vi giá trị, phải không? –

+0

Khi chuyển đổi sang loại đã ký (như trong trường hợp ở đây), kết quả được xác định thực hiện. Bởi vì giá trị nằm ngoài phạm vi của * cả hai * loại trong một thao tác đã ký, tôi thực hiện điều này để nằm trong phần 5. Trình biên dịch sai ở đây, một trong hai cách. – greyfade

0

Yup, tôi chỉ thử nghiệm này trên Visual Studio 2005, nó chắc chắn sẽ khác trong Debug và Release. Tôi tự hỏi nếu năm 2008 sửa chữa nó.

Điều thú vị là nó phàn nàn về diễn viên tiềm ẩn của bạn từ size_t (kết quả của .length) đến int không dấu, nhưng không có vấn đề khi tạo mã xấu.

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