การเลือกประเภทของตัวแปรดัชนี


11

เราใช้ประเภทจำนวนเต็มแทนตัวแปรดัชนีส่วนใหญ่ แต่ในบางสถานการณ์เราถูกบังคับให้เลือก

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

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

สิ่งนี้จะทำให้คอมไพเลอร์เพิ่มการเตือนว่าการใช้ตัวแปรที่ลงนาม / ไม่ได้ลงนามร่วมกัน ถ้าฉันสร้างตัวแปรดัชนีเป็นfor( size_t i = 0; i < vec.size(); i++ )(หรือunsigned int) มันจะสังคายนาปัญหา

เมื่อเฉพาะเจาะจงมากขึ้นในการใช้ประเภท windows ส่วนใหญ่ Windows APIs จะเกี่ยวข้องกับ DWORD (ซึ่งพิมพ์ -ed เป็นยาวไม่ได้ลงนาม)

ดังนั้นเมื่อฉันใช้การทำซ้ำที่คล้ายกันอีกครั้งจะทำให้คำเตือนเดียวกัน ตอนนี้ถ้าฉันเขียนมันใหม่เป็น

DWORD dwCount;
....

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

ฉันคิดว่ามันแปลก ๆ มันอาจเป็นปัญหาที่มีการรับรู้

ฉันยอมรับว่าเราควรจะใช้ตัวแปรดัชนีชนิดเดียวกันเพื่อหลีกเลี่ยงปัญหาช่วงที่อาจเกิดขึ้นกับตัวแปรดัชนี เช่นถ้าเรากำลังใช้

_int64 i64Count; // 
....

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

แต่ในกรณีของ DWORD หรือจำนวนเต็มที่ไม่ได้ลงนามจะมีปัญหาในการเขียนใหม่เป็น

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

คนส่วนใหญ่ทำงานกับปัญหาที่คล้ายกันอย่างไร


4
ทำไมคุณจะใช้จำนวนเต็มที่ลงนามเพื่อเป็นตัวแทนของดัชนี? มันเหมือนกับการใช้เวกเตอร์ของจำนวนเต็มเพื่อจัดเก็บสตริง
ŠimonTóth

3
@Let_Me_Be: เพราะมันทำให้เงื่อนไขขอบเขตง่ายต่อการทดสอบ ตัวอย่างเช่นในการนับถอยหลังถึงศูนย์การทดสอบน้อยกว่าก่อนดำเนินการลูปร่างกายไม่สามารถทำงานกับค่าที่ไม่ได้ลงนาม ในทำนองเดียวกันการนับจำนวนสูงสุดจะไม่ทำงาน แน่นอนว่าในกรณีนี้จำนวนเต็มที่ลงนามจะไม่ทำงานอย่างใดอย่างหนึ่ง (เพราะมันไม่สามารถแสดงค่าขนาดใหญ่ดังกล่าว)
Yttrill

"สิ่งนี้จะทำให้คอมไพเลอร์เพิ่มการเตือนว่าการใช้ตัวแปรที่ลงนาม / ไม่ได้ลงนามผสมกัน" นั่นเป็นเพียงหนึ่งในสองปัญหาที่คุณจะต้องต่อสู้ ในหลายกรณีstd::size_tมีอันดับสูงกว่า int (หรือยาว) หากขนาดของเวกเตอร์เกินขนาดstd::numeric_limits<int>::max()คุณจะต้องเสียใจกับการใช้ int
Adrian McCarthy

คำตอบ:


11

vector มี typedef ที่บอกประเภทที่ถูกต้องให้คุณใช้: -

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

มันเกือบจะถูกกำหนดไว้เสมอว่า size_t แต่คุณไม่สามารถวางใจได้


8
ไม่ใช่การปรับปรุงในการอ่าน IMHO
Doc Brown

ไม่มี แต่มันเป็นวิธีเดียวที่จะบอกสิ่งที่เป็นชนิดที่ถูกต้องเพื่อใช้สำหรับดัชนีเป็นเวกเตอร์ที่ไม่ได้เรื่องจริงๆ ...
JohnB

4
วันนี้ใน c ++ 11 เพียงใช้อัตโนมัติ
JohnB

6
@JohnB คุณหมายถึงauto i = 0อย่างไร แต่นั่นไม่ได้ความช่วยเหลือที่ทุกคนกลายเป็นi int
สุดยอด

1
using index_t = std::vector<int>::size_type;การอ่านได้ดีขึ้นด้วย
Toby Speight

4
std::vector<int> vec;

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

ใช้ตัววนซ้ำสำหรับสิ่งนี้ไม่ใช่การforวนซ้ำ

สำหรับคนอื่น ๆ ตราบใดที่ชนิดตัวแปรที่มีขนาดเดียวกันstatic_castควรจะทำงานได้ดี (เช่นDWORDการint16_t)


2
for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)เป็นความเจ็บปวดที่จะเขียน มีfor (auto i = vec.begin();...มากขึ้นอ่านได้ทั้งหมด แน่นอนว่าforeachยังอยู่ใน C ++ 11
David Thornley

3

กรณีที่คุณอธิบายเป็นหนึ่งในสิ่งที่ฉันไม่ชอบใน C ++ เช่นกัน แต่ฉันได้เรียนรู้ที่จะอยู่กับสิ่งนั้นทั้งโดยการใช้

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

หรือ

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

(แน่นอนหลังเฉพาะเมื่อไม่มีความเสี่ยงของการได้รับ int มากเกินไป)


3

เหตุผลที่มันเตือนคุณเกี่ยวกับการเปรียบเทียบระหว่างที่ลงชื่อและไม่ได้ลงชื่อเนื่องจากค่าที่เซ็นชื่อจะถูกแปลงเป็นไม่ได้ลงชื่อซึ่งอาจไม่ใช่สิ่งที่คุณคาดหวัง

ในตัวอย่างของคุณ (เปรียบเทียบintกับsize_t) intจะถูกแปลงเป็นนัยsize_t(เว้นแต่จะintมีช่วงที่ใหญ่กว่าsize_t) ดังนั้นหากสิ่งintนั้นเป็นลบมันจะมีค่ามากกว่าค่าที่คุณเปรียบเทียบกับเนื่องจากการขึ้นบรรทัดใหม่ สิ่งนี้จะไม่เป็นปัญหาหากดัชนีของคุณไม่เคยติดลบ แต่คุณจะยังคงได้รับคำเตือนนั้น

แต่ให้ใช้ชนิดที่ได้รับการรับรอง (เช่นunsigned int, size_tหรือเป็นจอห์นบแนะนำ , std::vector<int>::size_type) สำหรับตัวแปรดัชนีของคุณ:

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

ระวังเมื่อนับถอยหลังถึง:

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

ด้านบนจะไม่ทำงานเนื่องจากi >= 0เป็นจริงเสมอเมื่อiไม่ได้ลงนาม ให้ใช้ " โอเปอเรเตอร์ลูกศร " แทนสำหรับลูปที่นับถอยหลัง:

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

ในฐานะที่เป็นคำตอบอื่น ๆ ชี้ให้เห็นโดยปกติคุณจะต้องการใช้ iterator เพื่อสำรวจ vectorนี่คือไวยากรณ์ C ++ 11:

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

1
ยังคงเสี่ยงต่อการที่unsigned intขนาดไม่ใหญ่พอที่จะเก็บขนาด
Adrian McCarthy

2

ตัวเลือกใหม่สำหรับ C ++ 11 คุณสามารถทำสิ่งต่าง ๆ ดังต่อไปนี้

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

และ

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

แม้ว่ามันจะทำซ้ำได้มากกว่าปกติสำหรับ for-loop แต่มันก็ไม่ได้ถูกยืดยาวเหมือนกับ pre-'11 วิธีในการระบุค่าและการใช้รูปแบบนี้จะใช้งานได้อย่างสม่ำเสมอหากไม่เป็นไปได้ทั้งหมด ต้องการเปรียบเทียบกับซึ่งทำให้ดีสำหรับการปรับรหัสใหม่ มันใช้ได้กับกรณีง่าย ๆ เช่นนี้:

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

นอกจากนี้ในขณะที่คุณควรใช้autoเมื่อใดก็ตามที่คุณตั้งค่ากำลังiบางส่วนค่าอัจฉริยะ (เหมือนvec.begin()) decltypeทำงานเมื่อคุณตั้งค่ากำลังที่จะคงที่เช่นศูนย์ที่รถยนต์ก็จะแก้ปัญหาที่ไปintเพราะเป็นเรื่องง่าย 0 จำนวนเต็มอักษร

ตามจริงแล้วฉันต้องการเห็นกลไกคอมไพเลอร์สำหรับการขยายการautoกำหนดประเภทสำหรับการเพิ่มลูปเพื่อดูค่าที่เปรียบเทียบกับ


1

ผมใช้โยนให้ int for (int i = 0; i < (int)v.size(); ++i)ในขณะที่ ใช่มันน่าเกลียด ฉันตำหนิมันในการออกแบบโง่ของห้องสมุดมาตรฐานที่พวกเขาตัดสินใจที่จะใช้จำนวนเต็มไม่ได้ลงนามเพื่อแสดงขนาด (เพื่อที่จะ .. อะไรขยายช่วงโดยหนึ่งบิต?)


1
ขนาดของสิ่งที่เป็นลบจะมีความหมายในสถานการณ์ใด การใช้จำนวนเต็มที่ไม่ได้ลงนามสำหรับคอลเลกชันขนาดต่าง ๆ ดูเหมือนจะเป็นทางเลือกที่สมเหตุสมผลสำหรับฉัน ล่าสุดได้ตรวจสอบการใช้ความยาวของสตริงไม่ค่อยกลับผลเชิงลบทั้ง ...
CVn

1
ในสถานการณ์ใดที่จะมีความหมายสำหรับการทดสอบอย่างง่ายเช่นif(v.size()-1 > 0) { ... }คืนค่าจริงสำหรับคอนเทนเนอร์เปล่า ปัญหาคือขนาดมักใช้ในการคำนวณเช่นกัน กับคอนเทนเนอร์ที่ใช้ดัชนีซึ่งจะถามถึงปัญหาเนื่องจากไม่ได้ลงนาม โดยทั่วไปแล้วการใช้ประเภทที่ไม่ได้ลงนามสำหรับสิ่งอื่น ๆ ที่มากกว่า 1) การจัดการระดับบิตหรือ 2) เลขคณิตแบบแยกส่วนกำลังเรียกใช้ปัญหา
zvrba

2
จุดดี. แม้ว่าฉันจะไม่เห็นจุดตัวอย่างของคุณจริง ๆ (ฉันอาจจะเขียนif(v.size() > 1) { ... }ตั้งแต่ที่ทำให้เจตนาชัดเจนมากขึ้นและในฐานะที่เป็นโบนัสเพิ่มเติมปัญหาของการลงนาม / ไม่ได้ลงนามกลายเป็นโมฆะ) ฉันจะเห็นว่าในบางกรณี signness อาจมีประโยชน์ ฉันยืนแก้ไขแล้ว
CVn

1
@Michael: ฉันเห็นด้วยมันเป็นตัวอย่างที่วางแผนไว้ ถึงกระนั้นฉันก็มักจะเขียนอัลกอริทึมที่มีลูปซ้อนกัน: สำหรับ (i = 0; i <v.size () - 1; ++ ฉัน) สำหรับ (j = i + 1; j <v.size (); ++ j) .. หาก v ว่างเปล่าลูปด้านนอกจะเรียกใช้งาน (size_t) -1 ครั้ง ดังนั้นฉันต้องตรวจสอบ v.empty () ก่อนลูปหรือส่ง v.size () ไปยังประเภทที่เซ็นชื่อซึ่งทั้งคู่คิดว่าเป็นการแก้ปัญหาที่น่าเกลียด ฉันเลือกนักแสดงเนื่องจากมี LOC น้อยกว่าไม่ถ้า () s => มีโอกาสน้อยลงสำหรับความผิดพลาด (นอกจากนี้ในส่วนที่ 2 การแปลง oveflow จะส่งกลับค่าเป็นจำนวนลบดังนั้นลูปจะไม่ทำงานเลย)
zvrba

การขยายช่วงโดย 1 บิตคือ (และยังคงเป็น) มีประโยชน์มากในระบบ 16 บิต
Adrian McCarthy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.