2010-08-14 19 views
10

Tôi có một vòng lặp phải đi từ N đến 0 (bao gồm). Biến số i của tôi thuộc loại size_t thường không được ký. Tôi hiện đang sử dụng mã sau:Đối với điều kiện vòng lặp dừng ở 0 khi sử dụng các số nguyên không dấu?

for (size_t i = N; i != (size_t) -1; --i) { 
    ... 
} 

Có đúng không? Có cách nào tốt hơn để xử lý tình trạng này không?

Xin cảm ơn,

Vincent.

+1

Hmm đó là câu hỏi thực sự hay. –

+1

'size_t' được đảm bảo để được unsigned, và cách sử dụng của bạn' (size_t) -1' là chính xác. Bạn chỉ gặp vấn đề khi N bằng giá trị đó ... –

+0

Làm thế nào để thực hiện {} khi (i--)? – supercat

Trả lời

7

Vâng, đúng và đó là cách tiếp cận rất phổ biến. Tôi sẽ không xem xét thay đổi nó.

Số học trên các loại số nguyên không dấu được đảm bảo sử dụng modulo 2^N số học (trong đó N là số bit giá trị trong loại) và hành vi trên tràn được xác định rõ.Kết quả được chuyển đổi thành phạm vi 0 thành 2^N - 1 bằng cách thêm hoặc trừ bội số của 2^N (ví dụ: modulo 2^N số học).

-1 chuyển thành loại số nguyên không dấu (trong đó size_t là một) chuyển thành 2^N - 1. -- cũng sử dụng modulo 2^N số học cho các loại chưa ký vì vậy một loại chưa ký với giá trị 0 sẽ bị giảm xuống 2^N - 1. Điều kiện kết thúc vòng lặp của bạn là chính xác.

4

Bạn có thể sử dụng này:

for (size_t i = n + 1; i-- > 0;) 
{ 
} 

Hy vọng rằng sẽ giúp.

+0

Điều đó sẽ chuyển từ 'n-1' sang 0, không phải từ' n' sang 0. – SoapBox

+0

bạn nói đúng, tôi bỏ lỡ yêu cầu "bao hàm", đó là cố định, nhờ – KeatsPeeks

+1

Nó sẽ không hoạt động trong trường hợp bệnh lý với 'n' là' SIZE_MAX'. Tất nhiên, có thể bạn sẽ không muốn lặp lại từ 'SIZE_MAX' thành 0 anyway ... – jamesdlin

0

Kể từ unsigned integer sẽ lăn vào giá trị tối đa của nó khi giảm đi từ số không, bạn có thể thử những điều sau đây, với điều kiện N ít thì đó giá trị lớn nhất (ai đó hãy sửa lại cho tôi nếu đây là UB):

for (size_t i = N; i <= N; i--) { /* ... */ } 
+0

Điều này có thể sẽ tải lại giá trị của N trên mỗi lần lặp lại, điều này làm giảm hiệu suất vô nghĩa. –

+2

Bạn lấy ý tưởng điên rồ này ở đâu? Điều này khác với 'for (i = 0; i

3
for (size_t i = N ; i <= N ; i--) { .... } 

Điều này sẽ làm điều đó bởi vì size_t là một int không dấu. Unsigned ints là 32 bit. Khi biến i có giá trị bằng 0, bạn muốn vòng lặp của bạn thực thi điều kiện. Nếu bạn thực hiện i--, máy tính thực hiện

00000000000000000000000000000000 
-00000000000000000000000000000001 

Kết quả nào tràn đầy, cho giá trị 111111111 ... 1. Đối với số nguyên bổ sung của hai chữ ký, giá trị này rõ ràng là âm. Tuy nhiên, loại i là một int không dấu nên máy tính sẽ giải thích 111111 ... 1 là một giá trị dương rất lớn.

Vì vậy, bạn có một vài tùy chọn:

1) Làm như trên và làm cho vòng lặp kết thúc khi xảy ra tràn.

2) Làm cho vòng lặp chạy từ i = 0 đến i < = N nhưng sử dụng (N-i) thay vì i ở mọi nơi trong vòng lặp của bạn. Ví dụ, myArray [i] sẽ trở thành myArray [N-i] (tắt bởi một tùy thuộc vào giá trị của N thực sự đại diện).

3) Làm cho điều kiện vòng lặp for của bạn khai thác ưu tiên của toán tử đơn nhất. Khi một người dùng khác đăng,

for (size_t i = N + 1 ; i-- > 0 ;) { ... } 

Điều này sẽ đặt i thành N + 1, kiểm tra xem điều kiện N + 1> 0 vẫn giữ. Nó có, nhưng i-- có một tác dụng phụ, vì vậy giá trị của i bị giảm xuống i = N. Tiếp tục đi cho đến khi bạn nhận được i = 1. Điều kiện sẽ được kiểm tra, 1> 0 là đúng, tác dụng phụ xảy ra , sau đó i = 0 và nó thực thi.

+0

Không sử dụng 'i <= N' làm điều kiện. Nó có thể sẽ lãng phí thời gian tải lại 'N' từ bộ nhớ trên mỗi lần lặp, và nếu' N' xảy ra là 'SIZE_MAX' bạn có một vòng lặp vô hạn. –

1

Bạn có thể sử dụng biến thứ hai làm bộ đếm vòng lặp để làm cho phạm vi lặp lại rõ ràng cho người đánh giá trong tương lai.

for (size_t j=0, i=N; j<=N; ++j, --i) { 
    // code here ignores j and uses i which runs from N to 0 
    ... 
} 
4

Cá nhân, tôi sẽ chỉ cần sử dụng một vòng lặp cấu trúc khác nhau, nhưng với mỗi họ riêng:

size_t i = N; 
do { 
    ... 
} while (i --> 0); 

(bạn chỉ có thể sử dụng (i--) như điều kiện vòng lặp, nhưng ta không bao giờ nên bỏ qua một cơ hội để sử dụng --> "toán tử").

+1

+1 để sử dụng toán tử '->'. Mã C được dự định để làm xáo trộn, và nó thú vị :) –

5

Chỉ vì for có một nơi thuận tiện để đặt thử nghiệm vào đầu mỗi lần lặp lại không có nghĩa là bạn phải sử dụng nó. Để xử lý N đến 0 bao gồm, thử nghiệm phải ở cuối, ít nhất là nếu bạn quan tâm đến việc xử lý giá trị tối đa. Đừng để sự tiện lợi hút bạn vào để đưa thử nghiệm vào sai chỗ.

for (size_t i = N;; --i) { 
    ... 
    if (i == 0) break; 
} 

Một vòng lặp do-while cũng sẽ làm việc nhưng sau đó bạn muốn bổ sung từ bỏ i được scoped để vòng lặp.

1
for (i=N; i+1; i--) 
+0

Lưu ý rằng, giống như bất kỳ giải pháp nào trừ các vòng lặp/và tương đương với/break, điều này sẽ thất bại nếu 'N' là' SIZE_MAX'. –

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