uint8_t ไม่สามารถพิมพ์ด้วย cout ได้


146

ฉันมีปัญหาแปลก ๆ เกี่ยวกับการทำงานกับจำนวนเต็มใน C ++

ฉันเขียนโปรแกรมอย่างง่ายที่กำหนดค่าให้กับตัวแปรแล้วพิมพ์มัน แต่มันไม่ทำงานอย่างที่คาดไว้

โปรแกรมของฉันมีโค้ดสองบรรทัดเท่านั้น:

uint8_t aa = 5;

cout << "value is " << aa << endl;

ผลลัพธ์ของโปรแกรมนี้คือ value is

คือมันพิมพ์เปล่าaa

เมื่อฉันเปลี่ยนuint8_tไปuint16_tโค้ดข้างต้นทำงานเช่นเสน่ห์

ฉันใช้ Ubuntu 12.04 (แม่นยำ Pangolin), 64- บิตและเวอร์ชั่นคอมไพเลอร์ของฉันคือ:

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

2
ข้อมูลซ้ำที่เป็นไปได้ของพฤติกรรม uint8_t iostream
phuclv

คำตอบ:


151

มันไม่ได้พิมพ์เปล่า แต่จริงๆอาจเป็นอักขระ ASCII ที่มีค่า 5 ซึ่งไม่สามารถพิมพ์ได้ (หรือมองไม่เห็น) มีรหัสอักขระ ASCII ที่มองไม่เห็นจำนวนหนึ่งซึ่งส่วนใหญ่ต่ำกว่าค่า 32 ซึ่งเป็นช่องว่างจริง

คุณต้องแปลงaaเป็นunsigned intเอาท์พุทค่าตัวเลขเนื่องจากostream& operator<<(ostream&, unsigned char)พยายามที่จะส่งออกค่าตัวอักษรที่มองเห็นได้

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;

24
เนื่องจากสไตล์ C ถูกปลดเปลื้องไปแล้วมันจะดีกว่าไหมถ้าจะทำ static_cast?
Tim Seguine

37
มันควรจะแปลงintไป นักแสดงเป็นวิธีหนึ่งในการทำเช่นนั้น แต่ไม่ใช่วิธีเดียว +aaยังใช้งานได้
Pete Becker

5
ไม่ใช่ int (var) และ (int) var จริงๆแล้วเป็นสิ่งเดียวกันหรือไม่
paulm

9
ดูคำถามที่เชื่อมโยงโดยใช้ประเภท (var) เหมือนกับ (ประเภท) var เหมือนกันกับตัวละคร C - ลองใช้กับ const เป็นต้นมันจะลบออก!
paulm

13
การตอบสนอง "ไม่ได้ปลดเปลื้อง c-style หมดกำลังใจสำหรับ c ++ ด้วยเหตุผลหลายประการ" ถึง "ไม่ int (var) และ (int) var จริง ๆ กันหรือไม่" แน่นอนทำให้ดูเหมือนว่าคุณไม่ได้ตระหนักint(var)และ(int)varมีความหมายเหมือนกัน int(var)มีท้อแท้ในกรณีเหล่านั้นอยู่ที่ไหน(int)varด้วยเหตุผลเดียวกันเพราะมันหมายถึงสิ่งเดียวกัน (ฉันสามารถเข้าใจว่าทำไมคุณถึงไปที่นี่ต่อไปดังนั้นฉันไม่ได้บอกว่าคุณจำเป็นต้องใช้static_castฉันแค่คิดว่าเส้นทางความคิดเห็นที่นี่ทำให้สับสนเล็กน้อยโดยไม่จำเป็น)

46

uint8_tส่วนใหญ่มีแนวโน้มจะเป็นสำหรับtypedef ชั้นมีเกินพิเศษเช่นจะพิมพ์ตัวอักษรที่มีหมายเลข 5 ซึ่งเป็นที่ไม่สามารถพิมพ์เพราะฉะนั้นพื้นที่ว่างเปล่าunsigned charostreamunsigned char


14
ฉันหวังว่ามาตรฐานจะถือว่า std :: uint8_t เป็นแบบแยกและไม่ใช่ typedef ของ friggin ไม่มีเหตุผลที่เหมาะสมในการใช้อักขระความหมายกับประเภทเหล่านี้เมื่อใช้ร่วมกับวัตถุสตรีม
antred

37

การเพิ่มตัวดำเนินการ unary + ก่อนตัวแปรของชนิดข้อมูลดั้งเดิมใด ๆ จะให้ค่าตัวเลขที่พิมพ์ได้แทนอักขระ ASCII (ในกรณีที่เป็นชนิดถ่าน)

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl;

ดี แต่ทำไม c ++ ไม่ปฏิบัติuint8_tเช่นunsigned charนั้นซึ่งจะเป็นค่าตัวเลข
R1S8K

@ R1S8K นี้เป็นเพราะในขณะที่ยังuint8_tเป็นเพียงชนิดของ def unsigned char, unsigned charตัวเองจะถูกจัดการโดยostreamเหมือนcharและพิมพ์ค่า ASCII ของมัน
Harsh

@ ขอบคุณชายที่รุนแรง! มันเป็น def ประเภทunsigned charที่อธิบายได้มากมาย ดังนั้นจำนวนเต็มเท่านั้นintใช่ไหม
R1S8K

@ R1S8K เอ่อชนิดจำนวนเต็มขนาดเล็กที่สุดนั้นshort intใช้เวลา 2 ไบต์ มีรูปแบบจำนวนเต็มอีกสองสามรูปแบบเช่นกัน
รุนแรง

@ รุนแรงนอกจากนี้ฉันคิดว่าเมื่อการเขียนโปรแกรมและประกาศตัวแปรมันไม่สำคัญว่าถ้าฉันประกาศตัวแปรที่จะจัดการเฉพาะกับตัวเลขขนาดเล็กไม่เกิน 250 เช่นขนาดลงทะเบียนขนาดใหญ่เช่นlongหรือintเพราะคอมไพเลอร์จะเพิ่มประสิทธิภาพการใช้ RAM หรือแฟลช ฉันกรอกถูกต้องหรือไม่?
R1S8K

16

มันเป็นเพราะถือว่าผู้ประกอบการส่งออกuint8_tเช่นchar( uint8_tมักจะเป็นเพียงนามแฝงสำหรับunsigned char) ดังนั้นจะพิมพ์ตัวอักษรที่มีรหัส ASCII 5(ซึ่งเป็นตัวละครที่พบมากที่สุดระบบการเข้ารหัส)

ดูเช่นการอ้างอิงนี้


ทำไม? คอมไพเลอร์ C ถือว่าเป็นตัวเลข ฉันคิดว่า C ++ แตกต่างกันในตอนนี้
R1S8K

@PerchEagle หากคุณอ่านข้อมูลอ้างอิงที่เชื่อมโยงคุณจะเห็นว่าตัวดำเนินการโอเวอร์โหลดสำหรับทั้งตัวละครsignedและunsignedตัวละคร (เกินธรรมดาcharซึ่งในภาษา C ++ เป็นประเภทที่สามแยกกันจริงๆ) ดังนั้นหากuint8_tเป็นนามแฝงสำหรับunsigned char(เป็นไปได้มาก) นั่นคือสิ่งที่จะใช้
โปรแกรมเมอร์บางคนเพื่อน

คุณสามารถตรวจสอบคำตอบของฉันในหัวข้อนี้และบอกฉันว่าคำตอบของฉันถูกหรือไม่? stackoverflow.com/questions/15585267/…คำตอบของฉันคือก่อนคำตอบสุดท้าย ขอบคุณมาก.
R1S8K

14
  • การใช้ประโยชน์จากADL (การค้นหาชื่อขึ้นอยู่กับอาร์กิวเมนต์):

    #include <cstdint>
    #include <iostream>
    #include <typeinfo>
    
    namespace numerical_chars {
    inline std::ostream &operator<<(std::ostream &os, char c) {
        return std::is_signed<char>::value ? os << static_cast<int>(c)
                                           : os << static_cast<unsigned int>(c);
    }
    
    inline std::ostream &operator<<(std::ostream &os, signed char c) {
        return os << static_cast<int>(c);
    }
    
    inline std::ostream &operator<<(std::ostream &os, unsigned char c) {
        return os << static_cast<unsigned int>(c);
    }
    }
    
    int main() {
        using namespace std;
    
        uint8_t i = 42;
    
        {
            cout << i << endl;
        }
    
        {
            using namespace numerical_chars;
            cout << i << endl;
        }
    }

    เอาท์พุท:

    *
    42
  • เครื่องมือปรับแต่งสตรีมแบบกำหนดเองก็สามารถทำได้เช่นกัน

  • ตัวดำเนินการ unary plus เป็นสำนวนที่เรียบร้อยเช่นกัน ( cout << +i << endl)

8
ไม่ได้KISSยังคงเป็นกระบวนทัศน์ที่ถูกต้อง ??
ῥεῖαῥεῖ


4
@ ῥεῖαῥεῖโอเคทำ c สไตล์มากมายในโค้ด c ++ จากนั้นทุกคนมีอิสระที่จะสร้างสรรค์ในแบบที่เป็นอยู่ในสภาพแวดล้อมที่เขา / เธอเหมาะสมที่สุด
pepper_chico

5
@ πάνταῥεῖอย่างจริงจัง? นักแสดงสไตล์การทำงานยังเป็นเพียงการหล่อสไตล์ค การเปลี่ยนแปลงอย่างใดอย่างหนึ่งจากที่อื่น ๆ ไม่ได้ช่วยอะไรในการออกดินแดนของ C ตรวจสอบ: stackoverflow.com/a/4775807/1000282 pete-becker แสดงความคิดเห็นในคำตอบของคุณเช่นกัน แต่คุณดูเหมือนจะไม่ได้รับความคิดเห็นล่าสุดของเขา
pepper_chico

3
โซลูชันนี้สง่างามและมีประสิทธิภาพเพราะทำงานกับเทมเพลต ในความเป็นจริงนั่นเป็นทางออกเดียวที่ฉันเห็นว่าเหมาะกับฉัน แม้ว่าข้อแม้หนึ่งฟังก์ชันแรกมีข้อผิดพลาดเนื่องจาก 'os' ถูกผูกไว้กับชนิดเดียวดังนั้นทั้งค่าที่ลงชื่อและค่าที่ไม่ได้ลงชื่อจะถูกส่งไปยังตัวดำเนินการรุ่นที่ไม่ถูกต้อง << () การแก้ไขนั้นง่ายพอ:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
จอร์ชส


4

operator<<()เกินระหว่างistreamและcharเป็นฟังก์ชั่นที่ไม่ใช่สมาชิก คุณอย่างชัดเจนสามารถใช้ฟังก์ชั่นสมาชิกในการรักษาchar(หรือuint8_t) intในฐานะที่เป็น

#include <iostream>
#include <cstddef>

int main()
{
   uint8_t aa=5;

   std::cout << "value is ";
   std::cout.operator<<(aa);
   std::cout << std::endl;

   return 0;
}

เอาท์พุท:

value is 5

2

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

นี่คือโซลูชันของฉันที่มีการเปลี่ยนแปลงรหัสน้อยที่สุด:

uint8_t aa = 5;

cout << "value is " << aa + 0 << endl;

การเพิ่ม"+0"จะปลอดภัยกับตัวเลขใด ๆ รวมถึงจุดลอย

สำหรับประเภทจำนวนเต็มมันจะเปลี่ยนชนิดของผลไปถ้าint และมันจะไม่เปลี่ยนประเภทถ้าsizeof(aa) < sizeof(int)sizeof(aa) >= sizeof(int)

วิธีแก้ปัญหานี้ยังดีสำหรับการเตรียมint8_tที่จะพิมพ์เพื่อสตรีมในขณะที่โซลูชันอื่นไม่ดี:

int8_t aa = -120;

cout << "value is " << aa + 0 << endl;
cout << "bad value is " << unsigned(aa) << endl;

เอาท์พุท:

value is -120
bad value is 4294967176

PS Solution กับ ADL ที่ได้รับจาก pepper_chico และπάνταῥεῖนั้นสวยงามจริงๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.