ฉันสังเกตว่ารหัส C และ C ++ ที่ทันสมัยดูเหมือนว่าจะใช้size_t
แทนint
/ unsigned int
สวยมากทุกที่ - จากพารามิเตอร์สำหรับฟังก์ชันสตริง C ถึง STL ฉันอยากรู้ว่าเหตุผลนี้และประโยชน์ที่จะได้รับ
ฉันสังเกตว่ารหัส C และ C ++ ที่ทันสมัยดูเหมือนว่าจะใช้size_t
แทนint
/ unsigned int
สวยมากทุกที่ - จากพารามิเตอร์สำหรับฟังก์ชันสตริง C ถึง STL ฉันอยากรู้ว่าเหตุผลนี้และประโยชน์ที่จะได้รับ
คำตอบ:
size_t
ประเภทเป็นชนิดจำนวนเต็มไม่ได้ลงนามที่เป็นผลมาจากการที่sizeof
ผู้ประกอบการ (และoffsetof
ผู้ประกอบการ) ดังนั้นจึงรับประกันได้ว่าจะขนาดใหญ่พอที่จะมีขนาดของวัตถุที่ใหญ่ที่สุดในระบบของคุณสามารถจัดการ (เช่นอาร์เรย์คงที่ของ 8Gb)
size_t
ชนิดอาจจะใหญ่กว่าเท่ากับหรือขนาดเล็กกว่าunsigned int
และคอมไพเลอร์ของคุณอาจทำให้สมมติฐานเกี่ยวกับเรื่องนี้สำหรับการเพิ่มประสิทธิภาพ
คุณอาจพบข้อมูลที่แม่นยำมากขึ้นในมาตรฐาน C99 ส่วน 7.17, ร่างซึ่งมีอยู่ในอินเทอร์เน็ตในรูปแบบไฟล์ PDFรูปแบบหรือในมาตรฐาน C11 ส่วน 7.19 นอกจากนี้ยังสามารถใช้ได้เป็นร่างรูปแบบไฟล์ PDF
size_t
สามารถเป็นตัวแทนได้! หากไม่เป็นเช่นนั้นใครทำ
คลาสสิก C (ภาษาแรกของ C อธิบายโดยไบรอัน Kernighan และเดนนิสริตชี่ในภาษา C Programming ศิษย์ฮอลล์, 1978) size_t
ไม่ได้ให้ คณะกรรมการมาตรฐาน C แนะนำsize_t
เพื่อขจัดปัญหาการพกพา
กล่าวโดยย่อsize_t
คือไม่ลบและเพิ่มประสิทธิภาพสูงสุดเพราะเป็นประเภทจำนวนเต็มที่ไม่ได้ลงชื่อซึ่งมีขนาดใหญ่พอ - แต่ไม่ใหญ่เกินไป - เพื่อแสดงขนาดของวัตถุที่ใหญ่ที่สุดที่เป็นไปได้บนแพลตฟอร์มเป้าหมาย
ขนาดไม่ควรเป็นค่าลบและsize_t
เป็นประเภทที่ไม่ได้ลงชื่อ นอกจากนี้เนื่องจากsize_t
ไม่ได้ลงนามคุณสามารถจัดเก็บหมายเลขที่มีขนาดใหญ่เป็นสองเท่าของประเภทที่เซ็นชื่อเนื่องจากเราสามารถใช้เครื่องหมายบิตเพื่อแสดงขนาดเช่นบิตอื่น ๆ ทั้งหมดในจำนวนเต็มที่ไม่ได้ลงนาม เมื่อเราได้รับเพิ่มอีกหนึ่งบิตเราจะคูณช่วงของจำนวนที่เราสามารถแทนด้วยค่าประมาณสอง
ดังนั้นคุณถามทำไมไม่ใช้เพียงunsigned int
? อาจไม่สามารถเก็บจำนวนที่มากพอ ในการดำเนินการในกรณีที่unsigned int
เป็น 32 4294967295
บิตจำนวนที่ใหญ่ที่สุดที่จะสามารถเป็นตัวแทนคือ โปรเซสเซอร์บางตัวเช่น IP16L32 สามารถคัดลอกวัตถุที่มีขนาดใหญ่กว่า4294967295
ไบต์
ดังนั้นคุณถามว่าทำไมไม่ใช้unsigned long int
? มันเรียกเก็บค่าผ่านทางประสิทธิภาพในบางแพลตฟอร์ม มาตรฐาน C ต้องการให้มีการlong
ครอบครองอย่างน้อย 32 บิต แพลตฟอร์ม IP16L32 ใช้ความยาว 32 บิตแต่ละคำเป็น 16- บิต ผู้ให้บริการ 32 บิตเกือบทั้งหมดในแพลตฟอร์มเหล่านี้ต้องการสองคำสั่งหากไม่มากเพราะพวกเขาทำงานกับ 32 บิตใน 16 บิตสองชิ้น ตัวอย่างเช่นการย้ายความยาว 32- บิตโดยปกติจะต้องใช้คำสั่งเครื่องสองอัน - อันหนึ่งเพื่อย้ายอัน 16 บิต
การใช้size_t
หลีกเลี่ยงการเรียกเก็บค่าประสิทธิภาพนี้ ตามบทความที่น่าอัศจรรย์นี้ "Type size_t
เป็น typedef ที่เป็นนามแฝงสำหรับประเภทจำนวนเต็มที่ไม่ได้ลงนามโดยทั่วไปunsigned int
หรือunsigned long
แต่อาจเป็นไปได้unsigned long long
การใช้งาน C แต่ละมาตรฐานนั้นควรเลือกจำนวนเต็มที่ไม่มีเครื่องหมายที่ใหญ่พอ - แต่ไม่ใหญ่กว่าที่ต้องการ - เพื่อแสดงขนาดของวัตถุที่ใหญ่ที่สุดที่เป็นไปได้บนแพลตฟอร์มเป้าหมาย "
unsigned int
กระป๋องและแตกต่างจากระบบหนึ่งไปอีกระบบหนึ่ง ต้องมีอย่างน้อยที่สุด 65536
แต่เป็นเรื่องปกติ4294967295
และอาจเป็น18446744073709551615
(2 ** 64-1) ในบางระบบ
unsigned char
) มาตรฐานไม่ได้ดูเหมือนจะมีสตริง '65535' หรือ '65536' ที่ใดก็ได้และ '32767' เกิดขึ้นเฉพาะ (1.9: 9) ในหมายเหตุเป็นไปได้ซึ่งแสดงเลขที่ใหญ่ที่สุดในint
; ไม่มีการรับประกันใด ๆ ถึงแม้INT_MAX
จะไม่เล็กกว่านั้น!
ประเภท size_t เป็นประเภทที่ส่งคืนโดยผู้ประกอบการ sizeof เป็นจำนวนเต็มที่ไม่ได้ลงนามซึ่งสามารถแสดงขนาดเป็นไบต์ของช่วงหน่วยความจำใด ๆ ที่รองรับบนเครื่องโฮสต์ มันคือ (โดยทั่วไป) เกี่ยวข้องกับ ptrdiff_t ใน ptrdiff_t นั้นเป็นค่าจำนวนเต็มที่ลงนามเช่น sizeof (ptrdiff_t) และ sizeof (size_t) เท่ากัน
เมื่อมีการเขียนรหัส C คุณควรเสมอใช้ size_t เมื่อใดก็ตามที่การจัดการกับช่วงที่หน่วยความจำ
ประเภท int ในทางกลับกันถูกกำหนดโดยทั่วไปว่าขนาดของค่าจำนวนเต็ม (ลงนาม) ที่เครื่องโฮสต์สามารถใช้ในการดำเนินการทางคณิตศาสตร์จำนวนเต็มอย่างมีประสิทธิภาพมากที่สุด ตัวอย่างเช่นในคอมพิวเตอร์ประเภทพีซีรุ่นเก่าหลายเครื่องขนาดค่า (size_t) จะเป็น 4 (ไบต์) แต่ขนาดของ (int) จะเป็น 2 (ไบต์) เลขคณิต 16 บิตนั้นเร็วกว่าเลขคณิต 32 บิตแม้ว่า CPU จะสามารถจัดการพื้นที่หน่วยความจำ (โลจิคัล) ได้สูงสุด 4 GiB
ใช้ชนิด int เฉพาะเมื่อคุณใส่ใจประสิทธิภาพเนื่องจากความแม่นยำที่แท้จริงขึ้นอยู่กับตัวเลือกคอมไพเลอร์และสถาปัตยกรรมเครื่อง โดยเฉพาะอย่างยิ่งมาตรฐาน C ระบุค่าคงที่ต่อไปนี้: sizeof (char) <= sizeof (สั้น) <= sizeof (int) <= sizeof (ยาว) วางไม่มีข้อ จำกัด อื่น ๆ เกี่ยวกับการเป็นตัวแทนจริงของความแม่นยำที่มีให้โปรแกรมเมอร์สำหรับแต่ละ ประเภทดั้งเดิมเหล่านี้
หมายเหตุ: นี่ไม่เหมือนกับใน Java (ซึ่งระบุความแม่นยำบิตสำหรับแต่ละประเภท 'char', 'byte', 'short', 'int' และ 'long')
size_t
สามารถแสดงขนาดของวัตถุใด ๆ ก็ได้ (เช่น: จำนวน, อาร์เรย์, โครงสร้าง) ช่วงหน่วยความจำทั้งหมดอาจเกินsize_t
size_t
- ฉันหวังว่าคุณไม่ได้หมายความอย่างนั้น เวลาส่วนใหญ่เราไม่ได้จัดการกับอาร์เรย์ที่สำคัญของพื้นที่ที่อยู่ + พกพาแม้เรื่อง size_t
ในกรณีเหล่านี้ที่คุณต้องการใช้ ในทุกกรณีคุณจะใช้ดัชนีจากจำนวนเต็ม (ลงนาม) เนื่องจากความสับสน (ที่มาโดยไม่มีการเตือน) การจัดเรียงจากพฤติกรรมอันไม่น่าสงสัยของผู้ไม่ได้ลงชื่อเป็นเรื่องธรรมดาและแย่กว่าปัญหาการพกพาที่อาจเกิดขึ้นในกรณีอื่น ๆ
ประเภท size_t ต้องใหญ่พอที่จะเก็บขนาดของวัตถุที่เป็นไปได้ int ที่ไม่ได้ลงชื่อไม่จำเป็นต้องตอบสนองเงื่อนไขนั้น
ตัวอย่างเช่นในระบบ 64 บิต int และ int ที่ไม่ได้ลงชื่ออาจมีความกว้าง 32 บิต แต่ size_t ต้องใหญ่พอที่จะเก็บหมายเลขที่ใหญ่กว่า 4G ได้
size_t
จะต้องมีขนาดใหญ่เท่านั้นหากคอมไพเลอร์สามารถยอมรับประเภท X เช่นขนาดของ (X) จะให้ค่าที่มากกว่า 4G คอมไพเลอร์ส่วนใหญ่จะปฏิเสธเช่นtypedef unsigned char foo[1000000000000LL][1000000000000LL]
และfoo[65536][65536];
อาจถูกปฏิเสธอย่างถูกกฎหมายหากเกินขีด จำกัด การใช้งานที่กำหนดไว้ในเอกสาร
ข้อความที่ตัดตอนมาจากคู่มือ glibc 0.02 นี้อาจเกี่ยวข้องเมื่อทำการวิจัยหัวข้อ:
มีปัญหาที่อาจเกิดขึ้นกับประเภท size_t และรุ่นของ GCC ก่อนที่จะเผยแพร่ 2.4 ANSI C ต้องการให้ size_t นั้นเป็นประเภทที่ไม่ได้ลงชื่อเสมอ สำหรับความเข้ากันได้กับไฟล์ส่วนหัวของระบบที่มีอยู่ GCC กำหนด size_t ในstddef.h' to be whatever type the system's
sys / types.h 'กำหนดให้เป็น ระบบ Unix ส่วนใหญ่ที่กำหนด size_t ใน `sys / types.h 'กำหนดให้เป็นประเภทที่เซ็นชื่อ รหัสบางอย่างในไลบรารีขึ้นอยู่กับ size_t ซึ่งเป็นประเภทที่ไม่ได้ลงชื่อและจะทำงานไม่ถูกต้องหากลงชื่อเข้าใช้
รหัสไลบรารี GNU C ซึ่งคาดว่า size_t จะไม่ได้ลงนามนั้นถูกต้อง คำจำกัดความของ size_t เป็นประเภทที่เซ็นชื่อไม่ถูกต้อง เราวางแผนว่าในเวอร์ชัน 2.4 GCC จะกำหนด size_t เป็นประเภทที่ไม่ได้ลงชื่อเสมอและfixincludes' script will massage the system's
sys / types.h 'เสมอเพื่อไม่ให้ขัดแย้งกับสิ่งนี้
ในระหว่างนี้เราจะแก้ไขปัญหานี้โดยบอก GCC ให้ใช้ประเภทที่ไม่ได้ลงนามสำหรับ size_t เมื่อรวบรวมไลบรารี GNU C `กำหนดค่า 'จะตรวจหาประเภท GCC ที่ใช้สำหรับ size_t โดยอัตโนมัติเพื่อแทนที่หากจำเป็น
ถ้าคอมไพเลอร์ของฉันถูกตั้งค่าเป็น 32 บิตsize_t
เป็นอะไรอื่นนอกจาก typedef unsigned int
สำหรับ ถ้าคอมไพเลอร์ของฉันถูกตั้งค่าเป็น 64 บิตsize_t
เป็นอะไรอื่นนอกจาก typedef unsigned long long
สำหรับ
unsigned long
ทั้งสองกรณีในบางระบบปฏิบัติการ
size_t คือขนาดของตัวชี้
ดังนั้นใน 32 บิตหรือ ILP32 ทั่วไป (จำนวนเต็มยาวตัวชี้) โมเดล size_t คือ 32 บิต และในรุ่น 64 บิตหรือรุ่นทั่วไป LP64 (ตัวชี้ยาว) ขนาด size_t คือ 64 บิต (จำนวนเต็มยังคงเป็น 32 บิต)
มีรุ่นอื่น ๆ แต่รุ่นเหล่านี้เป็นรุ่นที่ g ++ ใช้ (อย่างน้อยก็เป็นค่าเริ่มต้น)
size_t
ไม่จำเป็นต้องมีขนาดเดียวกับตัวชี้แม้ว่าโดยทั่วไปจะเป็น ตัวชี้ต้องสามารถชี้ไปยังตำแหน่งใด ๆ ในหน่วยความจำ size_t
มีขนาดใหญ่พอที่จะแสดงขนาดของวัตถุเดี่ยวที่ใหญ่ที่สุดได้