Chọn loại biến Index


11

Chúng tôi sử dụng loại Integer đại diện cho các biến chỉ số hầu hết thời gian. Nhưng trong một số tình huống, chúng tôi buộc phải chọn

std::vector<int> vec;
....

for(int i = 0; i < vec.size(); ++i)
....

Điều này sẽ khiến trình biên dịch đưa ra cảnh báo rằng việc sử dụng hỗn hợp các biến đã ký / không dấu. nếu tôi thực hiện biến chỉ số là for( size_t i = 0; i < vec.size(); i++ ), (hoặc một unsigned int) nó sẽ sắp xếp các vấn đề.

Khi sử dụng các loại cửa sổ cụ thể hơn, hầu hết các API Windows đang xử lý DWORD (mà typedef-ed là không dấu dài).

Vì vậy, khi tôi sử dụng phép lặp tương tự, một lần nữa sẽ gây ra cảnh báo tương tự. Bây giờ nếu tôi viết lại nó thành

DWORD dwCount;
....

for(DWORD i = 0; i < dwCount; ++i)
....

Tôi thấy điều này hơi kỳ lạ. Nó có thể là vấn đề với nhận thức.

Tôi đồng ý rằng chúng tôi phải sử dụng cùng một loại biến chỉ mục để tránh các vấn đề phạm vi có thể xảy ra với các biến chỉ mục. Ví dụ: nếu chúng ta đang sử dụng

_int64 i64Count; // 
....

for(_int64 i = 0; i < i64Count; ++i)
....

Nhưng trong trường hợp DWORD, hoặc số nguyên không dấu, có bất kỳ vấn đề nào khi viết lại nó dưới dạng

for(int i = 0; (size_t)i < vec.size(); ++i)

Làm thế nào hầu hết mọi người đang làm việc với các vấn đề tương tự?


4
Tại sao bạn sẽ sử dụng một số nguyên đã ký để thể hiện chỉ số? Điều đó giống như sử dụng vectơ số nguyên để lưu trữ một chuỗi.
Let_Me_Be

3
@Let_Me_Be: Bởi vì nó làm cho các điều kiện biên dễ kiểm tra hơn. Ví dụ, khi đếm ngược về 0, kiểm tra ít hơn trước khi thực hiện thân vòng lặp có thể hoạt động với giá trị không dấu. Tương tự, số lượng lên đến mức tối đa không hoạt động. Tất nhiên trong trường hợp đó, một số nguyên đã ký sẽ không hoạt động (vì nó không thể biểu thị giá trị lớn như vậy).
Yttrill

"Điều này sẽ khiến trình biên dịch đưa ra cảnh báo rằng việc sử dụng hỗn hợp các biến đã ký / không dấu." Đó chỉ là một trong hai vấn đề bạn sẽ phải giải quyết. Trong nhiều trường hợp std::size_tlà một thứ hạng cao hơn int (hoặc thậm chí dài). Nếu kích thước của vectơ vượt quá std::numeric_limits<int>::max(), bạn sẽ hối tiếc vì đã sử dụng int.
Adrian McCarthy

Câu trả lời:


11

vectơ có một typedef cho bạn biết loại chính xác để sử dụng: -

for(std::vector<int>::size_type i = 0; i < thing.size(); ++i)
{
}

Nó hầu như luôn được định nghĩa là size_t nhưng bạn không thể tin vào điều đó


8
Không thực sự là một cải tiến về khả năng đọc, IMHO.
Doc Brown

Không, nhưng vì đó là cách duy nhất để biết loại nào là đúng để sử dụng cho một chỉ mục vào vector, điều đó không thực sự quan trọng ...
JohnB

4
Những ngày này trong c ++ 11 chỉ cần sử dụng tự động
JohnB

6
@JohnB Ý bạn là như thế auto i = 0nào? Điều đó không giúp ích gì cả, itrở thành một int.
đỉnh

1
Khả năng đọc có thể được cải thiện, với using index_t = std::vector<int>::size_type;.
Toby Speight

4
std::vector<int> vec;

for(int i = 0; i < vec.size(); ++i)

Sử dụng một trình vòng lặp cho điều này, không phải là một forvòng lặp.

Đối với những người khác, miễn là kiểu biến là có cùng kích thước, static_castnên chỉ làm việc tốt (tức là DWORDđến int16_t)


2
for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)là một nỗi đau để viết. Có for (auto i = vec.begin();...rất nhiều dễ đọc hơn. Tất nhiên, foreachcũng có trong C ++ 11.
David Thornley

3

Trường hợp bạn mô tả là một trong những điều tôi không thích trong C ++. Nhưng tôi đã học được cách sống với điều đó, bằng cách sử dụng

for( size_t i = 0; i < vec.size(); i++ )

hoặc là

for( int i = 0; i < (int)vec.size(); i++ )

(tất nhiên, chỉ sau khi không có nguy cơ bị tràn int).


3

Lý do nó cảnh báo bạn về việc so sánh giữa ký và không dấu là vì giá trị đã ký có thể sẽ được chuyển đổi thành không dấu, có thể không như bạn mong đợi.

Trong ví dụ của bạn (so sánh intvới size_t), intsẽ được chuyển đổi hoàn toàn thành size_t(trừ khi intbằng cách nào đó có phạm vi lớn hơn size_t). Do đó, nếu intgiá trị âm, nó có thể sẽ lớn hơn giá trị bạn so sánh với giá trị đó. Đây sẽ không phải là vấn đề nếu chỉ mục của bạn không bao giờ âm, nhưng bạn vẫn sẽ nhận được cảnh báo đó.

Thay vào đó, sử dụng một loại unsigned (ví dụ như unsigned int, size_t, hoặc, như John B khuyến cáo , std::vector<int>::size_type) cho biến chỉ số của bạn:

for(unsigned int i = 0; i < vec.size(); i++)

Hãy cẩn thận khi đếm ngược, mặc dù:

for(unsigned int i = vec.size()-1; i >= 0; i--) // don't do this!

Những điều trên sẽ không hoạt động vì i >= 0luôn luôn đúng khi ikhông dấu. Thay vào đó, sử dụng " toán tử mũi tên " cho các vòng lặp đếm ngược:

for (unsigned int i = vec.size(); i-- > 0; )
    vec[i] = ...;

Như các câu trả lời khác chỉ ra, thông thường bạn muốn sử dụng một trình vòng lặp để duyệt qua a vector. Đây là cú pháp C ++ 11:

for (auto i = vec.begin(); i != vec.end(); ++i)

1
Điều này vẫn có nguy cơ unsigned intlà không đủ lớn để giữ kích thước.
Adrian McCarthy

2

Một tùy chọn mới cho C ++ 11, bạn có thể làm những việc như sau

for(decltype(vec.size()) i = 0; i < vec.size(); ++i) {...}

for(decltype(dWord) i = 0; i < dWord; ++i) {...}

Mặc dù nó lặp lại nhiều hơn một chút so với vòng lặp for cơ bản, nhưng nó gần như không dài dòng như các cách chỉ định trước '11, và sử dụng mẫu này một cách nhất quán sẽ hoạt động trong hầu hết các điều khoản có thể muốn so sánh với, điều này làm cho nó tuyệt vời cho việc tái cấu trúc mã. Nó thậm chí hoạt động cho các trường hợp đơn giản như thế này:

int x = 3; int final = 32; for(decltype(final) i = x; i < final; ++i)

Hơn nữa, trong khi bạn nên sử dụng autobất cứ khi nào bạn đặt ithành một giá trị thông minh nào đó (như vec.begin()), sẽ decltypehoạt động khi bạn đặt thành hằng số như 0, trong đó tự động sẽ giải quyết điều đó intbởi vì 0 là một số nguyên đơn giản.

Thành thật mà nói, tôi muốn thấy một cơ chế biên dịch để mở rộng autoxác định kiểu cho các bộ tăng vòng lặp để xem xét giá trị được so sánh với.


1

Tôi sử dụng một diễn viên để int, như trong for (int i = 0; i < (int)v.size(); ++i). Vâng, nó xấu. Tôi đổ lỗi cho thiết kế ngu ngốc của thư viện tiêu chuẩn nơi họ quyết định sử dụng các số nguyên không dấu để thể hiện kích thước. (Để .. cái gì? Mở rộng phạm vi thêm một chút?)


1
Trong tình huống nào một kích thước bộ sưu tập của bất cứ điều gì tiêu cực sẽ có ý nghĩa? Sử dụng số nguyên không dấu cho các kích thước bộ sưu tập khác nhau có vẻ như là sự lựa chọn hợp lý , với tôi. Tôi đã kiểm tra lần cuối, lấy độ dài của chuỗi hiếm khi trả về kết quả âm tính ...
CVn

1
Trong tình huống nào sẽ có ý nghĩa đối với thử nghiệm đơn giản như if(v.size()-1 > 0) { ... }trả về giá trị thật cho một container rỗng? Vấn đề là kích thước cũng thường được sử dụng trong số học, đặc biệt. với các thùng chứa dựa trên chỉ mục, yêu cầu sự cố do chúng không được ký. Về cơ bản, sử dụng các loại không dấu cho bất cứ điều gì khác ngoài 1) thao tác bitwise hoặc 2) số học mô-đun đang kêu gọi sự cố.
zvrba

2
điểm tốt. Mặc dù tôi không thực sự nhìn thấy điểm của ví dụ cụ thể của bạn (có lẽ tôi sẽ chỉ viết if(v.size() > 1) { ... }vì điều đó làm cho ý định rõ ràng hơn và như một phần thưởng bổ sung, vấn đề đã ký / không dấu trở nên vô hiệu), tôi thấy trong một số trường hợp cụ thể chữ ký có thể hữu ích. Tôi đứng sửa.
một CVn

1
@Michael: Tôi đồng ý rằng đó là ví dụ giả định. Tuy nhiên, tôi thường viết các thuật toán với các vòng lặp lồng nhau: for (i = 0; i <v.size () - 1; ++ i) cho (j = i + 1; j <v.size (); ++ j) .. nếu v trống, vòng lặp bên ngoài thực thi (size_t) -1 lần. Vì vậy, tôi phải kiểm tra v.empty () trước vòng lặp hoặc chuyển v.size () sang loại đã ký, cả hai cá nhân tôi nghĩ là cách giải quyết xấu xí. Tôi chọn một diễn viên vì nó ít LỘC, không có () s => ít khả năng nhầm lẫn hơn. (Ngoài ra, trong phần bổ sung thứ 2, oveflow chuyển đổi trả về một số âm, do đó, vòng lặp hoàn toàn không thực thi.)
zvrba

Mở rộng phạm vi thêm 1 bit là (và tiếp tục) rất hữu ích trong các hệ thống 16 bit.
Adrian McCarthy
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.