ใน C / C ++ มีการunsigned charใช้เพื่ออะไร มันแตกต่างจากปกติcharอย่างไร?
ใน C / C ++ มีการunsigned charใช้เพื่ออะไร มันแตกต่างจากปกติcharอย่างไร?
คำตอบ:
ใน C ++ มีอักขระสามประเภทที่แตกต่างกัน:
charsigned charunsigned 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 00000000LSB มาก่อน ตอนนี้ส่วนหลัก 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 chars (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 ที่ไม่ได้ลงชื่อขึ้นอยู่กับคอมไพเลอร์ แต่เป็นประเภทที่แตกต่างกัน
หากคุณกำลังใช้สาย charC-สไตล์การใช้งานเพียงแค่ หากคุณจำเป็นต้องใช้ตัวอักษรสำหรับเลขคณิต (หายากมาก) ให้ระบุการลงชื่อหรือไม่ได้ลงนามอย่างชัดเจนสำหรับการพกพา
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 , ,charsigned charunsigned 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