ใน C / C ++ มีการunsigned char
ใช้เพื่ออะไร มันแตกต่างจากปกติchar
อย่างไร?
ใน C / C ++ มีการunsigned char
ใช้เพื่ออะไร มันแตกต่างจากปกติchar
อย่างไร?
คำตอบ:
ใน C ++ มีอักขระสามประเภทที่แตกต่างกัน:
char
signed char
unsigned char
หากคุณใช้ประเภทตัวอักษรสำหรับข้อความให้ใช้แบบไม่มีเงื่อนไขchar
:
'a'
'0'
"abcde"
มันยังทำงานออกมาเป็นค่าตัวเลข แต่ก็ไม่ได้ระบุว่าค่านั้นจะถือว่าเป็นลงนามหรือไม่ได้ลงนาม ระวังการเปรียบเทียบตัวละครผ่านความไม่เท่าเทียมกัน - แม้ว่าคุณจะ จำกัด ตัวเองไว้ที่ ASCII (0-127) คุณก็ปลอดภัย
หากคุณใช้ประเภทอักขระเป็นตัวเลขให้ใช้:
signed char
ซึ่งให้อย่างน้อยช่วง -127 ถึง 127 (-128 ถึง 127 เป็นเรื่องปกติ)unsigned char
ซึ่งให้ช่วงอย่างน้อย 0 ถึง 255"อย่างน้อยที่สุด" เนื่องจากมาตรฐาน C ++ ให้เฉพาะช่วงค่าต่ำสุดที่จำเป็นต้องใช้สำหรับประเภทตัวเลขแต่ละประเภท sizeof (char)
จะต้องเป็น 1 (เช่นหนึ่งไบต์) แต่ในทางทฤษฎีอาจเป็นตัวอย่าง 32 บิต sizeof
จะยังคงมีรายงานขนาดของมันเป็น1
- ความหมายที่คุณอาจsizeof (char) == sizeof (long) == 1
มี
sizeof
เพราะไม่ใช่ฟังก์ชัน แต่เป็นตัวดำเนินการ มันเป็นสไตล์ที่ดียิ่งขึ้นกว่าการละเว้นวงเล็บเมื่อใช้ขนาดของตัวแปร หรือsizeof *p
sizeof (int)
สิ่งนี้ทำให้ชัดเจนอย่างรวดเร็วหากใช้กับชนิดหรือตัวแปร return
ในทำนองเดียวกันก็ยังเป็นที่ซ้ำซ้อนจะใส่วงเล็บหลัง มันไม่ใช่ฟังก์ชั่น
char
: มันเป็นประเภทของตัวอักษรที่เหมือน'a'
หรือ'0'
" เป็นความจริงใน C ++ แต่ไม่เซลเซียสใน C, เป็น'a'
int
นี้คือการดำเนินการขึ้นอยู่กับมาตรฐาน C ไม่ได้กำหนดลงนาม-Ness char
ของ ทั้งนี้ขึ้นอยู่กับแพลตฟอร์มถ่านอาจจะsigned
หรือunsigned
ดังนั้นคุณจึงจำเป็นอย่างชัดเจนขอsigned char
หรือunsigned char
ถ้าการดำเนินการของคุณขึ้นอยู่กับมัน เพียงใช้char
ถ้าคุณต้องการแสดงอักขระจากสตริงเนื่องจากสิ่งนี้จะตรงกับที่แพลตฟอร์มของคุณใส่ในสตริง
ความแตกต่างระหว่างsigned char
และunsigned char
เป็นอย่างที่คุณคาดหวัง บนแพลตฟอร์มส่วนใหญ่signed char
จะเป็นหมายเลขเสริมสองบิต 8 บิตตั้งแต่-128
ถึงถึง127
และunsigned char
จะเป็นเลขจำนวนเต็ม 8 บิตที่ไม่ได้ลงชื่อ ( 0
ถึง255
) หมายเหตุมาตรฐานไม่จำเป็นต้องให้char
ประเภทมี 8 บิตเท่านั้นที่กลับมาsizeof(char)
1
คุณจะได้รับจำนวนบิตในถ่านที่มีในCHAR_BIT
limits.h
มีน้อยถ้าแพลตฟอร์มใด ๆ ในวันนี้ซึ่งจะเป็นสิ่งอื่นนอกเหนือ8
จาก
มีบทสรุปที่ดีของปัญหานี้คือที่นี่
อย่างที่คนอื่น ๆ พูดถึงตั้งแต่ฉันโพสต์สิ่งนี้คุณควรใช้int8_t
และuint8_t
ถ้าคุณต้องการแสดงจำนวนเต็มเล็กน้อย
CHAR_BIT
จำเป็นต้องมีอย่างน้อย 8 บิตโดยมาตรฐาน
เพราะฉันรู้สึกว่ามันเรียกจริง ๆ ฉันแค่ต้องการระบุกฎของ C และ C ++ (พวกเขาเหมือนกันในเรื่องนี้) ก่อนอื่นบิตทั้งหมดของการunsigned char
มีส่วนร่วมในการกำหนดค่าถ้าวัตถุถ่านใด ๆ ที่ไม่ได้ลงนาม ประการที่สองunsigned char
มีการระบุไว้อย่างชัดเจนไม่ได้ลงนาม
ตอนนี้ผมมีการพูดคุยกับคนที่เกี่ยวกับสิ่งที่เกิดขึ้นเมื่อคุณแปลงค่าเป็น-1
ชนิด int unsigned char
ไป เขาปฏิเสธความคิดที่ว่าผลลัพธ์ที่unsigned char
ได้มีการตั้งค่าบิตทั้งหมดเป็น 1 เพราะเขากังวลเกี่ยวกับการแสดงสัญลักษณ์ แต่เขาไม่ต้องทำ ทันทีที่ติดตามกฏนี้แล้วการแปลงจะทำตามที่ตั้งใจไว้:
หากชนิดใหม่ไม่ได้ลงนามค่าจะถูกแปลงโดยการเพิ่มหรือลบซ้ำ ๆ มากกว่าค่าสูงสุดที่สามารถแสดงในรูปแบบใหม่จนกว่าค่านั้นจะอยู่ในช่วงของประเภทใหม่ (
6.3.1.3p2
ในร่าง C99)
นั่นคือคำอธิบายทางคณิตศาสตร์ C ++ อธิบายในแง่ของแคลคูลัสโมดูโลซึ่งให้ผลเป็นกฎเดียวกัน อย่างไรก็ตามสิ่งที่ไม่รับประกันคือบิตทั้งหมดในจำนวนเต็ม-1
เป็นหนึ่งก่อนการแปลง ดังนั้นสิ่งที่เรามีเพื่อให้เราสามารถอ้างว่าผลที่unsigned char
ได้มีCHAR_BIT
บิตทั้งหมดกลายเป็น 1?
UCHAR_MAX+1
เพื่อ-1
จะให้ค่าในช่วงคือUCHAR_MAX
พอจริงแล้ว! ดังนั้นเมื่อใดก็ตามที่คุณอยากได้unsigned char
บิตทั้งหมด
unsigned char c = (unsigned char)-1;
นอกจากนี้ยังติดตามว่าการแปลงไม่เพียง แต่ตัดทอนบิตคำสั่งซื้อที่สูงขึ้น เหตุการณ์ที่โชคดีสำหรับคอมพลีเมนต์ของทั้งสองคือมันเป็นแค่การตัดทอนที่นั่น แต่เหตุการณ์นั้นไม่จำเป็นต้องเป็นจริงสำหรับการแสดงสัญลักษณ์อื่น ๆ
UCHAR_MAX
?
(unsigned type)-1
สำนวนบางชนิด ~0
ไม่ใช่
int x = 1234
char *y = &x
แทน binary ของมี1234
00000000 00000000 00000100 11010010
เครื่องของฉันเป็น endian น้อยดังนั้นมันจึงกลับด้านและเก็บในหน่วยความจำ11010010 00000100 00000000 00000000
LSB มาก่อน ตอนนี้ส่วนหลัก printf("%d" , *p)
ถ้าฉันใช้ printf
จะอ่านไบต์แรก11010010
เพียงออกเป็น-46
แต่11010010
เป็นดังนั้นทำไมไม่ได้พิมพ์210
-46
ฉันสับสนจริงๆฉันเดาว่าการส่งเสริมการขายให้เป็นจำนวนเต็มกำลังทำอะไรบางอย่าง แต่ฉันไม่รู้
ตัวอย่างการใช้งานถ่านที่ไม่ได้ลงชื่อ :
unsigned char
มักใช้ในคอมพิวเตอร์กราฟิกซึ่งบ่อยครั้งมาก (แต่ไม่เสมอไป) กำหนดไบต์เดียวให้กับแต่ละองค์ประกอบสี เป็นเรื่องปกติที่จะเห็นสี RGB (หรือ RGBA) แสดงเป็นบิต 24 (หรือ 32) แต่ละunsigned char
อัน เนื่องจากunsigned char
ค่าอยู่ในช่วง [0,255] ค่าจึงถูกตีความโดยทั่วไปว่า:
ดังนั้นคุณจะจบลงด้วยสีแดง RGB เป็น (255,0,0) -> (สีแดง 100%, สีเขียว 0%, สีฟ้า 0%)
ทำไมไม่ใช้signed char
? เลขคณิตและการเลื่อนบิตกลายเป็นปัญหา ตามที่อธิบายไว้แล้วsigned char
ช่วงของจะถูกเปลี่ยนเป็น -128 วิธีที่ง่ายและไร้เดียงสา (ส่วนใหญ่ไม่ได้ใช้) สำหรับการแปลง RGB เป็นโทนสีเทาคือการหาค่าเฉลี่ยขององค์ประกอบสีทั้งสาม แต่สิ่งนี้จะเกิดปัญหาเมื่อค่าของส่วนประกอบสีเป็นค่าลบ สีแดง (255, 0, 0) ค่าเฉลี่ยเป็น (85, 85, 85) เมื่อใช้unsigned char
เลขคณิต อย่างไรก็ตามหากค่าเป็นsigned char
s (127, -128, -128) เราจะต้องลงท้ายด้วย (-99, -99, -99) ซึ่งจะเป็น (29, 29, 29) ในunsigned char
พื้นที่ของเราซึ่งไม่ถูกต้อง .
หากคุณต้องการใช้ตัวอักษรเป็นจำนวนเต็มขนาดเล็กวิธีที่ปลอดภัยที่สุดในการใช้มันคือกับint8_t
และuint8_t
ประเภท
int8_t
และuint8_t
เป็นทางเลือกและไม่ได้กำหนดไว้ในสถาปัตยกรรมที่ขนาดไบต์ไม่เท่ากับ 8 บิต ในทางกลับกันsigned char
และunsigned char
พร้อมใช้งานเสมอและรับประกันว่าจะถืออย่างน้อย 8 บิต มันอาจจะเป็นเรื่องธรรมดาทาง แต่ไม่ปลอดภัยที่สุด
signed char
และunsigned char
? หรือคุณจะแนะนำทางเลือก "ปลอดภัย" ที่ดีขึ้นในกรณีนั้นหรือไม่? ตัวอย่างเช่นติดกับจำนวนเต็ม "จริง" signed int
และunsigned int
แทนด้วยเหตุผลบางอย่าง?
signed char
และunsigned char
เป็นแบบพกพาสำหรับการใช้งานที่สอดคล้องและจะประหยัดพื้นที่เก็บข้อมูล แต่อาจทำให้ขนาดรหัสเพิ่มขึ้น ในบางกรณีเราจะประหยัดพื้นที่เก็บข้อมูลได้มากขึ้นด้วยการจัดเก็บค่าเล็ก ๆ ในบิตฟิลด์หรือบิตจำนวนเต็มชนิดปกติ ไม่มีคำตอบที่แน่นอนสำหรับคำถามนี้ความเกี่ยวข้องของวิธีการนี้ขึ้นอยู่กับกรณีเฉพาะในมือ และคำตอบนี้ไม่ได้ตอบคำถามต่อไป
unsigned char
ใช้ค่าบวกเท่านั้นเช่น0ถึง255
อยู่ที่ไหน
signed char
ใช้ทั้งค่าบวกและค่าลบ .... เช่น-128ถึง+127
char
และunsigned char
ไม่รับประกันว่าจะเป็น 8 บิตในทุกแพลตฟอร์ม - รับประกันว่าจะเป็น 8 บิตหรือมากกว่า บางแพลตฟอร์มมีไบต์ 9 บิต 32 บิตหรือ 64 บิต อย่างไรก็ตามแพลตฟอร์มที่พบบ่อยที่สุดในวันนี้ (Windows, Mac, Linux x86 ฯลฯ ) มีขนาด 8 บิต
signed char
มีช่วง -128 ถึง 127 unsigned char
มีช่วง 0 ถึง 255
char
จะเทียบเท่ากับ char ที่ลงนามแล้วหรือ char ที่ไม่ได้ลงชื่อขึ้นอยู่กับคอมไพเลอร์ แต่เป็นประเภทที่แตกต่างกัน
หากคุณกำลังใช้สาย char
C-สไตล์การใช้งานเพียงแค่ หากคุณจำเป็นต้องใช้ตัวอักษรสำหรับเลขคณิต (หายากมาก) ให้ระบุการลงชื่อหรือไม่ได้ลงนามอย่างชัดเจนสำหรับการพกพา
An unsigned char
เป็นค่าไบต์ที่ไม่ได้ลงนาม (0 ถึง 255) คุณอาจจะคิดchar
ในแง่ของการเป็น "ตัวละคร" แต่มันเป็นค่าตัวเลขจริงๆ ปกติchar
จะมีการลงชื่อดังนั้นคุณจึงมีค่า 128 ค่าและค่าเหล่านี้จะจับคู่กับอักขระโดยใช้การเข้ารหัส ASCII แต่ในทั้งสองกรณีสิ่งที่คุณเก็บไว้ในหน่วยความจำคือค่าไบต์
ในแง่ของค่าโดยตรงถ่านปกติจะใช้เมื่อค่าเป็นที่รู้จักกันระหว่างCHAR_MIN
และCHAR_MAX
ในขณะที่ถ่านไม่ได้ลงนามให้ช่วงสองครั้งที่ปลายบวก ตัวอย่างเช่นถ้าCHAR_BIT
เป็น 8 ช่วงปกติchar
จะรับประกันได้เพียง [0, 127] (เพราะสามารถลงชื่อหรือไม่ได้ลงชื่อ) ในขณะที่unsigned char
จะเป็น [0, 255] และsigned char
จะเป็น [-127, 127]
ในแง่ของสิ่งที่มันใช้มาตรฐานอนุญาตให้วัตถุของ POD (ข้อมูลเก่าธรรมดา) ถูกแปลงโดยตรงไปยังอาร์เรย์ของอักขระที่ไม่ได้ลงชื่อ สิ่งนี้ช่วยให้คุณตรวจสอบการเป็นตัวแทนและรูปแบบบิตของวัตถุ การรับประกันแบบปลอดภัยแบบคนเดียวไม่มีอยู่สำหรับถ่านหรือถ่านที่ลงชื่อแล้ว
unsigned char
ไม่ใช่อาร์เรย์โดยเฉพาะและการใด ๆ "แปลง" เป็นเพียงการกำหนดอย่างเป็นทางการโดยการคัดลอกจากวัตถุที่จริงการประกาศอาร์เรย์ของunsigned char
และจากนั้นตรวจสอบหลัง ยังไม่ชัดเจนว่า OR สามารถตีความอีกครั้งโดยตรงเช่นอาร์เรย์ด้วยค่าเผื่อสำหรับตัวชี้ทางคณิตศาสตร์ที่มันจะนำมาซึ่งเช่นว่า "ลำดับ" ==
"อาร์เรย์" ในการใช้งานนี้ มีประเด็นหลัก # 1701 ที่เปิดขึ้นโดยหวังว่าจะได้รับการชี้แจงนี้ โชคดีที่ความคลุมเครือนี้ทำให้ฉันยุ่งมากเมื่อเร็ว ๆ นี้
unsigned char
หรือจากนั้นดำเนินการ++ptr
จากที่นั่นเพื่ออ่านทุก ๆ ไบต์ของมัน ... แต่ AFAICT ไม่ได้กำหนดว่าเป็นการอนุญาตโดยเฉพาะดังนั้นเราจึง เหลือไว้เพื่ออนุมานว่ามัน'อาจจะตกลง'จากข้อความอื่น ๆ อีกมากมาย (และในหลาย ๆ วิธีการดำรงอยู่เพียงmemcpy
) ในมาตรฐานคล้ายกับตัวต่อจิ๊กซอว์ ซึ่งไม่เหมาะ บางทีถ้อยคำจะพัฒนาขึ้นในที่สุด นี่คือปัญหา CWG ที่ฉันพูดถึง แต่ไม่มีที่ว่างในการลิงก์ - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
unsigned char
เป็นหัวใจของกลอุบายทั้งหมด ในคอมไพเลอร์เกือบทั้งหมดสำหรับแพลตฟอร์มทั้งหมดunsigned char
เป็นเพียงไบต์และจำนวนเต็ม (ปกติ) 8 บิตที่ไม่ได้ลงนามซึ่งสามารถถือว่าเป็นจำนวนเต็มขนาดเล็กหรือแพ็คบิต
ในการเสพติดตามที่คนอื่นพูดมาตรฐานไม่ได้กำหนดสัญลักษณ์ของตัวละคร เพื่อให้คุณมี 3 ที่แตกต่างกันประเภท:char
, ,char
signed char
unsigned char
หากคุณต้องการใช้ประเภทต่างๆของระยะเวลาที่เฉพาะเจาะจงและ signedness คุณอาจดีกว่าด้วยuint8_t
, int8_t
, uint16_t
ฯลฯ เพียงเพราะพวกเขาทำสิ่งที่พวกเขากล่าวว่า
googling บางคนพบสิ่งนี้ซึ่งผู้คนมีการอภิปรายเกี่ยวกับเรื่องนี้
ถ่านที่ไม่ได้ลงนามนั้นเป็นไบต์เดียว ดังนั้นคุณจะใช้สิ่งนี้หากคุณต้องการข้อมูลหนึ่งไบต์ (ตัวอย่างเช่นคุณอาจต้องการใช้เพื่อตั้งค่าสถานะเป็นเปิดและปิดเพื่อส่งผ่านไปยังฟังก์ชั่นซึ่งมักจะทำใน Windows API)
ถ่านที่ไม่ได้ลงชื่อใช้บิตที่สงวนไว้สำหรับเครื่องหมายของถ่านปกติเป็นหมายเลขอื่น สิ่งนี้จะเปลี่ยนช่วงเป็น [0 - 255] ซึ่งตรงข้ามกับ [-128 - 127]
โดยทั่วไปจะใช้ตัวอักษรที่ไม่ได้ลงชื่อเมื่อคุณไม่ต้องการลงชื่อ สิ่งนี้จะสร้างความแตกต่างเมื่อทำสิ่งต่าง ๆ เช่นการเลื่อนบิต (shift ขยายเครื่องหมาย) และสิ่งอื่น ๆ เมื่อจัดการกับ char เป็นไบต์แทนที่จะใช้มันเป็นตัวเลข
unsigned char
ใช้ค่าบวกเท่านั้น: 0 ถึง 255 ในขณะที่
signed char
รับค่าบวกและลบ: -128 ถึง +127
ยกมาจากหนังสือ "the the c laugage programming":
รอบคัดเลือกsigned
หรือunsigned
อาจนำไปใช้กับถ่านหรือจำนวนเต็มใด ๆ ตัวเลขที่ไม่ได้ลงชื่อจะเป็นค่าบวกหรือศูนย์เสมอและปฏิบัติตามกฎของเลขคณิตโมดูโล 2 ^ n โดยที่ n คือจำนวนบิตในประเภท ตัวอย่างเช่นหากตัวอักษรเป็น 8 บิตตัวแปรถ่านที่ไม่ได้ลงนามมีค่าระหว่าง 0 และ 255 ในขณะที่ตัวอักษรที่ลงนามมีค่าระหว่าง -128 ถึง 127 (ในเครื่องประกอบสอง) ไม่ว่าจะเป็นตัวอักษรธรรมดา - อิสระ แต่อักขระที่พิมพ์ได้จะเป็นค่าบวกเสมอ
signed char
และunsigned char
ทั้งคู่เป็นตัวแทน 1byte แต่มีช่วงที่แตกต่างกัน
Type | range
-------------------------------
signed char | -128 to +127
unsigned char | 0 to 255
ในsigned char
กรณีที่เราพิจารณาว่าchar letter = 'A'
'A' เป็นตัวแทนของเลขฐานสองของ 65 ในASCII/Unicode
หาก 65 สามารถเก็บไว้ได้ -65 ก็สามารถจัดเก็บได้ ไม่มีค่าไบนารีติดลบอยู่ในASCII/Unicode
นั้นโดยไม่จำเป็นต้องกังวลเกี่ยวกับค่าลบ
ตัวอย่าง
#include <stdio.h>
int main()
{
signed char char1 = 255;
signed char char2 = -128;
unsigned char char3 = 255;
unsigned char char4 = -128;
printf("Signed char(255) : %d\n",char1);
printf("Unsigned char(255) : %d\n",char3);
printf("\nSigned char(-128) : %d\n",char2);
printf("Unsigned char(-128) : %d\n",char4);
return 0;
}
เอาท์พุท -:
Signed char(255) : -1
Unsigned char(255) : 255
Signed char(-128) : -128
Unsigned char(-128) : 128