uint8_t กับถ่านที่ไม่ได้ลงชื่อ


231

อะไรคือข้อได้เปรียบของการใช้uint8_tเกินunsigned charใน C?

ฉันรู้ว่าในเกือบทุกระบบuint8_tเป็นเพียง typedef unsigned charแล้วทำไมต้องใช้มันล่ะ

คำตอบ:


225

มันเป็นเอกสารแสดงเจตจำนงของคุณ - คุณจะเก็บตัวเลขขนาดเล็กมากกว่าตัวอักษร

นอกจากนี้ยังจะดูดีกว่าถ้าคุณกำลังใช้ typedefs อื่น ๆ เช่นหรือuint16_tint32_t


1
มันไม่ชัดเจนในคำถามต้นฉบับถ้าเราพูดถึงประเภทมาตรฐานหรือไม่ ฉันแน่ใจว่ามีหลายรูปแบบของแผนการตั้งชื่อนี้ในช่วงหลายปีที่ผ่านมา
Mark Ransom

8
การใช้unsigned charหรือsigned charเอกสารแสดงเจตนาเช่นกันอย่างชัดเจนเนื่องจากไม่มีการตกแต่งcharคือสิ่งที่แสดงว่าคุณกำลังทำงานกับตัวละคร
caf

9
ฉันคิดว่าเครื่องที่ไม่มีเครื่องตกแต่งunsignedนั้นเป็นunsigned intตามคำจำกัดความ?
Mark Ransom

5
@endolith การใช้ uint8_t สำหรับสตริงนั้นไม่จำเป็นว่าจะผิด แต่มันแปลกมาก
Mark Ransom

5
@ ท้ายที่สุดฉันคิดว่าฉันสามารถสร้างกรณีสำหรับ uint8_t ด้วยข้อความ UTF8 แท้จริงแล้วcharดูเหมือนว่าจะบ่งบอกถึงตัวละครในขณะที่ในบริบทของสตริง UTF8 มันอาจเป็นเพียงหนึ่งไบต์ของอักขระหลายไบต์ การใช้ uint8_t สามารถทำให้ชัดเจนว่าใครไม่ควรคาดหวังอักขระทุกตำแหน่ง - กล่าวอีกนัยหนึ่งว่าแต่ละองค์ประกอบของสตริง / อาร์เรย์เป็นจำนวนเต็มโดยพลการที่เราไม่ควรตั้งสมมติฐานเชิงความหมายใด ๆ แน่นอนว่าโปรแกรมเมอร์ C ทุกคนรู้สิ่งนี้ แต่มันอาจผลักให้ผู้เริ่มต้นถามคำถามที่ถูกต้อง
tne

70

เพียงแค่พูดจาหยาบคายบางระบบอาจไม่มีประเภท 8 บิต ตามที่Wikipedia :

จำเป็นต้องมีการติดตั้งเพื่อกำหนดประเภทจำนวนเต็มความกว้างที่แน่นอนสำหรับ N = 8, 16, 32, หรือ 64 หากมีประเภทใดที่ตรงกับข้อกำหนด มันไม่จำเป็นต้องกำหนดมันสำหรับ N อื่นใด ๆ แม้ว่ามันจะรองรับชนิดที่เหมาะสม

ดังนั้นจึงuint8_tไม่รับประกันว่าจะมีอยู่แม้ว่าจะใช้กับทุกแพลตฟอร์มที่ 8 บิต = 1 ไบต์ แพลตฟอร์มที่ฝังตัวบางอย่างอาจแตกต่างกันไป แต่ก็หาได้ยากมาก ระบบบางระบบอาจกำหนดcharชนิดให้เป็น 16 บิตซึ่งในกรณีนี้อาจจะไม่มีประเภท 8 บิต

นอกจากปัญหา (เล็กน้อย) คำตอบของ @Mark Ransomนั้นดีที่สุดในความคิดของฉัน ใช้สิ่งที่แสดงสิ่งที่คุณกำลังใช้ข้อมูลให้ชัดเจนที่สุด

นอกจากนี้ฉันสมมติว่าคุณหมายถึงuint8_t(typedef มาตรฐานจาก C99 ที่ให้ไว้ในstdint.hส่วนหัว) มากกว่าuint_8(ไม่ใช่ส่วนหนึ่งของมาตรฐานใด ๆ )


3
@caf ออกมาจากความอยากรู้ - คุณสามารถลิงค์ไปยังคำอธิบายของบางส่วนได้หรือไม่? ฉันรู้ว่ามีอยู่เพราะมีคนพูดถึงหนึ่ง (และเชื่อมโยงกับเอกสารของนักพัฒนา) ใน comp.lang.c ++ การสนทนาที่ตรวจสอบว่าการค้ำประกันประเภท C / C ++ อ่อนแอเกินไป แต่ฉันไม่พบกระทู้นั้นอีกต่อไปและเป็นประโยชน์เสมอ การอ้างอิงที่คล้ายกันในการอภิปรายใด ๆ :)
พาเวล Minaev

3
"ระบบบางระบบอาจกำหนดประเภท char เป็น 16 บิตซึ่งในกรณีนี้อาจจะไม่มีประเภท 8 บิตใด ๆ " - และถึงแม้จะมีการคัดค้านที่ไม่ถูกต้องจากฉันพาเวลได้แสดงให้เห็นในคำตอบของเขาว่าถ้าถ่านเป็น 16 บิตแล้วแม้ว่าคอมไพเลอร์จะให้ประเภท 8 บิตมันจะต้องไม่เรียกมันuint8_t(หรือพิมพ์ลงไป) นี่เป็นเพราะประเภท 8 บิตจะมีบิตที่ไม่ได้ใช้ในการเป็นตัวแทนจัดเก็บข้อมูลซึ่งuint8_tจะต้องไม่มี
464 Steve Jessop

3
สถาปัตยกรรม SHARC มีคำ 32- บิต ดูen.wikipedia.org/wiki/…สำหรับรายละเอียด
BCran

2
และ C5000 DSPs ของ TI (ซึ่งอยู่ใน OMAP1 และ OMAP2) คือ 16 บิต ฉันคิดว่า OMAP3 พวกเขาไปที่ C6000-series พร้อมถ่าน 8 บิต
Steve Jessop

4
การขุดลงใน N3242 - "Working Draft, มาตรฐานสำหรับการเขียนโปรแกรมภาษา C ++", ส่วนที่ 18.4.1 <cstdint> synopsis พูดว่า - typedef unsigned integer type uint8_t; // optional ดังนั้นโดยพื้นฐานแล้ว, C ++ มาตรฐานที่สอดคล้องกับไลบรารีไม่จำเป็นต้องกำหนด uint8_t เลย (ดูความคิดเห็น // ตัวเลือก )
nightlytrails

43

จุดทั้งหมดคือการเขียนรหัสที่ไม่ขึ้นอยู่กับการใช้งาน unsigned charไม่รับประกันว่าจะเป็นประเภท 8 บิต uint8_tคือ (ถ้ามี)


4
... ถ้ามันมีอยู่ในระบบ แต่มันจะหายากมาก +1
Chris Lutz

2
ดีถ้าคุณมีปัญหากับโค้ดของคุณที่ไม่ได้คอมไพล์ในระบบเพราะ uint8_t ไม่มีอยู่คุณสามารถใช้ find และ sed เพื่อเปลี่ยนการเกิดขึ้นของ uint8_t เป็น char ที่ไม่ได้ลงชื่อโดยอัตโนมัติหรือสิ่งที่มีประโยชน์สำหรับคุณมากกว่า
bazz

2
@bazz - ไม่ใช่ถ้าคุณกำลังสมมติว่ามันเป็นประเภท 8 บิตที่คุณไม่สามารถทำได้ - ตัวอย่างเช่นการคลายแพ็กข้อมูลในแพ็กเกจแบบ bytewise โดยระบบรีโมต ข้อสันนิษฐานโดยนัยคือเหตุผลที่ uint8_t ไม่มีอยู่นั้นอยู่ในตัวประมวลผลที่ตัวถ่านมีค่ามากกว่า 8 บิต
Chris Stratton

โยนในการยืนยันยืนยัน (sizeof (ถ่านที่ไม่ได้ลงชื่อ) == 8);
bazz

3
@bazz การยืนยันที่ไม่ถูกต้องฉันกลัว sizeof(unsigned char)จะกลับมา1เป็น 1 ไบต์ แต่ถ้าระบบ char และ int มีขนาดเท่ากันเช่นเช่น 16- บิตก็sizeof(int)จะกลับมา1
Toby

7

อย่างที่คุณพูดว่า " เกือบทุกระบบ"

charอาจเป็นหนึ่งในโอกาสที่จะเปลี่ยนแปลงน้อยลง แต่เมื่อคุณเริ่มใช้uint16_tและเพื่อน ๆ โดยใช้การuint8_tผสมผสานที่ดีขึ้นและอาจเป็นส่วนหนึ่งของมาตรฐานการเข้ารหัส


7

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

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

#pragma pack(1)
typedef struct {
  uint8_t    flag1:1;
  uint8_t    flag2:1;
  padding1   reserved:6;  /* not necessary but makes this struct more readable */
  uint32_t   sequence_no;
  uint8_t    data[8];
  uint32_t   crc32;
} s_mypacket __attribute__((packed));
#pragma pack()

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

มีข้อแม้บางประการที่ใช้โครงสร้างที่อัดแน่น gotcha ที่ใหญ่ที่สุดคือคุณต้องหลีกเลี่ยงการปฏิเสธที่อยู่ของสมาชิก บนระบบที่มีคำที่จัดแนว mutibyte สิ่งนี้อาจส่งผลให้เกิดข้อยกเว้นที่ไม่ตรงแนว - และ coredump

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

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

นี่คือการสนทนาที่เกี่ยวข้อง:

pragma pack (1) หรือ __attribute__ ((จัดชิด (1))) ใช้งานได้

__attribute __ ของ gcc เป็น (บรรจุแล้ว) / #pragma ไม่ปลอดภัยหรือไม่

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html


6

มีน้อย จากมุมมองการพกพาcharไม่สามารถมีขนาดเล็กกว่า 8 บิตและไม่มีอะไรจะเล็กไปกว่าcharนี้ดังนั้นหากการติดตั้ง C ที่กำหนดมีประเภทจำนวนเต็ม 8 บิตที่ไม่ได้ลงนามจะต้องเป็นcharเช่นนั้น อีกวิธีหนึ่งคืออาจไม่มีอย่างใดอย่างหนึ่งที่จุดtypedefเทคนิคใด ๆที่สงสัย

มันสามารถใช้ในการจัดทำเอกสารรหัสของคุณได้ดีขึ้นในแง่ที่ชัดเจนว่าคุณต้องการไบต์ 8 บิตและไม่มีอะไรอื่นอีก แต่ในทางปฏิบัติมันเป็นความคาดหวังที่สมเหตุสมผลแทบทุกที่แล้ว (มีแพลตฟอร์ม DSP ซึ่งมันไม่เป็นความจริง แต่โอกาสของรหัสที่ใช้มีน้อยและคุณอาจผิดพลาดโดยใช้ static assert ที่ด้านบนสุดของโปรแกรมของคุณ แพลตฟอร์มดังกล่าว)


7
@Skizz - ไม่มาตรฐานunsigned charจะต้องสามารถเก็บค่าได้ระหว่าง 0 ถึง 255 หากคุณสามารถทำได้ใน 4 บิตหมวกของฉันจะปิดให้คุณ
Chris Lutz

1
"มันจะยุ่งยากกว่านี้นิดหน่อย" - ยุ่งยากในแง่ที่ว่าคุณจะต้องเดิน (ว่ายน้ำจับเครื่องบิน ฯลฯ ) ไปจนถึงจุดที่นักเขียนคอมไพเลอร์ตบที่ด้านหลังศีรษะ และทำให้พวกเขาเพิ่มuint8_tการใช้งาน ฉันสงสัยว่าคอมไพเลอร์สำหรับ DSP ที่มีตัวอักษร 16 บิตมักใช้งานuint8_tหรือไม่
Steve Jessop

6
โดยวิธีการที่เกี่ยวกับความคิดที่สองก็อาจจะเป็นวิธีที่ง่ายที่สุดในการบอกว่า "ฉันต้อง 8 บิตจริงๆ" - และการใช้งาน#include <stdint.h> uint8_tหากแพลตฟอร์มมีมันก็จะมอบให้คุณ หากแพลตฟอร์มไม่มีโปรแกรมของคุณจะไม่รวบรวมและเหตุผลจะชัดเจนและตรงไปตรงมา
Pavel Minaev

2
ยังไม่มีซิการ์, ขออภัย: "สำหรับประเภทจำนวนเต็มที่ไม่ได้ลงชื่อนอกเหนือจากถ่านที่ไม่ได้ลงชื่อบิตของการแสดงวัตถุจะถูกแบ่งออกเป็นสองกลุ่ม: ค่าบิตและบิตแพ็ดดิ้ง ... หากมีบิตค่าบิตแต่ละบิตจะเป็นตัวแทนที่แตกต่างกัน กำลังของ 2 ระหว่าง 1 ถึง 2 ^ (N-1) ดังนั้นวัตถุประเภทนั้นจะสามารถแสดงค่าจาก 0 ถึง 2 ^ (N-1) โดยใช้การแทนฐานสองแบบบริสุทธิ์ ... ชื่อ typedef intN_t กำหนด ประเภทเลขจำนวนเต็มที่ลงนามแล้วที่มีความกว้าง N ไม่มีบิตการแพ็ดและการแทนค่าสองส่วน
Pavel Minaev

1
หากคุณต้องการโมดูโลทางคณิตศาสตร์บิตฟิลด์ที่ไม่ได้ลงนามจะทำได้ดี (หากไม่สะดวก) มันคือเวลาที่คุณต้องการพูดว่าอ็อคเท็ตของอ็อตเท็ตโดยไม่มีช่องว่างนั่นคือเมื่อคุณเป็นโซล คุณธรรมของเรื่องไม่ได้สำหรับประมวลสัญญาณและติดเหมาะสม 8 บิตสถาปัตยกรรมถ่านซื่อสัตย์ต่อพระเจ้า :)
พาเวล Minaev

4

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


ย้อนกลับไปเมื่อฉันถามสิ่งนี้ฉันได้กำหนดโปรโตคอลที่ง่ายสำหรับการสื่อสารผ่านอนุกรม
Lyndon White

2

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


3
uint8_t มักจะตรงกับช่วงและขนาดของunsigned charและ padding (ไม่มี) เมื่อunsigned char เป็น 8 บิต เมื่อunsigned charไม่ใช่ 8 บิตuint8_tจะไม่มีอยู่
chux - Reinstate Monica

@ chux คุณมีการอ้างอิงไปยังสถานที่ที่แน่นอนในมาตรฐานที่มันบอกว่า? ถ้าunsigned charเป็น 8 บิตจะuint8_tรับประกันว่าจะเป็นtypedefดังกล่าวและไม่ได้typedefของการขยายชนิดจำนวนเต็มไม่ได้ลงนาม ?
hsivonen

@hsivonen "สถานที่ที่แน่นอนในมาตรฐานที่มันบอกว่า?" -> ไม่ - ยังมองหา 7.20.1.1 มันอนุมานได้อย่างง่ายดายเช่นเดียวunsigned char/signed char/charกับชนิดที่เล็กที่สุด - ไม่น้อยกว่า 8 บิต unsigned charไม่มีช่องว่างภายใน สำหรับการuint8_tที่จะเป็นมันจะต้องเป็น 8 บิต padding ไม่อยู่เนื่องจากการดำเนินการจัดให้มีชนิดจำนวนเต็ม: unsigned charที่ตรงกับความต้องการที่น้อยที่สุดของ ในฐานะที่เป็น "... รับประกันว่าจะเป็น typedef ... " ดูเหมือนคำถามที่ดีในการโพสต์
chux - Reinstate Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.