uint8_t kann nicht mit cout gedruckt werden

146

Ich habe ein seltsames Problem mit der Arbeit mit ganzen Zahlen in C ++.

Ich habe ein einfaches Programm geschrieben, das einen Wert auf eine Variable setzt und diese dann druckt, aber es funktioniert nicht wie erwartet.

Mein Programm hat nur zwei Codezeilen:

uint8_t aa = 5;

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

Die Ausgabe dieses Programms ist value is

Dh es druckt leer für aa.

Wenn ich uint8_tzum uint16_tobigen Code wechsle , funktioniert das wie ein Zauber.

Ich verwende Ubuntu 12.04 (Precise Pangolin), 64-Bit, und meine Compiler-Version lautet:

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
CoderInNetwork
quelle
2
Mögliches Duplikat des Verhaltens
phuclv
cout
druckt

Antworten:

150

Es wird nicht wirklich ein Leerzeichen gedruckt, aber höchstwahrscheinlich das ASCII-Zeichen mit dem Wert 5, das nicht druckbar (oder unsichtbar) ist. Es gibt eine Reihe von unsichtbaren ASCII-Zeichencodes , die meisten davon unter dem Wert 32, der eigentlich leer ist.

Sie müssen in konvertieren aa, unsigned intum den numerischen Wert auszugeben, da ostream& operator<<(ostream&, unsigned char)versucht wird, den sichtbaren Zeichenwert auszugeben.

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;
πάντα ῥεῖ
quelle
24
Wäre es nicht besser, einen static_cast zu erstellen, da Casts im C-Stil verpönt sind?
Tim Seguine
37
Man sollte umgewandelt zu int. Eine Besetzung ist eine Möglichkeit, aber nicht die einzige. +aafunktioniert auch.
Pete Becker
5
Ist int (var) und (int) var nicht dasselbe?
Paulm
9
Sehen Sie, dass die verknüpfte Frage mit type (var) mit (type) var mit dem C-Cast identisch ist - probieren Sie sie mit const usw. aus, sie wird entfernt!
Paulm
13
Die Antwort "Nein. Casts im C-Stil werden aus mehreren Gründen von C ++ abgeraten." zu "Ist int (var) und (int) var nicht dasselbe?" sicher lässt es scheinen, als ob Sie nicht realisiert haben int(var)und (int)vargenau die gleiche Bedeutung haben. int(var)wird in genau den Fällen entmutigt, in denen dies (int)varaus genau den gleichen Gründen der Fall ist, weil es genau das Gleiche bedeutet. (Ich kann verstehen, warum Sie es hier sowieso versuchen würden, also sage ich nicht, dass Sie es verwenden müssen static_cast. Ich denke nur, dass der Kommentarpfad hier ein bisschen unnötig verwirrend geworden ist.)
46

uint8_twird höchstwahrscheinlich ein typedeffür sein unsigned char. Die ostreamKlasse hat eine spezielle Überladung für unsigned char, dh sie druckt das Zeichen mit der Nummer 5, die nicht druckbar ist, daher der leere Raum.

Arne
quelle
14
Ich wünschte, der Standard würde std :: uint8_t wirklich als separaten Typ behandeln und nicht nur als verdammtes typedef. Es gibt keinen vernünftigen Grund, Zeichensemantik auf diese Typen anzuwenden, wenn sie in Verbindung mit Stream-Objekten verwendet werden.
antred
36

Wenn Sie vor der Variablen eines primitiven Datentyps einen unären + -Operator hinzufügen, erhalten Sie einen druckbaren numerischen Wert anstelle eines ASCII-Zeichens (im Fall eines Zeichentyps).

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl;
SridharKritha
quelle
Das ist schön, aber warum nicht C ++ behandelt uint8_tals unsigned chardie numerische Werte sein würde?
R1S8K
@ R1S8K Dies liegt daran, dass zwar uint8_tnur ein Typ def von ist unsigned char, aber unsigned charselbst von ostreamlike behandelt wird charund seinen ASCII-Wert druckt.
Harter
@Harsh Danke Mann! Es ist also eine Art Def unsigned char, die viel erklärt. Die einzige ganze Zahl ist also int, richtig?
R1S8K
@ R1S8K Nun, der kleinste Integer-Typ würde short int2 Bytes belegen . Es gibt auch einige andere Variationen des Integer-Typs.
Hart
@Harsh Auch ich denke beim Programmieren und Deklarieren von Variablen ist es egal, ob ich eine Variable deklariere, die nur mit kleinen Zahlen von nicht mehr als 250 mit einer großen Registergröße wie longoder intweil der Compiler die Verwendung von RAM oder Flash optimieren würde Habe ich recht, was füllt dieses Register?
R1S8K
16

Dies liegt daran, dass der Ausgabeoperator das Gleiche uint8_twie a behandelt char( uint8_tnormalerweise nur ein Alias ​​für unsigned char), sodass das Zeichen mit dem ASCII-Code (dem am häufigsten verwendeten Zeichencodierungssystem) gedruckt wird 5.

Siehe zB diese Referenz .

Ein Programmierer
quelle
Warum? Der C-Compiler behandelt es als Zahl. Ich denke, C ++ ist an dieser Stelle anders.
R1S8K
@PerchEagle Wenn Sie die verknüpfte Referenz lesen, werden Sie feststellen, dass der Operator sowohl für signedals auch für unsignedZeichen überladen ist (jenseits der Ebene, chardie in C ++ tatsächlich ein dritter separater Typ ist). Wenn uint8_talso ein Alias ​​für unsigned char(sehr wahrscheinlich) ist, wird dieser verwendet.
Einige Programmierer Typ
Könntest du meine Antwort in diesem Thread überprüfen und mir sagen, ob meine Antwort richtig ist oder nicht? stackoverflow.com/questions/15585267/… , meine Antwort ist vor der letzten. Ich danke dir sehr.
R1S8K
14
  • Verwendung von ADL (Argument-abhängige Namenssuche):

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

    Ausgabe:

    *
    42
  • Ein benutzerdefinierter Stream-Manipulator wäre ebenfalls möglich.

  • Der unäre Plus-Operator ist auch eine nette Redewendung ( cout << +i << endl).
Pfeffer_chico
quelle
8
Ist KISS nicht immer noch ein gültiges Paradigma?
πάντα ῥεῖ
1
@ πάνταῥεῖ natürlich, aber nicht vergessen: Alles sollte so einfach wie möglich gemacht werden, aber nicht einfacher
pepper_chico
4
@ πάνταῥεῖ ok, mach weiter Unmengen von C-Casts in C ++ - Code, dann kann jeder so produktiv sein, wie er ist, in der Umgebung, in die er / sie am besten passt.
Pepper_chico
5
@ πάνταῥεῖ ernst? Die Besetzung im funktionalen Stil ist ebenfalls nur im Casting im C-Stil. Das Ändern von einem zum anderen hilft beim Verlassen des Bereichs von C nicht weiter. Überprüfen Sie Folgendes : stackoverflow.com/a/4775807/1000282 . pete-becker hat dies auch zu deiner antwort kommentiert, aber du scheinst seinen letzten kommentar verpasst zu haben.
Pepper_chico
3
Diese Lösung ist sehr elegant und effektiv, da sie mit Vorlagen funktioniert. In der Tat ist dies die einzige Lösung, die ich entdeckt habe und die für mich funktioniert. Eine Einschränkung ist jedoch, dass die erste Funktion einen Fehler aufweist, da 'os' an einen einzelnen Typ gebunden ist und daher entweder der vorzeichenbehaftete oder der vorzeichenlose Wert an die falsche Version des Operators << () gesendet wird. Die Lösung ist einfach genug:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Georges
7

coutbehandelt aaals charASCII - Wert 5typecasting , auf die ein nicht druckbare Zeichen, versuchen intvor dem Druck.

Mach dir keine Sorgen Kind
quelle
4

Die operator<<()Überlastung zwischen istreamund charist eine Nichtmitgliedsfunktion. Sie können die Elementfunktion explizit verwenden, um a char(oder a uint8_t) als zu behandeln 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;
}

Ausgabe:

value is 5
R Sahu
quelle
2

Wie bereits erwähnt, tritt das Problem auf, weil der Standard-Stream vorzeichenbehaftete Zeichen und vorzeichenlose Zeichen als einzelne Zeichen und nicht als Zahlen behandelt.

Hier ist meine Lösung mit minimalen Codeänderungen:

uint8_t aa = 5;

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

Das Hinzufügen "+0"ist mit jeder Zahl einschließlich Gleitkomma sicher.

Bei ganzzahligen Typen wird der Ergebnistyp in intif geändert sizeof(aa) < sizeof(int). Und es wird den Typ nicht ändern, wenn sizeof(aa) >= sizeof(int).

Diese Lösung eignet sich auch gut für die Vorbereitung int8_tdes Drucks zum Streamen, während einige andere Lösungen nicht so gut sind:

int8_t aa = -120;

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

Ausgabe:

value is -120
bad value is 4294967176

PS-Lösung mit ADL von pepper_chico und πάντα ῥεῖ ist wirklich schön.

Sergey
quelle