จำนวนเต็มใช้เป็นประเภทข้อมูลมากเกินไปหรือไม่


9

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

บางอย่างดูน่ากลัวเล็กน้อยเกี่ยวกับรหัสเช่นนี้

TStuffRec = record
   recordID : Integer;
   thingID : Integer;
   otherThingID : Integer;
end;

เมื่อมันสามารถเขียนเป็น

TStuffRec = record
   recordID : Cardinal;
   thingID : Cardinal;
   otherThingID : Cardinal;
end;

ฟังก์ชั่นบันทึกเหล่านี้มักจะทำงานเหมือนเดิม (และหวังว่าจะยังคงทำงานเหมือนเดิมแม้ใน Delphi 64 บิต) แต่จำนวนมากจะมีปัญหาการแปลง

แต่มีข้อเสียคือการใช้ int ที่ไม่ได้ลงชื่อเช่นกัน สาเหตุหลักมาจากการผสมสองอย่างนี้เข้าด้วยกัน

คำถามจริงคือสิ่งนี้จริง ๆ แล้วคิดหรือรวมอยู่ในแนวทางปฏิบัติที่ดีที่สุดหรือไม่? มันมักจะขึ้นอยู่กับนักพัฒนาหรือไม่


5
ปีเตอร์คุณกำลังมองหาคำตอบเฉพาะ Delphi เท่านั้น?
อดัมเลียร์

3
@Anna เข้าใจวิธีการทำงานของประเภทข้อมูล Delphi จะทำให้คำตอบที่ดีที่สุด ฉันค่อนข้างแน่ใจว่าโปรแกรมเมอร์ C สามารถเข้าใจและตอบคำถามนี้ได้
Peter Turner

คำตอบ:


9

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

for i := 0 to List.Count - 1 do
  //do something here

ฉันได้iประกาศว่าเป็นจำนวนเต็มไม่ได้ลงนาม (หลังจากทั้งหมดมันเป็นดัชนีในรายการที่เริ่มต้นที่ 0 มันไม่จำเป็นต้องเป็นลบใช่ไหม?) แต่เมื่อList.Countเป็น 0 มันจะไม่ทำให้เกิดการวนซ้ำตามที่คาดไว้เพราะ0 - 1หาค่าเป็นจำนวนบวกที่สูงจริงๆ อ๊ะ!

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


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

@Aaraught: ไม่ได้อยู่ใน Delphi (อย่างน้อยก็ไม่ใช่ถ้าคุณไม่ทำอะไรโง่ ๆ เช่นปิดการตรวจสอบการล้นในตัว) คุณจะได้รับข้อยกเว้นเมื่อตัวนับล้นแทนการวนซ้ำไม่สิ้นสุด มันยังคงเป็นบั๊ก แต่มันง่ายกว่ามากในการติดตาม
Mason Wheeler

ถ้าคุณบอกว่าดังนั้น. ฉันปิดใช้งานการตรวจสอบมากเกินไปใน Delphi เสมอ หลังจากได้รับการโจมตีอย่างไม่รู้จบด้วยผลบวกเท็จจากสิ่งต่าง ๆ เช่นรหัสแฮชและเช็คซัมฉันเพิ่งยอมแพ้กับ "ฟีเจอร์" ทั้งหมด แต่ฉันคิดว่าคุณพูดถูกมันจะจับข้อผิดพลาดนั้นได้
Aaronaught

@Aaraught: ใช่คุณต้องการปิดการใช้งานสำหรับสิ่งต่าง ๆ เช่นรหัสแฮชและเช็คซัมที่ออกแบบมาเป็นพิเศษเพื่อให้ล้นและล้อมรอบ แต่สำหรับการคำนวณทั่วไปที่ไม่ได้ออกแบบมาเพื่อให้ล้นและพันไปรอบ ๆ มันเป็นคุณสมบัติด้านความปลอดภัยที่สำคัญและการปิดเครื่องนั้นก็เหมือนกับการขับขี่ที่ไม่มีเข็มขัดนิรภัย
Mason Wheeler

บางทีคุณอาจลืมไปแล้ว แต่การตรวจสอบโอเวอร์โฟลว์และคำสั่งของคอมไพเลอร์นั้นบั๊กกี้อย่างไม่น่าเชื่อใน Delphi รุ่นเก่า ฉันจำได้อย่างชัดเจนถึงการฉีกผมออกหลายครั้งหลังจากเห็นตัวดีบั๊กหยุดตรงกลางบล็อก {$ O -} / {$ O +} เพื่อรายงานการไหลล้นอย่างมีความสุข หลังจากที่ในขณะที่ฉันไม่สามารถใช้อีกต่อไปและเพียงแค่ปิดการใช้งานทั่วโลก อีกครั้งใช่มันจะมีปัญหานี้ แต่ฉันก็ยังไม่คิดว่ามันคุ้มค่ากับจำนวนของการบวกเท็จ แน่นอนสำหรับตัวเขาแต่ละคน!
Aaronaught

3

ความซื่อสัตย์ฉันมักจะใช้จำนวนเต็มตามนิสัย ฉันคุ้นเคยกับการที่พวกเขาเสนอช่วงใหญ่พอสำหรับสถานการณ์ส่วนใหญ่และอนุญาตให้มีค่าลบ (เช่น -1) แท้จริงแล้วหลายครั้งที่ใช้ไบต์ / คำ / shortint จะเหมาะสมกว่า ตอนนี้คิดว่าฉันสามารถมุ่งเน้นไปที่จุดเหล่านี้:

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

  • ลูป บ่อยครั้งที่ฉันเขียนลูป "จาก i: = 0 ถึง Count-1" จะเกิดอะไรขึ้นถ้า "i" เป็นไบต์และ Count = 0 คือลูปนั้นจะเริ่มจาก 0 ถึง 255 ไม่ใช่ว่าฉันต้องการมัน

  • Uniforming ง่ายต่อการจดจำและใช้ "var i: integer;" กว่าที่จะหยุดในแต่ละกรณีและคิดว่า"หืมม .. ที่นี่เรากำลังติดต่อกับ 0..120 ช่วง .. ไบต์ .. ไม่รอเราอาจต้อง -1 สำหรับการเตรียมการเบื้องต้น .. ย่อ .. .. ถ้า 128 คือ ไม่พอ .. หรือ"ทำไมจึงเป็นขนาดเล็กในสถานที่นี้ไม่ใช่ shortint?"

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

  • -1 แม้เมื่อค่าอยู่ในช่วง 0..n-1 ฉันมักต้องตั้งค่า "ไม่มีค่า / ไม่รู้จัก / ไม่รู้จัก / ไม่กำหนด / ว่างเปล่า" ซึ่งเป็นวิธีปฏิบัติทั่วไป -1

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

ป.ล. ฉันจะใช้ประเภทอื่นเมื่อใด

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

1

แนวทางปฏิบัติที่ดีที่สุดคือการใช้ประเภทข้อมูลที่เหมาะกับความต้องการข้อมูลที่ใช้ (ข้อมูลที่คาดหวัง)

ตัวอย่าง C #: หากฉันต้องการรองรับ 0 ถึง 255 ฉันจะใช้ไบต์

ถ้าฉันต้องการสนับสนุน 1,000,000 ลบและบวกแล้ว int

ใหญ่กว่า 4.2 พันล้านจากนั้นใช้เป็นเวลานาน

โดยการเลือกประเภทที่ถูกต้องโปรแกรมจะใช้จำนวนหน่วยความจำที่เหมาะสมที่สุดและประเภทที่แตกต่างใช้หน่วยความจำที่แตกต่าง

นี่คือการอ้างอิง C # int จาก MSDN

int 
 -2,147,483,648 to 2,147,483,647
 Signed 32-bit integer

uint 
 0 to 4,294,967,295
 Unsigned 32-bit integer

long 
 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 Signed 64-bit integer

ulong 
 0 to 18,446,744,073,709,551,615
 Unsigned 64-bit integer

ใน C # (หรือ. net โดยทั่วไป) จะยาวและ ulong จะกลายเป็น 128 บิตบนเครื่อง 128 บิตหรือไม่ เพราะใน Delphi Integerประเภทข้อมูลคือ 32 บิตบนเครื่อง 32 บิตและเห็นได้ชัดว่าจะเป็น 64 บิตบนเครื่อง 64 บิต
Peter Turner

1
@ Peter Turner: ไม่ใน C # intเป็นเพียงการจดชวเลขSystem.Int32ไม่ว่าเครื่องจะทำงานกับรหัสใด
nikie

@nikie มันเหมือนกับtype int System.Int32หรืออะไรกับเอฟเฟกต์นั่น? มันสามารถเปลี่ยนแปลงได้อย่างง่ายดายในเวอร์ชันอนาคตของกรอบงานหรือไม่
Peter Turner

@ Peter Turner / nikie (sizeof (int) .ToString ()); ==> ส่งคืน 4 (ขนาดของ (Int64) .ToString ()); ==> ส่งคืน 8 บน Windows OS 64 บิตของฉัน ในฐานะ nikie, สถิติ, int จริงๆและ Int32
Jon Raynor

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

1

ประเภทจำนวนเต็มที่ไม่ได้ลงนามควรใช้เพื่อเป็นตัวแทนของตัวเลขที่สำคัญในภาษาที่พวกเขาเป็นตัวแทนของตัวเลขที่สำคัญ เนื่องจากวิธีการที่คอมพิวเตอร์ที่ทำงาน C เกิดขึ้นทำงานประเภทจำนวนเต็มไม่ได้ลงนามจะทำหน้าที่เป็นสมาชิกของวงแหวนพีชคณิต mod-2 ^ n (หมายถึงการคำนวณที่โอเวอร์โฟลว์จะ "ตัด" คาดการณ์ได้) และภาษาระบุว่าในหลายกรณี จำเป็นต้องมีพฤติกรรมเป็นแหวนพีชคณิตนามธรรมแม้ว่าพฤติกรรมดังกล่าวจะไม่สอดคล้องกับพฤติกรรมของตัวเลขที่สำคัญหรือจำนวนเต็มทางคณิตศาสตร์

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

เมื่อพิจารณาถึงการขาดชนิดตัวเลขที่สำคัญ แต่โดยทั่วไปแล้วควรใช้เลขจำนวนเต็มเพื่อแสดงทั้งจำนวนเต็มเชิงคณิตศาสตร์และตัวเลขสำคัญ

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