uint8_t no se puede imprimir con cout

146

Tengo un problema extraño sobre trabajar con enteros en C ++.

Escribí un programa simple que establece un valor en una variable y luego lo imprime, pero no funciona como se esperaba.

Mi programa solo tiene dos líneas de código:

uint8_t aa = 5;

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

La salida de este programa es value is

Es decir, se imprime en blanco para aa.

Cuando cambio uint8_tal uint16_tcódigo anterior funciona como un encanto.

Yo uso Ubuntu 12.04 (Precise Pangolin), 64 bits, y mi versión del compilador es:

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

Respuestas:

151

Realmente no imprime un espacio en blanco, pero probablemente el carácter ASCII con valor 5, que no es imprimible (o invisible). Hay una serie de códigos de caracteres ASCII invisibles , la mayoría de ellos por debajo del valor 32, que en realidad es el espacio en blanco.

Debe convertir aaa unsigned intpara generar el valor numérico, ya que ostream& operator<<(ostream&, unsigned char)intenta generar el valor del carácter visible.

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;
πάντα ῥεῖ
fuente
24
Dado que los lanzamientos de estilo C están mal vistos, ¿no sería mejor hacer un static_cast?
Tim Seguine
37
Debería convertirse a int. Un reparto es una forma de hacerlo, pero no la única. +aaTambién funciona.
Pete Becker
55
¿no son int (var) y (int) var realmente lo mismo?
Paul
9
Ver la pregunta vinculada usando type (var) es lo mismo que (type) var es lo mismo que el C cast - pruébalo con const, etc., ¡lo elimina!
Paul
13
La respuesta "No. Los lanzamientos de estilo c se desaconsejan para c ++ por varias razones". a "¿no es int (var) y (int) var realmente lo mismo?" seguro hace que parezca que no te diste cuenta int(var)y (int)vartienes exactamente el mismo significado. int(var)se desaconseja exactamente en aquellos casos en que (int)vares, por exactamente las mismas razones, porque significa exactamente lo mismo. (Puedo entender por qué te gustaría ir a por ello aquí de todos modos, aunque, por lo que no estoy diciendo que necesita para su uso static_castsólo creo el rastro comentario aquí tiene un poco innecesariamente confuso..)
46

uint8_tprobablemente será un typedefpara unsigned char. La ostreamclase tiene una sobrecarga especial para unsigned char, es decir, imprime el carácter con el número 5, que no es imprimible, de ahí el espacio vacío.

arne
fuente
14
Deseo que el estándar realmente trate std :: uint8_t como un tipo separado y no solo como un maldito typedef. No hay una razón sensata para aplicar la semántica de caracteres a estos tipos cuando se usa junto con objetos de flujo.
antred
37

Agregar un operador unario + antes de la variable de cualquier tipo de datos primitivo dará un valor numérico imprimible en lugar del carácter ASCII (en el caso del tipo char).

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl;
SridharKritha
fuente
Eso es bueno, pero ¿por qué c ++ no trata uint8_tcomo unsigned charcuáles serían valores numéricos?
R1S8K
@ R1S8K esto se debe a que si bien uint8_tes solo un tipo def unsigned char, unsigned charse maneja de la ostreammisma manera chare imprime su valor ASCII.
Duro
@ Harsh Gracias hombre! así que es un tipo de definición unsigned charque explica mucho. Entonces el único número entero es int, ¿verdad?
R1S8K
@ R1S8K Bueno, el tipo entero más pequeño sería el short intque ocupa 2 bytes. También hay algunas otras variaciones del tipo entero.
Duro
@Harsh También creo que al programar y declarar variables, no importa si declaro una variable que se ocupe solo de números pequeños que no excedan de 250 con un tamaño de registro grande como longo intporque el compilador optimizaría el uso de RAM o flash de acuerdo con lo que llena ese registro, ¿estoy en lo cierto?
R1S8K
16

Esto se debe a que el operador de salida trata el a uint8_tcomo char( uint8_tgeneralmente es solo un alias unsigned char), por lo que imprime el carácter con el código ASCII (que es el sistema de codificación de caracteres más común) 5.

Ver, por ejemplo, esta referencia .

Algún tipo programador
fuente
¿Por qué? el compilador de C lo trata como un número. Creo que C ++ es diferente en este punto.
R1S8K
@PerchEagle Si lee la referencia vinculada, verá que el operador está sobrecargado para ambos signedy los unsignedcaracteres (más allá de lo simple, charque en C ++ es realmente un tercer tipo separado). Entonces, si uint8_tes un alias para unsigned char(muy probable), eso es lo que se usará.
Algún tipo programador
¿Podría verificar mi respuesta en este hilo y decirme si mi respuesta es correcta o no? stackoverflow.com/questions/15585267/… , mi respuesta es antes de la última. Muchas gracias.
R1S8K
14
  • Haciendo uso de ADL (búsqueda de nombre dependiente del 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;
        }
    }

    salida:

    *
    42
  • También sería posible un manipulador de flujo personalizado.

  • El operador unario plus también es un buen lenguaje ( cout << +i << endl).
pepper_chico
fuente
8
¿No es KISS todavía un paradigma válido?
πάντα ῥεῖ
1
@ πάνταῥεῖ, por supuesto, pero no lo olvides: todo debe hacerse lo más simple posible, pero no más simple
pepper_chico
44
@ πάνταῥεῖ ok, sigue haciendo toneladas de moldes de estilo c en código c ++, entonces, cualquiera es libre de ser productivo de la manera que es, en el entorno que más le convenga.
pepper_chico
55
@ πάνταῥεῖ en serio? El reparto de estilo funcional, también, es simplemente el reparto de estilo C. cambiar uno del otro no ayuda en nada al abandonar el reino de C, verifique: stackoverflow.com/a/4775807/1000282 . pete-becker también comentó esto en tu respuesta, pero parece que te perdiste su último comentario.
pepper_chico
3
Esta solución es muy elegante y efectiva, ya que funciona con plantillas. De hecho, esa es la única solución que he visto que funciona para mí. Sin embargo, una advertencia, la primera función tiene un error, porque 'os' está vinculado a un solo tipo y, por lo tanto, el valor con signo o sin signo se enviará a la versión incorrecta del operador << (). La solución es bastante simple:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Georges
7

coutse trata aacomo un charvalor ASCII 5que es un carácter no imprimible, intente la conversión de texto intantes de imprimir.

No te preocupes niño
fuente
4

los operator<<() sobrecarga entre istreamy chares una función no miembro. Puede usar explícitamente la función miembro para tratar a char(o a uint8_t) como un 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;
}

Salida:

value is 5
R Sahu
fuente
2

Como otros dijeron antes, el problema ocurre porque el flujo estándar trata los caracteres con signo y los caracteres sin signo como caracteres únicos y no como números.

Aquí está mi solución con cambios mínimos de código:

uint8_t aa = 5;

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

Agregando "+0" es seguro con cualquier número, incluido el punto flotante.

Para los tipos enteros, cambiará el tipo de resultado a intif sizeof(aa) < sizeof(int). Y no cambiará el tipo sisizeof(aa) >= sizeof(int) .

Esta solución también es buena para prepararse int8_tpara imprimirse en streaming, mientras que otras soluciones no son tan buenas:

int8_t aa = -120;

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

Salida:

value is -120
bad value is 4294967176

La solución PS con ADL dada por pepper_chico y πάντα ῥεῖ es realmente hermosa.

Sergey
fuente