cint로 uint8_t를 인쇄 할 수 없습니다


146

C ++에서 정수로 작업하는 데 이상한 문제가 있습니다.

값을 변수로 설정하고 인쇄하는 간단한 프로그램을 작성했지만 예상대로 작동하지 않습니다.

내 프로그램에는 두 줄의 코드 만 있습니다.

uint8_t aa = 5;

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

이 프로그램의 출력은 value is

즉,에 공백으로 인쇄됩니다 aa.

내가 변경하는 경우 uint8_tuint16_t위의 코드를 마치 마법처럼 작동합니다.

64 비트 Ubuntu 12.04 (Precise Pangolin)를 사용하며 컴파일러 버전은 다음과 같습니다.

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


답변:


151

실제로 공백을 인쇄하지는 않지만 값이 5 인 ASCII 문자는 인쇄 할 수 없거나 보이지 않습니다. 보이지 않는 ASCII 문자 코드 가 여러 개 있는데 , 대부분이 실제로는 공백 인 값 32 미만입니다.

보이는 문자 값을 출력하려고하기 때문에 숫자 값을 출력하려면 로 변환 aa해야 합니다.unsigned intostream& 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
type (var)을 사용하여 연결된 질문을 참조하십시오. (type) var 은 C 캐스트 와 동일 합니다. const 등으로 시도해보십시오. 제거하십시오!
paulm

13
"아니오. c 스타일 캐스트는 여러 가지 이유로 c ++에 권장되지 않습니다." "int (var)와 (int) var가 실제로 같은 것이 아닌가?" 당신이 인식하지 않은 것처럼 보일 수 int(var)(int)var정확히 같은 의미를 갖는다. int(var)바로 이러한 경우에 권장하지 않습니다 (int)var, 정확히 같은 이유입니다 때문에 정확히 같은 일을 의미한다. (어쨌든 왜 당신이 여기에 갈 것인지 이해할 수 있습니다. 그래서 나는 당신이 사용해야한다고 말하는 것이 아닙니다 static_cast. 나는 여기에 주석 트레일이 불필요하게 혼란스럽게 생각합니다.)

46

uint8_ttypedef대한 가능성이 높습니다 unsigned char. 이 ostream클래스에는에 대한 특수 과부하가 있습니다 unsigned char. 즉 인쇄 할 수없는 숫자 5로 문자를 인쇄하므로 빈 공간이됩니다.


14
표준이 실제로 std :: uint8_t를 friggin 'typedef가 아닌 별도의 유형으로 처리하기를 바랍니다. 스트림 객체와 함께 사용될 때 이러한 유형에 문자 의미를 적용해야 할 이유는 없습니다.
antred

37

기본 데이터 유형의 변수 앞에 단항 + 연산자를 추가하면 ASCII 문자 (문자 유형의 경우) 대신 인쇄 가능한 숫자 값이 제공됩니다.

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

멋지지만 왜 C ++ 이 숫자 값 uint8_t으로 취급하지 unsigned char않습니까?
R1S8K

반면 때문에 R1S8K @입니다 uint8_t데프 단지 유형 unsigned char, unsigned char자체에 의해 처리됩니다 ostream단지처럼 char그 ASCII 값을 인쇄합니다.
거친

@Harsh 감사합니다! 그래서 그것은 unsigned char많은 유형의 설명입니다. 그래서 유일한 정수는 int맞습니까?
R1S8K

@ R1S8K 가장 작은 정수 유형은 short int2 바이트를 차지합니다. 정수 유형의 다른 변형도 있습니다.
가혹한

@Harsh 또한 변수를 프로그래밍하고 선언 할 때 큰 레지스터 크기가 250 이상을 초과하지 않는 작은 숫자만을 처리 long하거나 int컴파일러가 RAM 또는 플래시의 사용을 최적화하기 때문에 변수를 선언하는 것은 중요하지 않습니다. 그 레지스터를 채우는 내용에 따르면, 맞습니까?
R1S8K

16

출력 연산자는 ( 일반적으로에 대한 별칭 일뿐) uint8_t처럼 취급 하므로 ASCII 코드 (가장 일반적인 문자 인코딩 시스템)로 문자를 인쇄합니다 .charuint8_tunsigned char5

예를 들어이 참조를 참조하십시오 .


왜? C 컴파일러는 이것을 숫자로 취급합니다. 이 시점에서 C ++이 다르다고 생각합니다.
R1S8K

@PerchEagle 연결된 참조를 읽으면 연산자가 문자 signedunsigned문자 모두에 대해 오버로드 된 것을 볼 수 있습니다 ( charC ++에서 실제로 세 번째로 분리 된 형식 이외의 일반 ). 따라서 (아마도) 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
  • 커스텀 스트림 매니퓰레이터도 가능합니다.

  • 단항 더하기 연산자는 깔끔한 관용구입니다 ( cout << +i << endl).

8
KISS가 여전히 유효한 패러다임 이 아닙니까 ?
πάντα ῥεῖ


4
@ πάνταῥεῖ 좋아, C ++ 코드로 많은 c 스타일 캐스트를 계속 수행하면 누구나 자신이 가장 적합한 환경에서 생산성을 높일 수 있습니다.
pepper_chico

5
@ πάνταῥεῖ 진심으로? 기능적 스타일 캐스트도 c 스타일 캐스팅입니다. 하나를 다른 것으로 변경해도 C 영역을 떠나는 데 도움이되지 않으면 stackoverflow.com/a/4775807/1000282를 확인하십시오 . pete-becker도 귀하의 답변에 이것을 언급했지만 귀하는 그의 마지막 의견을 놓친 것 같습니다.
pepper_chico

3
이 솔루션은 템플릿과 함께 작동하기 때문에 매우 우아하고 효과적입니다. 사실, 그것이 내가 찾은 유일한 해결책입니다. 하나의 경고는 첫 번째 함수에 버그가 있습니다. 'os'는 단일 유형에 바인딩되어 있기 때문에 부호있는 값 또는 부호없는 값이 잘못된 버전의 operator << ()로 전송됩니다. 수정은 간단합니다 :return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Georges


4

operator<<()사이 과부하 istreamchar비 멤버 함수이다. 멤버 함수를 명시 적으로 사용하여 char(또는 a 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경우 결과 유형을 if로 변경 합니다 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

pepper_chico와 πάντα ῥεῖ의 ADL을 사용한 PS 솔루션은 정말 아름답습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.