2012-01-28 38 views
8

Tôi đã thực hiện một số nghiên cứu về Stackoverflow về đảo ngược cho các vòng lặp trong C++ sử dụng số nguyên không dấu thay vì số nguyên đã ký. Nhưng tôi vẫn KHÔNG hiểu tại sao có vấn đề (xem Unsigned int reverse iteration with for loops). Tại sao mã sau đây sẽ sinh ra lỗi phân đoạn?Số nguyên chưa được ký trong C++ cho vòng

#include <vector> 
#include <iostream> 
using namespace std; 

int main(void) 
{ 
    vector<double> x(10); 

    for (unsigned int i = 9; i >= 0; i--) 
    { 
     cout << "i= " << i << endl; 
     x[i] = 1.0; 
    } 

    cout << "x0= " << x[0] << endl; 

    return 0; 
} 

Tôi hiểu rằng vấn đề là khi chỉ số tôi bằng 0, bởi vì có điều gì đó giống như tràn. Nhưng tôi nghĩ rằng một số nguyên không dấu được phép lấy giá trị bằng không, phải không? Bây giờ nếu tôi thay thế nó bằng một số nguyên đã ký, thì hoàn toàn không có vấn đề gì.

Có ai đó có thể giải thích cho tôi cơ chế đằng sau vòng lặp ngược đó với số nguyên không dấu?

Cảm ơn bạn rất nhiều!

+3

'i> = 0' luôn đúng với' i' chưa được ký, vì vậy vòng lặp không bao giờ kết thúc. – TonyK

+0

Đọc cảnh báo trình biên dịch, chúng hữu ích. Trong trường hợp này, trình biên dịch của bạn có thể đã cảnh báo bạn về thực tế là điều kiện trong vòng lặp của bạn luôn đúng. – dragonroot

+0

@dragonroot: Thật không may là không. Tôi sử dụng cờ -Wall của g ++. Bạn có biết một lá cờ trình biên dịch sẽ phát hiện loại vấn đề này không? Cảm ơn. – Benjamin

Trả lời

24

Vấn đề ở đây là số nguyên không dấu không bao giờ là số âm.

Do đó, vòng lặp kiểm tra:

i >= 0 

sẽ luôn luôn đúng. Vì vậy, bạn nhận được một vòng lặp vô hạn.

Khi giảm xuống dưới 0, giá trị này bao quanh giá trị lớn nhất unsigned.
Do đó, bạn cũng sẽ truy cập x[i] ngoài giới hạn.

Đây không phải là vấn đề đối với các số nguyên đã ký vì nó sẽ đơn giản là tiêu cực và do đó không thành công i >= 0.

Vì vậy, nếu bạn muốn sử dụng số nguyên unsigned, bạn có thể thử một trong những khả năng sau:

for (unsigned int i = 9; i-- != 0;) 

for (unsigned int i = 9; i != -1; i--) 

Hai đã được đề xuất bởi GManNickG và AndreyT từ các ý kiến.


Và đây là gốc 3 phiên bản của tôi:

for (unsigned int i = 9; i != (unsigned)0 - 1; i--) 

hoặc

for (unsigned int i = 9; i != ~(unsigned)0; i--) 

hoặc

for (unsigned int i = 9; i != UINT_MAX; i--) 
+0

Hoặc có 'i' là * một * nhiều hơn chỉ mục, sao cho' 0' là điều kiện kết thúc thích hợp. Như khó khăn như phần còn lại mặc dù. –

+0

Không phải là tràn số nguyên và hành vi không xác định dòng dưới không? – josefx

+2

@josefx Chỉ số nguyên đã ký trên/dưới là hành vi không xác định. – Mysticial

4

Dù giá trị của unsigned int i nó luôn luôn là sự thật rằng i >= 0 nên y vòng lặp for của chúng tôi không bao giờ kết thúc.

Nói cách khác, nếu tại một số điểm i là 0 và bạn giảm nó, nó vẫn vẫn không âm, bởi vì nó có chứa sau đó là một con số khổng lồ, có lẽ 4294967295 (có nghĩa là 2 -1).

6

Vấn đề là, vòng lặp của bạn cho phép tôi ở mức 0 và chỉ hy vọng thoát khỏi vòng lặp nếu i nhỏ hơn 0. Vì i chưa được ký, nó không bao giờ nhỏ hơn 0. Nó cuộn qua 2^32-1.Đó là lớn hơn kích thước của vector của bạn và do đó dẫn đến một segfault.

3

Vấn đề là ở đây:

for (unsigned int i = 9; i >= 0; i--) 

Bạn đang khởi động với giá trị là 9 cho một int unsigned và định nghĩa lối ra của bạn là i> = 0 và điều này sẽ luôn luôn đúng. (unsigned int sẽ không bao giờ là tiêu cực !!!). Bởi vì điều này vòng lặp của bạn sẽ bắt đầu lại (vòng lặp vô tận, bởi vì i = 0 sau đó -1 đi uint tối đa).

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