อะไรคือวิธีปฏิบัติที่ดีที่สุดเกี่ยวกับ ints ที่ไม่ได้ลงชื่อ?


43

ฉันใช้ ints ที่ไม่ได้ลงชื่อทุกที่และฉันไม่แน่ใจว่าควรจะ ซึ่งอาจมาจากคอลัมน์รหัสคีย์หลักของฐานข้อมูลไปจนถึงตัวนับ ฯลฯ หากตัวเลขไม่ควรเป็นค่าลบฉันจะใช้ int ที่ไม่ได้ลงนามเสมอ

อย่างไรก็ตามฉันสังเกตจากรหัสของผู้อื่นว่าไม่มีใครทำเช่นนี้ มีบางอย่างที่สำคัญที่ฉันมองข้ามหรือไม่?

แก้ไข: เนื่องจากคำถามนี้ฉันยังสังเกตเห็นว่าใน C การคืนค่าลบสำหรับข้อผิดพลาดเป็นเรื่องธรรมดาแทนที่จะทิ้งข้อยกเว้นเช่นเดียวกับใน C ++


26
เพียงระวังfor(unsigned int n = 10; n >= 0; n --)(ลูปอนันต์)
Chris Burt-Brown

3
ใน C และ C ++ ints ที่ไม่ได้ลงนามได้กำหนดพฤติกรรมการล้นอย่างแม่นยำ (modulo 2 ^ n) ints ที่เซ็นชื่อไม่ เครื่องมือเพิ่มประสิทธิภาพใช้ประโยชน์จากพฤติกรรมล้นมากขึ้นซึ่งนำไปสู่ผลลัพธ์ที่น่าประหลาดใจในบางกรณี
Steve314

2
คำถามที่ดี! ฉันก็ถูกล่อลวงให้ใช้ uints ช่วงที่ จำกัด แต่ก็พบว่าความเสี่ยง / ความไม่สะดวกนั้นเกินดุลประโยชน์ / ความสะดวกสบายใด ๆ ห้องสมุดส่วนใหญ่ตามที่คุณพูดให้ยอมรับ ints ปกติที่ uint จะทำ สิ่งนี้ทำให้ยากต่อการทำงาน แต่ยังมีคำถาม: มันคุ้มค่าหรือไม่ ในทางปฏิบัติ (สมมติว่าคุณไม่ไปเกี่ยวกับสิ่งต่าง ๆ ในลักษณะโง่) คุณจะไม่ค่อยมีค่าของ -218 มาในที่คาดว่าเป็นบวก -218 นั้นต้องมาจากที่ไหนสักแห่งใช่ไหม และคุณสามารถติดตามที่มาของมัน เกิดขึ้นน้อยมาก ใช้ประโยชน์จากการยืนยันข้อยกเว้นสัญญารหัสเพื่อช่วยเหลือคุณ
งาน

@William Ting: หากเป็นเรื่องเกี่ยวกับ C / C ++ เท่านั้นคุณควรเพิ่มแท็กที่เหมาะสมให้กับคำถามของคุณ
CesarGon

2
@ Chris: ปัญหาลูปไม่สิ้นสุดในความเป็นจริงมีความสำคัญอย่างไร? ฉันหมายความว่าถ้ามันปล่อยออกมาแล้วรหัสก็ไม่ได้ทดสอบอย่างชัดเจน แม้ว่าคุณจะต้องใช้เวลาหลายชั่วโมงในการแก้ไขข้อผิดพลาดในครั้งแรกที่คุณทำผิดพลาดครั้งที่สองคุณควรรู้ว่าจะต้องค้นหาอะไรก่อนเมื่อรหัสของคุณไม่หยุดวนซ้ำ
รักษาความปลอดภัย

คำตอบ:


28

มีบางอย่างที่สำคัญที่ฉันมองข้ามหรือไม่?

เมื่อคำนวณเกี่ยวข้องกับทั้งสองชนิดลงนามและได้รับการรับรองเช่นเดียวกับขนาดแตกต่างกันกฎสำหรับโปรโมชั่นชนิดอาจมีความซับซ้อนและนำไปสู่พฤติกรรมที่ไม่คาดคิด

ฉันเชื่อว่านี่เป็นเหตุผลหลักที่ทำให้ Java ละเว้นประเภท int ที่ไม่ได้ลงชื่อ


3
อีกวิธีหนึ่งคือให้คุณกรอกหมายเลขด้วยตนเองตามความเหมาะสม นี่คือสิ่งที่ Go ดูเหมือนจะทำ (ฉันเพิ่งเล่นไปรอบ ๆ กับมันเล็กน้อย) และฉันชอบมันมากกว่าวิธีของ Java
Tikhon Jelvis

2
นั่นเป็นเหตุผลที่ดีสำหรับ Java ที่จะไม่รวมประเภทที่ไม่ได้ลงนาม 64 บิตและอาจเป็นเหตุผลที่ดีที่จะไม่รวมประเภทที่ไม่ได้ลงชื่อ 32 บิต [แม้ว่าความหมายของการเพิ่มค่าแบบ 32 บิตที่ลงชื่อและไม่ได้ลงนาม การดำเนินการเช่นนี้ควรให้ผลลัพธ์ที่มีการเซ็นชื่อแบบ 64 บิต] ประเภทที่ไม่ได้ลงนามขนาดเล็กกว่าintจะไม่ยากเช่นนี้ แต่ (เนื่องจากการคำนวณใด ๆ จะส่งเสริมให้int); ฉันไม่มีอะไรจะพูดดีเกี่ยวกับการขาดประเภทไบต์ที่ไม่ได้ลงนาม
supercat

17

ฉันคิดว่าMichaelมีจุดที่ถูกต้อง แต่ IMO เหตุผลที่ทุกคนใช้ int ตลอดเวลา (โดยเฉพาะในfor (int i = 0; i < max, i++) คือการที่เราเรียนรู้แบบนั้น เมื่อทุกตัวอย่างในหนังสือ ' วิธีการเรียนรู้การเขียนโปรแกรม ' ใช้intในforวงวนน้อยมากที่จะตั้งคำถามกับการฝึกฝน

อีกเหตุผลคือintสั้นกว่า 25% uintและเราทุกคนขี้เกียจ ... ;-)


2
ฉันเห็นด้วยกับปัญหาการศึกษา คนส่วนใหญ่ดูเหมือนจะไม่ตั้งคำถามกับสิ่งที่พวกเขาอ่าน: ถ้ามันอยู่ในหนังสือมันไม่ผิดหรอกใช่มั้ย
Matthieu M.

1
นั่นก็เป็นเหตุผลว่าทำไมทุกคนใช้ postfix ++เมื่อเพิ่มขึ้นถึงแม้ว่าพฤติกรรมที่เฉพาะเจาะจงนั้นไม่ค่อยมีความจำเป็นและอาจนำไปสู่การปั่นป่วนแบบไม่มีจุดหมายในการคัดลอกหากดัชนีลูปเป็นตัววนซ้ำหรือชนิดที่ไม่ใช่พื้นฐานอื่น ๆ .
underscore_d

อย่าทำอะไรที่เหมือนกับ "for (uint i = 10; i> = 0; --i)" การใช้ ints เฉพาะสำหรับตัวแปรลูปหลีกเลี่ยงความเป็นไปได้นี้
David Thornley

11

การเข้ารหัสข้อมูลช่วงเป็นประเภทเป็นสิ่งที่ดี มันบังคับใช้จำนวนที่เหมาะสมในเวลารวบรวม

สถาปัตยกรรมจำนวนมากดูเหมือนจะมีคำแนะนำเฉพาะสำหรับการจัดการกับint-> floatการแปลง การแปลงจากunsignedสามารถจะช้ากว่า (เล็กน้อย)


8

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


1
อาจเป็นการดีกว่าที่จะไม่ผสมค่าที่ถูกต้องกับการบ่งชี้ข้อผิดพลาดในตัวแปรเดียวกันและใช้ตัวแปรแยกต่างหากสำหรับสิ่งนี้ ได้รับไลบรารีมาตรฐาน C ไม่ได้เป็นตัวอย่างที่ดีที่นี่
รักษาความปลอดภัย

7

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

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


4
แต่ฉันอาจต้องการ จำกัด ค่าของฉันสำหรับหนึ่งเดือนถึง 1 ถึง 12 เท่านั้น ฉันจะใช้ประเภทอื่นเพื่อมันได้หรือไม่ ประมาณหนึ่งเดือน บางภาษาอนุญาตการ จำกัด ค่าเช่นนั้น อื่น ๆ เช่น. Net / C # ให้สัญญาโค้ด แน่นอนว่าจำนวนเต็มไม่เป็นลบเกิดขึ้นค่อนข้างบ่อย แต่ภาษาส่วนใหญ่ที่สนับสนุนประเภทนี้ไม่รองรับข้อ จำกัด เพิ่มเติม ดังนั้นหนึ่งควรใช้ผสม uints และการตรวจสอบข้อผิดพลาดหรือทำทุกอย่างผ่านการตรวจสอบข้อผิดพลาด? ห้องสมุดส่วนใหญ่ไม่ขอ uint ว่าควรใช้อย่างใดอย่างหนึ่งดังนั้นการใช้ one และ casting อาจไม่สะดวก
งาน

@ งานฉันจะบอกว่าคุณควรใช้ข้อ จำกัด ของคอมไพเลอร์ / ล่ามในเดือนของคุณ มันอาจให้หม้อไอน้ำแก่คุณในการตั้งค่า แต่สำหรับอนาคตคุณมีข้อ จำกัด ที่บังคับใช้ซึ่งป้องกันข้อผิดพลาดและสื่อสารสิ่งที่คุณคาดหวังได้ชัดเจนขึ้น การป้องกันข้อผิดพลาดและการทำให้การสื่อสารง่ายขึ้นมีความสำคัญมากกว่าความไม่สะดวกขณะดำเนินการ
daramarak

1
"ฉันอาจต้องการ จำกัด ค่าของฉันสำหรับหนึ่งเดือนถึง 1 ถึง 12 เท่านั้น"หากคุณมีชุดของค่าที่ จำกัด เช่นเดือนคุณควรใช้ประเภทการแจงนับไม่ใช่จำนวนเต็มแบบดิบ
Josh Caswell

6

ฉันใช้unsigned intใน C ++ สำหรับดัชนีอาเรย์ส่วนใหญ่และสำหรับตัวนับใด ๆ ที่เริ่มต้นจาก 0 ฉันคิดว่าเป็นการดีที่จะพูดอย่างชัดเจน "ตัวแปรนี้ไม่สามารถลบได้"


14
คุณน่าจะใช้ size_t ใน c ++
JohnB

2
ฉันรู้ฉันไม่สามารถถูกรบกวนได้
quant_dev

3

คุณควรใส่ใจเรื่องนี้เมื่อคุณต้องจัดการกับจำนวนเต็มที่จริงอาจเข้าใกล้หรือเกินขีด จำกัด ของ int ที่ลงนามแล้ว เนื่องจากจำนวนเต็มบวกสูงสุดของ 32 บิตคือ 2,147,483,647 ดังนั้นคุณควรใช้ int ที่ไม่ได้ลงชื่อถ้าคุณรู้ว่ามันจะเป็น a) ไม่เคยเป็นลบและ b) อาจถึง 2,147,483,648 ในกรณีส่วนใหญ่รวมถึงคีย์ฐานข้อมูลและตัวนับฉันจะไม่เข้าหาตัวเลขประเภทนี้ดังนั้นฉันจึงไม่ต้องกังวลกับตัวเองด้วยความกังวลว่าบิตสัญญาณจะถูกใช้สำหรับค่าตัวเลขหรือเพื่อระบุสัญญาณ

ฉันจะบอกว่า: ใช้ int จนกว่าคุณจะรู้ว่าคุณต้องการ int ที่ไม่ได้ลงชื่อ


2
เมื่อทำงานกับค่าที่สามารถเข้าถึงค่าสูงสุดคุณควรเริ่มตรวจสอบการดำเนินการสำหรับจำนวนเต็มมากเกินโดยไม่คำนึงถึงเครื่องหมาย การตรวจสอบเหล่านี้มักจะง่ายขึ้นสำหรับประเภทที่ไม่ได้ลงนามเนื่องจากการดำเนินการส่วนใหญ่มีผลลัพธ์ที่กำหนดไว้อย่างดีโดยไม่ได้กำหนดและพฤติกรรมการใช้งานที่กำหนดไว้
รักษาความปลอดภัย

3

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

หากคุณเคยเขียนโปรแกรมความน่าเชื่อถือสูงใน Ada คุณยังสามารถใช้ตัวแปรประเภทต่าง ๆ เช่นระยะทางเป็นฟุตเทียบกับระยะทางเป็นเมตรและคอมไพเลอร์ตั้งค่าสถานะหากคุณกำหนดให้อีกตัวหนึ่งโดยบังเอิญ ที่สมบูรณ์แบบสำหรับการเขียนโปรแกรมขีปนาวุธนำทาง แต่ overkill (ปุนตั้งใจ) ถ้าคุณตรวจสอบแบบฟอร์มบนเว็บ ไม่จำเป็นต้องมีอะไรผิดปกติทั้งสองทางตราบเท่าที่มันเหมาะกับความต้องการ


2

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

ด้วยเหตุผลเดียวกับที่ฉันมีในบางกรณีที่เลือกใช้BIGINT(จำนวนเต็ม 64 บิต) แทนที่จะเป็นINTEGER(จำนวนเต็ม 32 บิต) ในตาราง SQL Server ความน่าจะเป็นที่ข้อมูลจะถึงขีด จำกัด 32 บิตภายในระยะเวลาที่เหมาะสมใด ๆ คือ miniscule แต่ถ้ามันเกิดขึ้นผลที่ตามมาในบางสถานการณ์อาจทำลายล้างได้ เพียงให้แน่ใจว่าได้ทำแผนที่ประเภทระหว่างภาษาอย่างถูกต้องหรือคุณจะจบลงด้วยความแปลกประหลาดที่น่าสนใจจริงๆไกลไปตามถนน ...

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


หากคุณกำลังทำงานกับข้อมูลที่ดึงมาจากระบบ SAP ฉันขอแนะนำช่อง BIGINT สำหรับ ID (เช่นหมายเลขลูกค้า, หมายเลขบทความ ฯลฯ ) ตราบใดที่ไม่มีใครใช้สตริงตัวอักษรและตัวเลขเป็น ID นั่นคือ ... ถอนหายใจ
Treb

1

ฉันอยากจะแนะนำว่านอกบริบทการจัดเก็บข้อมูลและบริบทการแลกเปลี่ยนข้อมูล จำกัด พื้นที่หนึ่งควรใช้ประเภทที่ลงนาม ในกรณีส่วนใหญ่ที่จำนวนเต็มที่มีลายเซ็น 32- บิตจะเล็กเกินไป แต่ค่าที่ไม่ได้ลงนาม 32- บิตจะเพียงพอสำหรับวันนี้มันจะไม่นานก่อนที่ค่าที่ไม่ได้ลงนาม 32- บิตจะไม่ใหญ่พอ

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


1
"ฉันจะแนะนำ [... ] โดยทั่วไปใช้ประเภทที่เซ็นชื่อ" อืมคุณลืมที่จะพูดถึงข้อดีของประเภทที่เซ็นชื่อและให้รายการของเวลาที่จะใช้ประเภทที่ไม่ได้ลงนามเท่านั้น "แปลก" ? แม้ว่าการดำเนินการที่ไม่ได้ลงนามส่วนใหญ่จะมีพฤติกรรมและผลลัพธ์ที่กำหนดไว้อย่างดี แต่คุณป้อนพฤติกรรมที่ไม่ได้กำหนดและการใช้งานที่กำหนดไว้เมื่อใช้ประเภทที่ลงชื่อ (ล้น, บิตกะ, ... ) คุณมีคำนิยามที่แปลกประหลาดของ "ความแปลกประหลาด" ที่นี่
รักษาความปลอดภัย

1
@Secure: "ความแปลกประหลาด" ที่ฉันอ้างถึงเกี่ยวข้องกับความหมายของตัวดำเนินการเปรียบเทียบโดยเฉพาะอย่างยิ่งในการดำเนินการที่เกี่ยวข้องกับประเภทที่ลงนามและไม่ได้ลงนาม คุณถูกต้องว่าพฤติกรรมของประเภทที่เซ็นชื่อนั้นไม่ได้กำหนดไว้เมื่อใช้ค่าที่มีขนาดใหญ่พอที่จะล้น แต่พฤติกรรมของประเภทที่ไม่ได้ลงนามอาจเป็นที่น่าแปลกใจแม้ว่าจะจัดการกับจำนวนที่ค่อนข้างน้อย ตัวอย่างเช่น (-3) + (1u) มากกว่า -1 นอกจากนี้ความสัมพันธ์เชื่อมโยงทางคณิตศาสตร์ปกติบางอย่างที่ใช้กับตัวเลขนั้นไม่ได้นำไปใช้กับผู้ที่ไม่ได้ลงชื่อ ตัวอย่างเช่น (ab)> c ไม่ได้บ่งบอกถึง (ac)> b
supercat

1
@Secure: แม้ว่าจะเป็นความจริงที่ว่าเราไม่สามารถพึ่งพาพฤติกรรมการเชื่อมโยงดังกล่าวกับหมายเลขที่ลงนาม "ใหญ่" ได้เช่นกัน แต่พฤติกรรมจะทำงานได้ตามที่คาดไว้เมื่อจัดการกับตัวเลขที่มีขนาด "เล็ก" สัมพันธ์กับโดเมนของจำนวนเต็ม ในทางตรงกันข้ามการไม่เกี่ยวข้องกันดังกล่าวข้างต้นเป็นปัญหากับค่าที่ไม่ได้ลงนาม "2 3 1" ความจริงที่ว่าพฤติกรรมที่มีการลงชื่อนั้นมีพฤติกรรมที่ไม่ได้กำหนดเมื่อใช้งานนอกขอบเขตสามารถอนุญาตให้มีการสร้างโค้ดที่ดีขึ้นในบางแพลตฟอร์มเมื่อใช้ค่าที่เล็กกว่าขนาดคำดั้งเดิม
supercat

1
หากความคิดเห็นเหล่านี้อยู่ในคำตอบของคุณตั้งแต่แรกแทนที่จะเป็นคำแนะนำและ "การโทรหาชื่อ" โดยไม่ให้เหตุผลใด ๆ ฉันก็จะไม่แสดงความคิดเห็น ;) แม้ว่าฉันจะยังไม่เห็นด้วยกับ "ความแปลก" นี่เป็นเพียงความหมายของประเภท ใช้เครื่องมือที่เหมาะสมสำหรับงานที่ได้รับและทราบว่าเป็นเครื่องมือ ประเภทที่ไม่ได้ลงนามเป็นเครื่องมือที่ไม่ถูกต้องเมื่อคุณต้องการความสัมพันธ์ +/- มีเหตุผลที่size_tไม่ได้ลงชื่อและptrdiff_tลงชื่อ
รักษาความปลอดภัย

1
@Secure: หากสิ่งที่ต้องการคือการแสดงลำดับของบิตประเภทที่ไม่ได้ลงนามนั้นยอดเยี่ยม ฉันคิดว่าเราเห็นด้วยที่นั่น และในไมโครขนาดเล็กบางประเภทที่ไม่ได้รับการรับรองอาจมีประสิทธิภาพมากกว่าสำหรับปริมาณที่เป็นตัวเลข นอกจากนี้ยังมีประโยชน์ในกรณีที่ deltas แทนปริมาณตัวเลข แต่ค่าจริงไม่ได้ (เช่นหมายเลขลำดับ TCP) ในทางกลับกันเมื่อใดก็ตามที่ค่าลบที่ไม่ได้ลงชื่อหนึ่งต้องกังวลเกี่ยวกับกรณีมุมแม้ในขณะที่ตัวเลขมีขนาดเล็ก; คณิตศาสตร์ดังกล่าวที่มีค่าที่เซ็นชื่อจะแสดงเฉพาะกรณีมุมเมื่อตัวเลขมีขนาดใหญ่
supercat

1

ฉันใช้ ints ที่ไม่ได้ลงชื่อเพื่อทำให้รหัสของฉันและเจตนาชัดเจนยิ่งขึ้น สิ่งหนึ่งที่ฉันทำเพื่อป้องกันการแปลงโดยนัยที่ไม่คาดคิดเมื่อทำเลขคณิตที่มีทั้งประเภทที่ลงชื่อและไม่ได้ลงชื่อคือการใช้ชื่อย่อที่ไม่ได้ลงชื่อ (โดยปกติ 2 ไบต์) สำหรับตัวแปรที่ไม่ได้ลงนาม สิ่งนี้มีประสิทธิภาพด้วยเหตุผลสองสามประการ:

  • เมื่อคุณใช้เลขคณิตกับตัวแปรย่อที่ไม่ได้ลงชื่อและตัวอักษร (ซึ่งเป็นประเภท int) หรือตัวแปรของประเภท int สิ่งนี้ทำให้มั่นใจได้ว่าตัวแปรที่ไม่ได้ลงนามจะได้รับการเลื่อนระดับเป็นเสมอก่อนที่จะประเมินนิพจน์เนื่องจาก int มีอันดับสูงกว่าสั้นเสมอ . สิ่งนี้จะช่วยหลีกเลี่ยงพฤติกรรมที่ไม่คาดคิดใด ๆ ในการคำนวณทางคณิตศาสตร์ด้วยประเภทที่เซ็นชื่อและไม่ได้ลงนามโดยสมมติว่าผลลัพธ์ของนิพจน์เหมาะสมกับการลงทะเบียน int แน่นอน
  • ส่วนใหญ่แล้วตัวแปรที่ไม่ได้ลงนามที่คุณใช้จะไม่เกินค่าสูงสุดของ short-byte ที่ไม่ได้ลงชื่อ 2 ตัว (65,535)

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

ตัวอย่างเช่นเมื่อเร็ว ๆ นี้ฉันมีห่วงบางอย่างเช่นนี้

const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
    if((i-2)%cuint == 0)
    {
       //Do something
    }
}

ตัวอักษร '2' เป็นประเภท int หากฉันเป็น int ที่ไม่ได้ลงนามแทนที่จะเป็น short short ที่ไม่ได้ลงชื่อดังนั้นในนิพจน์ย่อย (i-2), 2 จะได้รับการเลื่อนระดับเป็น int ที่ไม่ได้ลงนาม (เนื่องจาก int ที่ไม่ได้ลงชื่อมีลำดับความสำคัญสูงกว่า int ที่ลงนามแล้ว) ถ้า i = 0 ดังนั้นนิพจน์ย่อยจะเท่ากับ (0u-2u) = ค่าที่มากเนื่องจากการล้น แนวคิดเดียวกันกับ i = 1 อย่างไรก็ตามเนื่องจาก i เป็นคำย่อที่ไม่ได้ลงชื่อจึงได้รับการเลื่อนระดับเป็นประเภทเดียวกับตัวอักษร '2' ซึ่งลงนาม int และทุกอย่างทำงานได้ดี

เพื่อความปลอดภัยที่เพิ่มขึ้น: ในกรณีที่ไม่ค่อยเกิดขึ้นซึ่งสถาปัตยกรรมที่คุณใช้งานอยู่ทำให้เกิด int เป็น 2 ไบต์สิ่งนี้อาจทำให้ตัวถูกดำเนินการทั้งสองในนิพจน์ทางคณิตศาสตร์ได้รับการเลื่อนตำแหน่งให้เป็น int ที่ไม่ได้ลงชื่อในกรณีที่ตัวแปรสั้น ลงใน int 2-byte ที่ลงชื่อแล้วซึ่งส่วนหลังมีค่าสูงสุด 32,767 <65,535 (ดูhttps://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsignedสำหรับรายละเอียดเพิ่มเติม) เพื่อป้องกันสิ่งนี้คุณสามารถเพิ่ม static_assert เข้ากับโปรแกรมของคุณดังนี้:

static_assert(sizeof(int) == 4, "int must be 4 bytes");

และมันจะไม่คอมไพล์ในสถาปัตยกรรมที่ int คือ 2 ไบต์

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