int กับ size_t ที่ไม่ได้ลงชื่อ


492

ฉันสังเกตว่ารหัส C และ C ++ ที่ทันสมัยดูเหมือนว่าจะใช้size_tแทนint/ unsigned intสวยมากทุกที่ - จากพารามิเตอร์สำหรับฟังก์ชันสตริง C ถึง STL ฉันอยากรู้ว่าเหตุผลนี้และประโยชน์ที่จะได้รับ

คำตอบ:


388

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

size_tชนิดอาจจะใหญ่กว่าเท่ากับหรือขนาดเล็กกว่าunsigned intและคอมไพเลอร์ของคุณอาจทำให้สมมติฐานเกี่ยวกับเรื่องนี้สำหรับการเพิ่มประสิทธิภาพ

คุณอาจพบข้อมูลที่แม่นยำมากขึ้นในมาตรฐาน C99 ส่วน 7.17, ร่างซึ่งมีอยู่ในอินเทอร์เน็ตในรูปแบบไฟล์ PDFรูปแบบหรือในมาตรฐาน C11 ส่วน 7.19 นอกจากนี้ยังสามารถใช้ได้เป็นร่างรูปแบบไฟล์ PDF


50
Nope คิดว่า x86-16 ด้วยโมเดลหน่วยความจำขนาดใหญ่ (ไม่ใหญ่): พอยน์เตอร์อยู่ไกล (32- บิต) แต่แต่ละวัตถุ จำกัด 64k (ดังนั้น size_t สามารถเป็น 16 บิต)
dan04

8
"ขนาดของวัตถุที่ใหญ่ที่สุด" ไม่ใช่ถ้อยคำที่ไม่ดี แต่ถูกต้องอย่างแน่นอน sixe ของวัตถุสามารถถูก จำกัด ได้มากกว่าพื้นที่ที่อยู่
gnasher729

3
"คอมไพเลอร์ของคุณอาจตั้งสมมติฐานเกี่ยวกับมัน": ฉันหวังว่าคอมไพเลอร์จะรู้ช่วงของค่าที่แน่นอนที่size_tสามารถเป็นตัวแทนได้! หากไม่เป็นเช่นนั้นใครทำ
Marc van Leeuwen

4
@ Marc: ฉันคิดว่าประเด็นนั้นมีมากกว่าที่ผู้เรียบเรียงอาจจะสามารถทำอะไรกับความรู้นั้นได้

8
ฉันแค่ต้องการประเภทที่นิยมมากขึ้นนี้ไม่จำเป็นต้องมีไฟล์ส่วนหัว
user2023370

98

คลาสสิก C (ภาษาแรกของ C อธิบายโดยไบรอัน Kernighan และเดนนิสริตชี่ในภาษา C Programming ศิษย์ฮอลล์, 1978) size_tไม่ได้ให้ คณะกรรมการมาตรฐาน C แนะนำsize_tเพื่อขจัดปัญหาการพกพา

อธิบายรายละเอียดได้ที่ embedded.com (พร้อมตัวอย่างที่ดีมาก)


6
อีกบทความที่ยอดเยี่ยมที่อธิบายทั้ง size_t และ ptrdiff_t: viva64.com/en/a/0050
Ihor Kaharlichenko

73

กล่าวโดยย่อ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 แต่ละมาตรฐานนั้นควรเลือกจำนวนเต็มที่ไม่มีเครื่องหมายที่ใหญ่พอ - แต่ไม่ใหญ่กว่าที่ต้องการ - เพื่อแสดงขนาดของวัตถุที่ใหญ่ที่สุดที่เป็นไปได้บนแพลตฟอร์มเป้าหมาย "


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

หาก int ที่ไม่ได้ลงชื่อของคุณตรงกับ 32 บิตดังนั้นใช่จำนวนที่มากที่สุดที่สามารถเก็บได้คือ 2 ^ 32 - 1 ซึ่งคือ 4294967295 (0xffffffff) คุณมีคำถามอื่นหรือไม่
Rose Perrone

3
@Mitch: ค่ามากที่สุดที่สามารถแสดงในunsigned intกระป๋องและแตกต่างจากระบบหนึ่งไปอีกระบบหนึ่ง ต้องมีอย่างน้อยที่สุด 65536แต่เป็นเรื่องปกติ4294967295และอาจเป็น18446744073709551615(2 ** 64-1) ในบางระบบ
Keith Thompson

1
ค่าที่ใหญ่ที่สุดของ int ที่ไม่ได้ลงชื่อ 16 บิตสามารถมีได้ 65535 ไม่ใช่ 65536 ความแตกต่างเล็กน้อย แต่สำคัญที่ 65536 นั้นเท่ากับ 0 ใน 16 บิตที่ไม่ได้ลงนาม int
Sie Raybould

1
@ gnasher729: คุณแน่ใจเกี่ยวกับมาตรฐาน C ++ หรือไม่ มีการค้นหาบางครั้งฉันอยู่ภายใต้การแสดงผลที่พวกเขาเพียงแค่ลบรับประกันทั้งหมดเกี่ยวกับช่วงจำนวนเต็ม (ไม่รวมunsigned char) มาตรฐานไม่ได้ดูเหมือนจะมีสตริง '65535' หรือ '65536' ที่ใดก็ได้และ '32767' เกิดขึ้นเฉพาะ (1.9: 9) ในหมายเหตุเป็นไปได้ซึ่งแสดงเลขที่ใหญ่ที่สุดในint; ไม่มีการรับประกันใด ๆ ถึงแม้INT_MAXจะไม่เล็กกว่านั้น!
Marc van Leeuwen

51

ประเภท 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')


คำจำกัดความของพฤตินัยของ int คือว่ามันเป็น 16 บิตต่อ 16 เครื่องและ 32 บิตสำหรับทุกอย่างที่ใหญ่กว่า มีการเขียนโค้ดมากเกินไปซึ่งสันนิษฐานว่า int กว้าง 32 บิตเพื่อทำการเปลี่ยนแปลงในตอนนี้และเป็นผลให้ผู้ใช้ควรใช้ size_t หรือ {, u} int {8,16,32,64} _t หากพวกเขาต้องการสิ่งที่เฉพาะเจาะจง - - เพื่อเป็นการป้องกันไว้ล่วงหน้าผู้คนควรใช้สิ่งเหล่านี้เสมอแทนที่จะเป็นประเภทจำนวนเต็มหนึ่งส่วน
ชัดเจน

3
"เป็นจำนวนเต็มที่ไม่ได้ลงนามซึ่งสามารถแสดงขนาดเป็นไบต์ของช่วงหน่วยความจำใด ๆ ที่รองรับบนเครื่องโฮสต์" -> ไม่ size_tสามารถแสดงขนาดของวัตถุใด ๆ ก็ได้ (เช่น: จำนวน, อาร์เรย์, โครงสร้าง) ช่วงหน่วยความจำทั้งหมดอาจเกินsize_t
chux - Reinstate Monica

"เมื่อเขียนรหัส C คุณควรใช้ size_t ทุกครั้งที่จัดการกับช่วงหน่วยความจำ" - นั่นหมายความว่าทุกดัชนีของอาเรย์ทุกตัวควรเป็นsize_t- ฉันหวังว่าคุณไม่ได้หมายความอย่างนั้น เวลาส่วนใหญ่เราไม่ได้จัดการกับอาร์เรย์ที่สำคัญของพื้นที่ที่อยู่ + พกพาแม้เรื่อง size_tในกรณีเหล่านี้ที่คุณต้องการใช้ ในทุกกรณีคุณจะใช้ดัชนีจากจำนวนเต็ม (ลงนาม) เนื่องจากความสับสน (ที่มาโดยไม่มีการเตือน) การจัดเรียงจากพฤติกรรมอันไม่น่าสงสัยของผู้ไม่ได้ลงชื่อเป็นเรื่องธรรมดาและแย่กว่าปัญหาการพกพาที่อาจเกิดขึ้นในกรณีอื่น ๆ
johannes_lalala

23

ประเภท size_t ต้องใหญ่พอที่จะเก็บขนาดของวัตถุที่เป็นไปได้ int ที่ไม่ได้ลงชื่อไม่จำเป็นต้องตอบสนองเงื่อนไขนั้น

ตัวอย่างเช่นในระบบ 64 บิต int และ int ที่ไม่ได้ลงชื่ออาจมีความกว้าง 32 บิต แต่ size_t ต้องใหญ่พอที่จะเก็บหมายเลขที่ใหญ่กว่า 4G ได้


38
"object" เป็นภาษาที่ใช้โดยมาตรฐาน
. GitHub หยุดช่วยน้ำแข็ง

2
ฉันคิดว่าsize_tจะต้องมีขนาดใหญ่เท่านั้นหากคอมไพเลอร์สามารถยอมรับประเภท X เช่นขนาดของ (X) จะให้ค่าที่มากกว่า 4G คอมไพเลอร์ส่วนใหญ่จะปฏิเสธเช่นtypedef unsigned char foo[1000000000000LL][1000000000000LL]และfoo[65536][65536];อาจถูกปฏิเสธอย่างถูกกฎหมายหากเกินขีด จำกัด การใช้งานที่กำหนดไว้ในเอกสาร
supercat

1
@ MattJoiner: การใช้ถ้อยคำทำได้ดี "วัตถุ" ไม่ชัดเจนเลย แต่นิยามไว้ว่าหมายถึง "พื้นที่เก็บข้อมูล"
Lightness Races ในวงโคจร

4

ข้อความที่ตัดตอนมาจากคู่มือ glibc 0.02 นี้อาจเกี่ยวข้องเมื่อทำการวิจัยหัวข้อ:

มีปัญหาที่อาจเกิดขึ้นกับประเภท size_t และรุ่นของ GCC ก่อนที่จะเผยแพร่ 2.4 ANSI C ต้องการให้ size_t นั้นเป็นประเภทที่ไม่ได้ลงชื่อเสมอ สำหรับความเข้ากันได้กับไฟล์ส่วนหัวของระบบที่มีอยู่ GCC กำหนด size_t ในstddef.h' to be whatever type the system'ssys / 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 โดยอัตโนมัติเพื่อแทนที่หากจำเป็น


2

ถ้าคอมไพเลอร์ของฉันถูกตั้งค่าเป็น 32 บิตsize_tเป็นอะไรอื่นนอกจาก typedef unsigned intสำหรับ ถ้าคอมไพเลอร์ของฉันถูกตั้งค่าเป็น 64 บิตsize_tเป็นอะไรอื่นนอกจาก typedef unsigned long longสำหรับ


1
สามารถกำหนดได้unsigned longทั้งสองกรณีในบางระบบปฏิบัติการ
StaceyGirl

-4

size_t คือขนาดของตัวชี้

ดังนั้นใน 32 บิตหรือ ILP32 ทั่วไป (จำนวนเต็มยาวตัวชี้) โมเดล size_t คือ 32 บิต และในรุ่น 64 บิตหรือรุ่นทั่วไป LP64 (ตัวชี้ยาว) ขนาด size_t คือ 64 บิต (จำนวนเต็มยังคงเป็น 32 บิต)

มีรุ่นอื่น ๆ แต่รุ่นเหล่านี้เป็นรุ่นที่ g ++ ใช้ (อย่างน้อยก็เป็นค่าเริ่มต้น)


15
size_tไม่จำเป็นต้องมีขนาดเดียวกับตัวชี้แม้ว่าโดยทั่วไปจะเป็น ตัวชี้ต้องสามารถชี้ไปยังตำแหน่งใด ๆ ในหน่วยความจำ size_tมีขนาดใหญ่พอที่จะแสดงขนาดของวัตถุเดี่ยวที่ใหญ่ที่สุดได้
Keith Thompson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.