ยาว ๆ ใน c99

Jan 10 2021

ในมาตรฐาน C99 long longพวกเขาแนะนำ จุดประสงค์ของสิ่งนี้คืออะไร? จากประสบการณ์การเขียนโปรแกรม C (จำกัด ) ของฉันฉันมีเพียงทุกครั้งที่เห็น int 4 ไบต์และยาว 8 ไบต์ ตัวอย่างเช่นจาก Compiler Explorer:

ถ้าlongเป็นแล้ว8ทำไมต้องเพิ่มแบบอื่นlong long? สิ่งนี้ทำอะไรกับคอมไพเลอร์ / สถาปัตยกรรม?

คำตอบ

6 chux-ReinstateMonica Jan 10 2021 at 05:36

ถ้า long เป็น 8 แล้วทำไมต้องเพิ่ม long type อีก? สิ่งนี้ทำอะไรกับคอมไพเลอร์ / สถาปัตยกรรม?

"if long is already 8" ไม่เป็นความจริงเสมอไปเนื่องจากมีโค้ดจำนวนมากที่อาศัย 32 บิตlongและintเป็น 32 หรือ 16 บิต

การกำหนดlongให้เป็น 64 บิตจะทำลายฐานรหัส นี่เป็นข้อกังวลหลัก


แต่การกำหนดlongให้ยังคงเป็น 32 บิต (และไม่ใช่long long) จะไม่ทำให้สามารถเข้าถึงจำนวนเต็ม 64 บิตมาตรฐานได้ดังนั้นจึงมีเหตุผลสำหรับlong long.

การอนุญาตlongเป็น 32 บิตหรือ 64 บิต (หรืออื่น ๆ ) ทำให้สามารถเปลี่ยนได้

ฟังก์ชั่นต่างๆผ่านเข้า / กลับlongเช่นfseek(), ftell(). พวกเขาได้รับประโยชน์จากlongการเป็นมากกว่า 32 บิตสำหรับการรองรับไฟล์ขนาดใหญ่

แนวทางปฏิบัติที่แนะนำจะส่งเสริมให้กว้างขึ้นlong: "ประเภทที่ใช้size_tและptrdiff_tไม่ควรมีอันดับการแปลงจำนวนเต็มมากกว่านั้นsigned long intเว้นแต่ว่าการนำไปใช้นั้นรองรับออบเจ็กต์ที่มีขนาดใหญ่พอที่จะทำให้จำเป็น" สิ่งนี้เกี่ยวข้องกับขนาดหน่วยความจำที่เกิน 32 บิต


บางทีในอนาคตการใช้งานอาจใช้int/long/long long/intmax_tเป็น 32/64/128/256 บิต

IAC ผมเห็นความกว้างคงประเภทintN_tความนิยมเพิ่มขึ้นมากกว่าและlong long longฉันมักจะใช้การแก้ไขประเภทกว้างหรือbool( unsigned) char, int/ unsigned, size_t( u) intmax_tและลาsigned char( unsigned) short( unsigned) long( unsigned) long longสำหรับกรณีพิเศษ

4 dbush Jan 10 2021 at 05:26

มาตรฐาน C รับประกันเฉพาะว่าintสามารถ (พูดหลวม ๆ ) 2 ไบต์ a longสามารถเป็น 4 ไบต์และlong longสามารถเป็น 8 ไบต์

ในความเป็นจริง MSVC ยังคงใช้ 4 ไบต์longแม้ว่าจะมี 4 ไบต์intก็ตาม

3 NateEldredge Jan 10 2021 at 05:38

ข้อกำหนดเดียวที่เกี่ยวข้องสำหรับintและlongตอนนี้คือintต้องมีอย่างน้อย 16 บิตและlongต้องมีอย่างน้อย 32 บิต ระบบ 16 และ 32 บิตมักจะมี 32 บิตlongและเครื่อง 64 บิตนั้นพบได้น้อยกว่ามากในช่วงปลายทศวรรษ 1990 ดังนั้นก่อนหน้า C99 โปรแกรมเมอร์จึงไม่สามารถพึ่งพาการมีประเภทจำนวนเต็ม 64 บิตได้เลย ปัญหานั้นได้รับการแก้ไขโดยการแนะนำlong longซึ่งจะต้องมีอย่างน้อย 64 บิต (ฉันเชื่อว่า GCC มีให้แล้วและอาจเป็นคอมไพเลอร์อื่น ๆ เป็นส่วนเสริม)

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

2 PeterCordes Jan 10 2021 at 10:29

ฉันคิดว่าคุณไม่ได้ตระหนักว่าคุณกำลังตั้งสมมติฐานผิดอย่างมากเกี่ยวกับการทำงานของข้อกำหนดความกว้างประเภท C: ISO C เพียงกำหนดช่วงค่าต่ำสุดเช่นขนาดที่เล็กที่สุดที่อนุญาตLONG_MAXและLONG_MIN(-2147483647 ไม่ใช่ 8 เพราะ ISO C อนุญาตให้มีส่วนเติมเต็มและจำนวนเต็ม / ขนาดที่ลงนามไม่ใช่เฉพาะส่วนเติมเต็มของ 2) การใช้งานจริงได้รับอนุญาตให้มีประเภทที่กว้างขึ้นซึ่งมักจะตรงกับความกว้างรีจิสเตอร์หรือขนาดตัวถูกดำเนินการที่เครื่องเป้าหมายทำได้อย่างมีประสิทธิภาพ

มีการเขียนเกี่ยวกับเรื่องนี้มากมายใน Stack Overflow และที่อื่น ๆ ซึ่งฉันจะไม่พยายามทำซ้ำที่นี่ ดูสิ่งนี้ด้วยhttps://en.cppreference.com/w/c/language/arithmetic_types


นั่นทำให้คุณผิดพลาดในการดูตัวเลือกประเภทความกว้างใน x86-64 System V ABI และสมมติว่าการใช้งาน C อื่น ๆ เหมือนกันฉันคิดว่า x86-64 เป็น ISA 64 บิตที่สามารถทำงานกับจำนวนเต็ม 64 บิตlongได้อย่างมีประสิทธิภาพดังนั้น 64 บิตจึงเป็นทางเลือกที่ดีพอสมควร

ไม่มี ABI ที่มีเหตุผลสำหรับเครื่อง 32 บิตเช่น i386 จะใช้ 64 บิตlongเพราะไม่จำเป็นต้องใช้เพียง 32 บิต การใช้ 64 บิตหมายความว่าไม่สามารถใส่ลงในทะเบียนเดียวได้ คอมไพล์-m32หรือคอมไพล์สำหรับ ARM 32 บิต Godbolt ยังมี GCC สำหรับ AVR และ MSP430 ในเครื่อง 8 บิตและ 16 บิต GCC จะเลือกความกว้างที่เล็กที่สุดที่ ISO C อนุญาต (2 ไบต์intฯลฯ )

ในปี 1999 x86-64 ไม่มีอยู่จริง (ISAs 64 บิตอื่น ๆ ทำเช่น Alpha) ดังนั้นการมองไปที่หนึ่งใน 2 ABI หลักเพื่อให้เข้าใจตัวเลือก C99 นั้นจะไม่ทำให้คุณไปไกลมากนัก

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


และ BTW, x86-64 สามารถทำสิ่งที่เป็นจำนวนเต็ม 32 บิตได้อย่างมีประสิทธิภาพเช่นเดียวกับ 64 บิตบางครั้งก็มีประสิทธิภาพมากกว่า ดังนั้นการสร้างlongประเภท 64 บิตจึงไม่ใช่เนื้อหาที่ดี รหัสบางตัวใช้longเพราะต้องการประเภทที่ต้องเป็น 32 บิต แต่ไม่ได้รับประโยชน์จากการกำหนดให้กว้างขึ้น สำหรับรหัสดังกล่าว 64 บิตlongจะเสียเพียงแค่การใช้แคช / แบนด์วิธหน่วยความจำและขนาดโค้ด (คำนำหน้า REX) ใน C99 ตัวเลือกที่ดีที่สุดน่าจะเป็นint_least32_tแต่พิมพ์ได้นานและไม่ค่อยได้ใช้งานจนน่ารำคาญ

แต่ OTOH longบางครั้งก็หวังว่าจะเป็น "ประเภทที่มีประสิทธิภาพที่กว้างที่สุด (1-register)" แม้ว่าจะไม่มีการรับประกันดังกล่าวก็ตามและ LLP64 ABI เช่น Windows x64 ที่มี 32 บิตlongก็ไม่เป็นเช่นนั้น

เวิร์มอีกกระป๋องหนึ่งคือ C99 int_fast32_tและ x86-64 IMO ของ System V ทางเลือกที่ไม่ดีในการทำให้เป็นประเภท 64 บิต (ฉันมีคำตอบครึ่งข้อสำหรับCpp uint32_fast_t แก้ไขเป็น uint64_t แต่ช้ากว่าสำหรับการดำเนินการเกือบทั้งหมดกว่า uint32_t (x86_64) ทำไมถึงแก้เป็น uint64_tซึ่งฉันควรจะเสร็จ ... int_fast32_tทำให้เกิดคำถามว่า "เร็วเพื่ออะไร วัตถุประสงค์ "และในการนำไปใช้งานจำนวนมากไม่ใช่สิ่งที่คุณคาดหวังสำหรับหลาย ๆ กรณี

ดูสิ่งนี้ด้วย

  • C ++ - ประเภทจำนวนเต็มเร็วที่สุด?
  • ควรกำหนดประเภท [u] int_fastN_t สำหรับ x86_64 โดยมีหรือไม่มี x32 ABI ได้อย่างไร
  • เหตุใดจึงควรเลือกใช้ uint32_t มากกว่า uint_fast32_t
  • เหตุใด uint_least16_t จึงเร็วกว่า uint_fast16_t สำหรับการคูณใน x86_64
  • การเพิ่มประสิทธิภาพคอมไพลเลอร์อนุญาตผ่านประเภทความกว้างที่ไม่คงที่ C / C ++ "int" "น้อยที่สุด" และ "เร็ว"
old_timer Jan 11 2021 at 06:16

มีข้อ จำกัด บางประการ แต่ผู้เขียนคอมไพเลอร์มีอิสระที่จะเลือกความยาวสำหรับประเภทตัวแปร C มาตรฐาน (char, short, int, long, long long) โดยธรรมชาติถ่านจะเป็นไบต์สำหรับสถาปัตยกรรมนั้น (ส่วนใหญ่ที่มีคอมไพเลอร์ C คือ 8 บิต) และโดยธรรมชาติคุณไม่สามารถมีสิ่งที่เล็กกว่าใหญ่กว่าสิ่งที่ใหญ่กว่าความยาวต้องไม่เล็กกว่า int แต่อย่างแน่นอนภายในปี 1999 เราได้เห็นการเปลี่ยน x86 16 เป็น 32 บิตและตัวอย่างเช่น int เปลี่ยนจาก 16 เป็น 32 ด้วยเครื่องมือจำนวนหนึ่ง แต่อยู่นาน 32 ต่อมาการเปลี่ยน 32 ถึง 64 บิต x86 เกิดขึ้นและขึ้นอยู่กับเครื่องมือว่ามีประเภทที่พร้อมใช้ เพื่อช่วย.

ปัญหานี้เกิดขึ้นมานานแล้วและวิธีแก้ปัญหาไม่ได้เป็นการแก้ไขความยาวของประเภทซึ่งอยู่ในกฎขึ้นอยู่กับผู้เขียนคอมไพเลอร์ตามขนาด แต่ผู้เขียนคอมไพเลอร์จำเป็นต้องสร้างไฟล์ stdint.h ที่ตรงกับเครื่องมือและเป้าหมาย (stdint.h มีความเฉพาะเจาะจงสำหรับเครื่องมือและกำหนดเป้าหมายเป็นอย่างน้อยและอาจเป็นเวอร์ชันของเครื่องมือและตัวเลือกการสร้างสำหรับเครื่องมือนั้นเป็นต้น) ตัวอย่างเช่น uint32_t จะเป็น 32 บิตเสมอ ผู้เขียนบางคนจะแปลงสิ่งนั้นเป็น int อื่น ๆ เป็นเวลานาน ฯลฯ ใน stdint.h ประเภทตัวแปรภาษา C ยังคง จำกัด อยู่ที่ char, short, int และอื่น ๆ ตามภาษา (uint32_t ไม่ใช่ประเภทตัวแปรซึ่งจะถูกแปลงเป็นประเภทตัวแปรผ่าน stdint.h) วิธีแก้ปัญหา / วิธีแก้ปัญหานี้เป็นวิธีที่จะทำให้ทุกคนคลั่งไคล้และทำให้ภาษาคงอยู่

ผู้เขียนมักจะเลือกเช่นถ้า GPR เป็น 16 บิตให้ int เป็น 16 บิตและถ้า 32 บิตเป็น 32 บิตเป็นต้น แต่ก็มีอิสระบ้าง

ใช่หมายความโดยเฉพาะว่าไม่มีเหตุผลที่จะสันนิษฐานว่าเครื่องมือสองตัวสำหรับเป้าหมายเฉพาะ (เช่นคอมพิวเตอร์ที่คุณกำลังอ่านสิ่งนี้) ใช้คำจำกัดความเดียวกันสำหรับ int และ long โดยเฉพาะและหากคุณต้องการเขียนโค้ดสำหรับ แพลตฟอร์มนี้ที่สามารถพอร์ตข้ามเครื่องมือเหล่านี้ (ที่รองรับแพลตฟอร์มนี้) จากนั้นใช้ประเภท stdint.h และไม่ใช่ int, long ฯลฯ ... แน่นอนที่สุดถ้าคุณกำลังข้ามแพลตฟอร์ม msp430 mcu, arm mcu, เครื่อง arm linux ซึ่งเป็นเครื่องที่ใช้ x86 ซึ่งประเภทแม้กระทั่งสำหรับ "toolchain" เดียวกัน (เช่น gnu gcc และ binutils) ไม่มีคำจำกัดความเดียวกันสำหรับ int และ long เป็นต้นถ่านและ short มักจะเป็น 8 และ 16 บิต int และ long มีแนวโน้มที่จะแตกต่างกันมากที่สุดบางครั้งขนาดเท่ากันบางครั้งก็แตกต่างกัน แต่ประเด็นคือไม่ถือว่า

เป็นเรื่องเล็กน้อยในการตรวจจับขนาดสำหรับตัวเลือกเวอร์ชัน / เป้าหมาย / บรรทัดคำสั่งของคอมไพเลอร์หรือเพียงแค่ไปที่เส้นทาง stdint เพื่อลดปัญหาในภายหลัง