uint8_t não pode ser impresso com cout

146

Eu tenho um problema estranho sobre como trabalhar com números inteiros em C ++.

Eu escrevi um programa simples que define um valor para uma variável e depois o imprime, mas não está funcionando conforme o esperado.

Meu programa tem apenas duas linhas de código:

uint8_t aa = 5;

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

A saída deste programa é value is

Ou seja, ele imprime em branco para aa.

Quando mudo uint8_tpara uint16_to código acima, funciona como um encanto.

Eu uso o Ubuntu 12.04 (Precise Pangolin), 64 bits, e minha versão do compilador é:

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

Respostas:

151

Realmente não imprime um espaço em branco, mas provavelmente o caractere ASCII com o valor 5, que não é imprimível (ou invisível). Há vários códigos de caracteres ASCII invisíveis , a maioria deles abaixo do valor 32, que é o espaço em branco.

Você precisa converter aapara unsigned intpara gerar o valor numérico, pois ostream& operator<<(ostream&, unsigned char)tenta gerar o valor do caractere visível.

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;
πάντα ῥεῖ
fonte
24
Como as conversões no estilo C são desaprovadas, não seria melhor fazer um static_cast?
Tim Seguine 24/10
37
Deve ser convertido para int. Um elenco é uma maneira de fazer isso, mas não a única. +aatambém funciona.
Pete Becker
5
int (var) e (int) var não são a mesma coisa?
paulm 17/02/2014
9
Veja a pergunta vinculada usando type (var) é o mesmo que (type) var é o mesmo que o elenco C - experimente com const etc, remove-o!
paulm 17/02/2014
13
A resposta "Não. As conversões no estilo c são desencorajadas para c ++ por vários motivos". para "int (var) e (int) var não são a mesma coisa?" com certeza faz parecer que você não percebeu int(var)e (int)vartem exatamente o mesmo significado. int(var)é desencorajado exatamente nos casos em que (int)varé, exatamente pelas mesmas razões, porque significa exatamente a mesma coisa. (Eu posso entender por que você iria para ele aqui de qualquer maneira, embora, então eu não estou dizendo que você precisa para uso static_castEu só acho que a trilha comentário aqui tem um pouco desnecessariamente confuso..)
46

uint8_tprovavelmente será um typedefpara unsigned char. A ostreamclasse possui uma sobrecarga especial para unsigned char, ou seja, imprime o caractere com o número 5, que não é imprimível, daí o espaço vazio.

arne
fonte
14
Desejo que o padrão realmente trate std :: uint8_t como um tipo separado e não apenas um typedef friggin '. Não há razão sadia para aplicar semântica de caracteres a esses tipos quando usados ​​em conjunto com objetos de fluxo.
antred 31/07/2015
37

A adição de um operador + unário antes da variável de qualquer tipo de dados primitivo fornecerá valor numérico imprimível em vez de caractere ASCII (no caso do tipo char).

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl;
SridharKritha
fonte
Isso é legal, mas por que o c ++ não trata uint8_tcomo unsigned charvalores numéricos?
R1S8K 25/05/19
@ R1S8K é porque, embora uint8_tseja apenas um tipo de definição unsigned char, unsigned charele é tratado ostreamexatamente como chare imprime seu valor ASCII.
Harsh
@Harsh Thanks man! então é uma definição do tipo unsigned charque explica muito. Então, o único número inteiro é int, certo?
R1S8K
@ R1S8K Bem, o menor tipo inteiro seria o short intque ocupa 2 bytes. Existem algumas outras variações do tipo inteiro também.
Harsh
@Harsh Também acho que, ao programar e declarar variáveis, não importa se declaro uma variável que lidaria apenas com pequenos números não superiores a 250 com um tamanho de registro grande como longou intporque o compilador otimizaria o uso de RAM ou flash de acordo com o que preenche esse registro, estou certo?
R1S8K
16

Isso ocorre porque o operador de saída trata uint8_tcomo a char( uint8_tgeralmente é apenas um alias para unsigned char), por isso imprime o caractere com o código ASCII (que é o sistema de codificação de caracteres mais comum) 5.

Veja, por exemplo, esta referência .

Algum cara programador
fonte
Por quê? o compilador C trata-o como um número. Eu acho que C ++ é diferente neste momento.
R1S8K 25/05/19
@PerchEagle Se você ler a referência vinculada, verá que o operador está sobrecarregado para caracteres signede dois unsigned(além da planície, charque em C ++ é realmente um terceiro tipo separado). Então, se uint8_té um alias para unsigned char(muito provável), é isso que será usado.
Algum programador
Você poderia verificar minha resposta neste tópico e me dizer se minha resposta está correta ou não? stackoverflow.com/questions/15585267/… , minha resposta é anterior à última. Muito obrigado.
R1S8K
14
  • Fazendo uso da ADL (pesquisa de nome dependente de argumento):

    #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;
        }
    }
    

    resultado:

    *
    42
    
  • Um manipulador de fluxo personalizado também seria possível.

  • O operador unary plus também é um idioma puro ( cout << +i << endl).
pepper_chico
fonte
8
O KISS ainda não é um paradigma válido?
πάντα ῥεῖ
1
@ πάνταῥεῖ é claro, mas não se esqueça: Tudo deve ser feito o mais simples possível, mas não mais simples
pepper_chico
4
@ ok, continue fazendo toneladas de lançamentos de estilo c em código c ++, então qualquer um é livre para ser produtivo do jeito que é, no ambiente em que ele / ela se encaixa melhor.
pepper_chico
5
@ πάνταῥεῖ sério? elenco de estilo funcional, também, é apenas elenco de estilo c. alterar um do outro não ajuda em nada a deixar o domínio de C, verifique: stackoverflow.com/a/4775807/1000282 . pete-becker também comentou sua resposta, mas parece que você perdeu o último comentário.
pepper_chico
3
Esta solução é muito elegante e eficaz, porque funciona com modelos. De fato, essa é a única solução que vi que funciona para mim. Uma ressalva, no entanto, a primeira função tem um bug, porque 'os' está vinculado a um único tipo e, portanto, o valor assinado ou o não assinado serão enviados para a versão errada do operador << (). A correção é simples:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Georges
7

coutestá tratando aacom o charvalor ASCII, 5que é um caractere não imprimível, tente converter para intantes da impressão.

Não se preocupe criança
fonte
4

A operator<<()sobrecarga entre istreame charé uma função não membro. Você pode usar explicitamente a função de membro para tratar um char(ou a uint8_t) como um 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;
}

Resultado:

value is 5
R Sahu
fonte
2

Como outros disseram antes, o problema ocorre porque o fluxo padrão trata char assinado e char não assinado como caracteres únicos e não como números.

Aqui está minha solução com alterações mínimas de código:

uint8_t aa = 5;

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

A adição "+0"é segura com qualquer número, incluindo ponto flutuante.

Para tipos inteiros, o tipo de resultado será alterado para intif sizeof(aa) < sizeof(int). E isso não mudará de tipo se sizeof(aa) >= sizeof(int).

Essa solução também é boa para preparar int8_ta impressão para transmitir, enquanto outras soluções não são tão boas:

int8_t aa = -120;

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

Resultado:

value is -120
bad value is 4294967176

A solução PS com ADL dada por pepper_chico e πάντα ῥεῖ é realmente bonita.

Sergey
fonte