uint8_t ne peut pas être imprimé avec cout

146

J'ai un problème étrange à propos du travail avec des entiers en C ++.

J'ai écrit un programme simple qui définit une valeur sur une variable puis l'imprime, mais il ne fonctionne pas comme prévu.

Mon programme ne comporte que deux lignes de code:

uint8_t aa = 5;

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

La sortie de ce programme est value is

Ie, il imprime en blanc pour aa.

Quand je change uint8_tau uint16_tcode ci - dessus fonctionne comme un charme.

J'utilise Ubuntu 12.04 (Precise Pangolin), 64 bits, et ma version de compilateur est:

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

Réponses:

151

Il n'imprime pas vraiment un blanc, mais très probablement le caractère ASCII avec la valeur 5, qui est non imprimable (ou invisible). Il existe un certain nombre de codes de caractères ASCII invisibles , la plupart inférieurs à la valeur 32, qui est en fait le blanc.

Vous devez convertir aaen unsigned intpour afficher la valeur numérique, car ostream& operator<<(ostream&, unsigned char)essaie de générer la valeur du caractère visible.

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;
πάντα ῥεῖ
la source
24
Puisque les castes de style C sont mal vues, ne serait-il pas préférable de faire un static_cast?
Tim Seguine
37
Il devrait être converti en int. Un casting est une façon de faire cela, mais pas la seule. +aafonctionne également.
Pete Becker
5
int (var) et (int) var ne sont-ils pas réellement la même chose?
paulm
9
Voir la question liée en utilisant type (var) est la même que (type) var c'est la même chose que le cast C - essayez-le avec const etc, il le supprime!
paulm
13
La réponse "Non, les casts de style c sont déconseillés pour C ++ pour un certain nombre de raisons." "est-ce que int (var) et (int) var ne sont pas réellement la même chose? Sûr donne l'impression que vous ne vous en êtes pas rendu compte int(var)et que vous (int)varavez exactement la même signification. int(var)est déconseillé exactement dans les cas où (int)varest, pour exactement les mêmes raisons, car cela signifie exactement la même chose. (Je peux comprendre pourquoi vous iriez ici de toute façon, donc je ne dis pas que vous devez utiliser static_cast. Je pense juste que la piste des commentaires ici a été un peu déroutante inutilement.)
46

uint8_tsera probablement un typedefpour unsigned char. La ostreamclasse a une surcharge spéciale pour unsigned char, c'est-à-dire qu'elle imprime le caractère avec le numéro 5, qui n'est pas imprimable, d'où l'espace vide.

arne
la source
14
Je souhaite que le standard traite vraiment std :: uint8_t comme un type séparé et pas seulement comme un typedef friggin. Il n'y a aucune raison valable d'appliquer la sémantique des caractères à ces types lorsqu'ils sont utilisés avec des objets de flux.
antred le
37

L'ajout d'un opérateur unaire + avant la variable de tout type de données primitif donnera une valeur numérique imprimable au lieu du caractère ASCII (dans le cas de type char).

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl;
SridharKritha
la source
C'est bien, mais pourquoi le C ++ ne traite-t-il pas uint8_tcomme unsigned charquelles seraient des valeurs numériques?
R1S8K
@ R1S8K c'est parce que while uint8_test juste un type def de unsigned char, unsigned charlui-même est géré par ostreamlike charet imprime sa valeur ASCII.
Rude
@Harsh Merci mec! c'est donc un type unsigned charqui explique beaucoup de choses. Donc, le seul entier est int, non?
R1S8K
@ R1S8K Eh bien, le plus petit type entier serait celui short intqui prend 2 octets. Il existe également quelques autres variantes du type entier.
Rude
@Harsh Aussi, je pense que lors de la programmation et de la déclaration de variables, peu importe si je déclare une variable qui ne traiterait que de petits nombres ne dépassant pas 250 avec une grande taille de registre comme longou intparce que le compilateur optimiserait l'utilisation de la RAM ou du flash d'après ce qui remplit ce registre, ai-je raison?
R1S8K
16

C'est parce que l'opérateur de sortie traite uint8_tcomme a char( uint8_test généralement juste un alias pour unsigned char), donc il imprime le caractère avec le code ASCII (qui est le système de codage de caractères le plus courant) 5.

Voir par exemple cette référence .

Un mec programmeur
la source
Pourquoi? le compilateur C le traite comme un nombre. Je pense que C ++ est différent à ce stade.
R1S8K
@PerchEagle Si vous lisez la référence liée, vous verrez que l'opérateur est surchargé pour les deux caractères signedet unsigned(au-delà de plain charqui en C ++ est en réalité un troisième type distinct). Donc, si uint8_test un alias pour unsigned char(très probablement), c'est ce qui sera utilisé.
Un mec programmeur
Pourriez-vous vérifier ma réponse sur ce fil et me dire si ma réponse est correcte ou non? stackoverflow.com/questions/15585267/… , ma réponse est avant la dernière. Merci beaucoup.
R1S8K
14
  • Utilisation d' ADL (recherche de nom dépendante de l'argument):

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

    production:

    *
    42
  • Un manipulateur de flux personnalisé serait également possible.

  • L'opérateur unaire plus est également un idiome ( cout << +i << endl).
pepper_chico
la source
8
KISS n'est-il pas encore un paradigme valable ??
πάντα ῥεῖ
1
@ πάνταῥεῖ bien sûr, mais n'oubliez pas: tout doit être rendu aussi simple que possible, mais pas plus simple
pepper_chico
4
@ πάνταῥεῖ ok, continuez à faire des tonnes de casts de style c en code c ++ alors, tout le monde est libre d'être productif tel qu'il est, dans l'environnement qui lui convient le mieux.
pepper_chico
5
@ πάνταῥεῖ sérieusement? Le moulage de style fonctionnel est également un moulage de style C. changer l'un de l'autre n'aide en rien à quitter le royaume de C, vérifiez: stackoverflow.com/a/4775807/1000282 . pete-becker a également commenté cela sur votre réponse, mais vous semblez avoir manqué son dernier commentaire.
pepper_chico
3
Cette solution est très élégante et efficace, car elle fonctionne avec des modèles. En fait, c'est la seule solution que j'ai repérée qui fonctionne pour moi. Une mise en garde cependant, la première fonction a un bogue, car 'os' est lié à un seul type, et par conséquent, la valeur signée ou non signée sera envoyée à la mauvaise version de l'opérateur << (). Le correctif est assez simple:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Georges
7

couttraite aacomme une charvaleur ASCII 5qui est un caractère non imprimable, essayez le typage intavant d'imprimer.

Ne t'inquiète pas enfant
la source
4

La operator<<()surcharge entre istreamet charest une fonction non membre. Vous pouvez explicitement utiliser la fonction membre pour traiter a char(ou a uint8_t) comme 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;
}

Production:

value is 5
R Sahu
la source
2

Comme d'autres l'ont déjà dit, le problème se produit car le flux standard traite les caractères signés et non signés comme des caractères uniques et non comme des nombres.

Voici ma solution avec des changements de code minimes:

uint8_t aa = 5;

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

L'ajout "+0"est sécurisé avec n'importe quel nombre, y compris la virgule flottante.

Pour les types entiers, le type de résultat sera remplacé par intif sizeof(aa) < sizeof(int). Et cela ne changera pas de type si sizeof(aa) >= sizeof(int).

Cette solution est également bonne pour se préparer int8_tà être imprimé en streaming alors que certaines autres solutions ne sont pas si bonnes:

int8_t aa = -120;

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

Production:

value is -120
bad value is 4294967176

PS La solution avec ADL donnée par pepper_chico et πάντα ῥεῖ est vraiment belle.

Sergey
la source