ใน C/C++ an unsigned charใช้ทำอะไร? ต่างจากปกติcharอย่างไร ?

ตอบ

ใน C ++ มีอักขระที่แตกต่างกันสามประเภท:

  • char
  • signed char
  • unsigned char

หากคุณกำลังใช้ประเภทอักขระสำหรับtextให้ใช้ unqualified char:

  • เป็นประเภทของตัวอักษรเช่น'a'หรือ'0'(ใน C ++ เท่านั้นในประเภท C คือint)
  • เป็นประเภทที่ประกอบเป็นสตริง C เช่น "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มี

สิ่งนี้ขึ้นอยู่กับการใช้งานเนื่องจากมาตรฐาน C ไม่ได้กำหนดความมีลายเซ็นของchar. ขึ้นอยู่กับแพลตฟอร์ม char อาจเป็นsignedor unsignedดังนั้นคุณต้องขออย่างชัดเจนsigned charหรือunsigned charว่าการใช้งานของคุณขึ้นอยู่กับมันหรือไม่ เพียงใช้charหากคุณต้องการแสดงอักขระจากสตริง เนื่องจากสิ่งนี้จะตรงกับสิ่งที่แพลตฟอร์มของคุณใส่ในสตริง

ความแตกต่างระหว่างsigned charและunsigned charเป็นไปตามที่คุณคาดหวัง บนแพลตฟอร์มส่วนใหญ่signed charจะเป็นหมายเลขเสริมของ 8 บิต 2 ตั้งแต่-128ถึง127และunsigned charจะเป็นจำนวนเต็ม 8 บิตที่ไม่ได้ลงนาม ( 0ถึง255) โปรดทราบว่ามาตรฐานไม่ต้องการให้charประเภทมี 8 บิต มีเพียงsizeof(char)return 1เท่านั้น คุณสามารถรับจำนวนบิตในตัวอักษรด้วยCHAR_BITในlimits.h. มีน้อยถ้าแพลตฟอร์มใด ๆ ในวันที่นี้จะเป็นอย่างอื่นที่ไม่ใช่8แม้ว่า

มีบทสรุปที่ดีของปัญหานี้คือที่นี่

ตามที่คนอื่น ๆ พูดถึงตั้งแต่ฉันโพสต์นี้ คุณควรใช้int8_tและuint8_tถ้าคุณต้องการแทนจำนวนเต็มขนาดเล็ก

เพราะฉันรู้สึกว่ามันถูกเรียกร้องจริงๆ ฉันแค่ต้องการระบุกฎบางอย่างของ C และ C++ (ในเรื่องนี้เหมือนกัน) ครั้งแรกที่ทุกบิตของunsigned charการมีส่วนร่วมในการกำหนดค่าถ้าวัตถุใด ๆ ที่ไม่ได้ลงชื่อถ่าน ประการที่สองunsigned charระบุอย่างชัดเจนว่าไม่ได้ลงนาม

ตอนนี้ ฉันได้พูดคุยกับใครบางคนเกี่ยวกับสิ่งที่เกิดขึ้นเมื่อคุณแปลงค่า-1ของ type เป็นunsigned char. เขาปฏิเสธความคิดที่ว่าผลลัพธ์ที่unsigned charได้มีการตั้งค่าบิตทั้งหมดเป็น 1 เพราะเขากังวลเกี่ยวกับการแสดงสัญลักษณ์ แต่เขาไม่จำเป็นต้องเป็น เป็นไปตามกฎนี้ทันทีที่การแปลงทำในสิ่งที่ตั้งใจไว้:

If the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type. (6.3.1.3p2 in a C99 draft)

นั่นคือคำอธิบายทางคณิตศาสตร์ C ++ อธิบายในแง่ของแคลคูลัสแบบโมดูโลซึ่งให้ผลเป็นกฎเดียวกัน อย่างไรก็ตาม สิ่งที่ไม่รับประกันคือบิตทั้งหมดในจำนวนเต็ม-1เป็นหนึ่งเดียวก่อนการแปลง แล้วเรามีอะไรกัน จึงสามารถอ้างได้ว่าผลลัพธ์ที่unsigned charได้มีCHAR_BITบิตทั้งหมดกลายเป็น 1?

  1. บิตทั้งหมดมีส่วนร่วมในการกำหนดมูลค่า - นั่นคือไม่มีช่องว่างภายในเกิดขึ้นในวัตถุ
  2. เพิ่มเพียงครั้งเดียวUCHAR_MAX+1เพื่อ-1ให้ได้ค่าในช่วงคือUCHAR_MAX

พอแล้วจริงๆ! ดังนั้นเมื่อใดก็ตามที่คุณต้องการมีunsigned charบิตทั้งหมด คุณทำ

unsigned char c = (unsigned char)-1;

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

ตัวอย่างการใช้งานของunsigned char :

unsigned charมักใช้ในคอมพิวเตอร์กราฟิก ซึ่งบ่อยครั้งมาก (แต่ไม่เสมอไป) กำหนดไบต์เดียวให้กับแต่ละองค์ประกอบสี เป็นเรื่องปกติที่จะเห็นสี RGB (หรือ RGBA) แสดงเป็น 24 (หรือ 32) บิต โดยแต่ละสีเป็นunsigned char. เนื่องจากunsigned charค่าอยู่ในช่วง [0,255] โดยทั่วไปค่าจะถูกตีความดังนี้:

  • 0 หมายถึงขาดองค์ประกอบสีที่กำหนดทั้งหมด
  • 255 หมายถึง 100% ของเม็ดสีที่กำหนด

ดังนั้นคุณจะจบลงด้วย RGB สีแดงเป็น (255,0,0) -> (100% สีแดง 0% สีเขียว 0% สีฟ้า)

ทำไมไม่ใช้signed char? เลขคณิตและการขยับบิตกลายเป็นปัญหา ตามที่อธิบายไว้แล้วsigned charช่วงของ a ถูกเปลี่ยนโดยหลักเป็น -128 วิธีการที่เรียบง่ายและไร้เดียงสา (ส่วนใหญ่ไม่ได้ใช้) ในการแปลง RGB เป็นระดับสีเทาคือค่าเฉลี่ยองค์ประกอบสีทั้งสาม แต่สิ่งนี้จะเกิดปัญหาเมื่อค่าขององค์ประกอบสีเป็นค่าลบ สีแดง (255, 0, 0) ค่าเฉลี่ยถึง (85, 85, 85) เมื่อใช้unsigned charเลขคณิต อย่างไรก็ตาม หากค่าเป็นsigned chars (127,-128,-128) เราจะลงท้ายด้วย (-99, -99, -99) ซึ่งจะเป็น (29, 29, 29) unsigned charซึ่งไม่ถูกต้อง .

unsigned charใช้ค่าบวกเท่านั้น....เช่น0ถึง255

ในทางตรงกันข้าม

signed charรับทั้งค่าบวกและค่าลบ....ชอบ-128ถึง+127

signed charมีช่วง -128 ถึง 127; unsigned charมีช่วง 0 ถึง 255

char จะเทียบเท่ากับถ่านที่ลงนามหรือถ่านที่ไม่ได้ลงนาม ทั้งนี้ขึ้นอยู่กับคอมไพเลอร์ แต่เป็นประเภทที่แตกต่างกัน

หากคุณกำลังใช้สตริงสไตล์ C เพียงใช้char. หากคุณต้องการใช้อักขระสำหรับเลขคณิต (ค่อนข้างหายาก) ให้ระบุการลงชื่อหรือไม่ได้ลงนามอย่างชัดเจนเพื่อการพกพา

charและunsigned charไม่รับประกันว่าจะเป็นประเภท 8 บิตบนทุกแพลตฟอร์ม—รับประกันว่าจะเป็น 8 บิตหรือใหญ่กว่า บางแพลตฟอร์มมีไบต์ 9 บิต 32 บิตหรือ 64 บิต อย่างไรก็ตาม แพลตฟอร์มทั่วไปส่วนใหญ่ในปัจจุบัน (Windows, Mac, Linux x86 เป็นต้น) มีไบต์แบบ 8 บิต

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 (ข้อมูลเก่าธรรมดา) เป็นอาร์เรย์ของถ่านที่ไม่ได้ลงนามโดยตรง ซึ่งช่วยให้คุณสามารถตรวจสอบการแสดงและรูปแบบบิตของวัตถุได้ ไม่มีการรับประกันแบบเดียวกันสำหรับการเล่นแบบปลอดภัยสำหรับถ่านหรือถ่านที่มีลายเซ็น

ถ้าคุณชอบใช้ความยาวและการเซ็นชื่อเฉพาะประเภทต่างๆ คุณอาจจะดีกว่าด้วยuint8_t, int8_t, uint16_t, ฯลฯ เพียงเพราะพวกเขาทำตามที่พวกเขาพูด

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

นอกจากนี้ อย่างที่คนอื่นบอก มาตรฐานไม่ได้กำหนดสัญลักษณ์ของถ่าน ดังนั้นคุณจึงมี 3 charประเภทที่แตกต่างกัน: char, signed char, unsigned char.

unsigned charใช้ค่าบวกเท่านั้น: 0 ถึง 255 ในขณะที่ signed charใช้ค่าบวกและค่าลบ: -128 ถึง +127

googling บางคนพบสิ่งนี้ซึ่งผู้คนมีการอภิปรายเกี่ยวกับเรื่องนี้

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

ถ่านที่ไม่ได้ลงนามใช้บิตที่สงวนไว้สำหรับเครื่องหมายของถ่านปกติเป็นตัวเลขอื่น สิ่งนี้จะเปลี่ยนช่วงเป็น [0 - 255] ตรงข้ามกับ [-128 - 127]

โดยทั่วไปแล้ว อักขระที่ไม่ได้ลงนามจะใช้เมื่อคุณไม่ต้องการเครื่องหมาย สิ่งนี้จะสร้างความแตกต่างเมื่อทำสิ่งต่าง ๆ เช่น shifts bit (shift extends sign) และสิ่งอื่น ๆ เมื่อจัดการกับ char เป็น byte แทนที่จะใช้เป็นตัวเลข

อ้างจากหนังสือ "the c programming laugage":

ตัวระบุsignedหรือunsignedอาจใช้กับถ่านหรือจำนวนเต็มใดๆ ตัวเลขที่ไม่ได้ลงนามจะเป็นค่าบวกหรือศูนย์เสมอ และปฏิบัติตามกฎของโมดูโลเลขคณิต 2^n โดยที่ n คือจำนวนบิตในประเภท ตัวอย่างเช่น หากอักขระเป็น 8 บิต ตัวแปร char ที่ไม่ได้ลงนามจะมีค่าระหว่าง 0 ถึง 255 ในขณะที่อักขระที่ลงชื่อมีค่าระหว่าง -128 ถึง 127 (ในเครื่องเสริมสองเครื่อง) ไม่ว่าอักขระธรรมดาจะมีการลงชื่อหรือไม่ลงชื่อก็ตามคือเครื่อง - ขึ้นอยู่กับ แต่อักขระที่พิมพ์ได้นั้นเป็นค่าบวกเสมอ

signed charและunsigned charทั้งสองเป็นตัวแทนของ 1 ไบต์ แต่มีช่วงต่างกัน

   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