uint8_t nie można wydrukować za pomocą cout

146

Mam dziwny problem z pracą z liczbami całkowitymi w C ++.

Napisałem prosty program, który ustawia wartość zmiennej, a następnie ją drukuje, ale nie działa zgodnie z oczekiwaniami.

Mój program ma tylko dwie linie kodu:

uint8_t aa = 5;

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

Wynik tego programu to value is

To znaczy, drukuje puste dla aa.

Kiedy zmieni uint8_tsię uint16_tpowyższy kod działa jak czar.

Używam Ubuntu 12.04 (Precise Pangolin), 64-bitowego, a moja wersja kompilatora to:

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
CoderInNetwork
źródło

Odpowiedzi:

151

Tak naprawdę nie wypisuje spacji, ale najprawdopodobniej znak ASCII o wartości 5, który jest niedrukowalny (lub niewidoczny). Istnieje wiele niewidocznych kodów znaków ASCII , większość z nich poniżej wartości 32, która w rzeczywistości jest pusta.

Musisz przekonwertować aana, unsigned intaby wyświetlić wartość liczbową, ponieważ ostream& operator<<(ostream&, unsigned char)próbuje wyświetlić widoczną wartość znaku.

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;
πάντα ῥεῖ
źródło
24
Ponieważ rzuty w stylu C są źle widziane, czy nie byłoby lepiej wykonać static_cast?
Tim Seguine
37
Powinien zostać przekonwertowany na int. Obsada to jeden ze sposobów, ale nie jedyny. +aarównież działa.
Pete Becker
5
czy int (var) i (int) var nie jest właściwie tym samym?
paulm
9
Zobacz połączone pytanie z użyciem type (var) jest tym samym, co (type) var, to samo, co rzutowanie w C - wypróbuj to z const itp., Usuwa je!
paulm,
13
Odpowiedź „Nie rzuty w stylu c są odradzane w przypadku języka C ++ z wielu powodów”. do "Czy int (var) i (int) var w rzeczywistości to to samo?" pewnie sprawia wrażenie, jakbyś nie zdawał sobie sprawy int(var)i (int)varmiał dokładnie to samo znaczenie. int(var)jest odradzany dokładnie w tych przypadkach, w których (int)varjest, z dokładnie tych samych powodów, ponieważ oznacza dokładnie to samo. (Rozumiem jednak, dlaczego i tak zdecydowałeś się na to tutaj, więc nie mówię, że musisz go używać static_cast. Myślę tylko, że ścieżka komentarzy tutaj jest trochę niepotrzebnie myląca.)
46

uint8_tnajprawdopodobniej będzie typedeffor unsigned char. ostreamKlasa ma szczególne przeciążenia dla unsigned char, tj drukuje znak z numerem 5, która jest nie do druku, stąd pustej przestrzeni.

arne
źródło
14
Chciałbym, żeby standard naprawdę traktował std :: uint8_t jako oddzielny typ, a nie tylko cholerną 'typedef. Nie ma rozsądnego powodu, aby stosować semantykę znaków do tych typów w połączeniu z obiektami strumienia.
antred
37

Dodanie jednoargumentowego operatora + przed zmienną dowolnego pierwotnego typu danych da drukowalną wartość liczbową zamiast znaku ASCII (w przypadku typu char).

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl;
SridharKritha
źródło
To fajne, ale dlaczego c ++ nie traktuje uint8_tjako unsigned charwartości liczbowych?
R1S8K
@ R1S8K dzieje się tak, ponieważ while uint8_tjest tylko typem def unsigned char, unsigned charsam jest obsługiwany przez ostreamto samo chari wyświetla swoją wartość ASCII.
Ostry
@Harsh Thanks man! więc jest to typ def, unsigned charktóry wiele wyjaśnia. Więc jedyną liczbą całkowitą jest int, prawda?
R1S8K
@ R1S8K Cóż, najmniejszy typ liczby całkowitej to, short intktóry zajmuje 2 bajty. Istnieje również kilka innych odmian typu całkowitego.
Ciężki
@Harsh Również myślę, że podczas programowania i deklarowania zmiennych nie ma znaczenia, czy zadeklaruję zmienną, która będzie obsługiwała tylko małe liczby nie przekraczające 250 przy dużym rozmiarze rejestru, jak longlub intponieważ kompilator zoptymalizowałby użycie pamięci RAM lub flash według tego, co wypełnia ten rejestr, mam rację?
R1S8K
16

Dzieje się tak, ponieważ operator wyjściowy traktuje to uint8_tjak a char( uint8_tzwykle jest tylko aliasem dla unsigned char), więc wypisuje znak z kodem ASCII (który jest najpowszechniejszym systemem kodowania znaków) 5.

Zobacz np. Ten odnośnik .

Jakiś koleś programista
źródło
Czemu? kompilator C traktuje ją jako liczbę. Myślę, że C ++ jest w tym momencie inny.
R1S8K
@PerchEagle Jeśli przeczytasz odnośnik, zobaczysz, że operator jest przeciążony zarówno dla znaków, jak signedi unsigned(poza zwykłym, charktóry w C ++ jest tak naprawdę trzecim oddzielnym typem). Więc jeśli uint8_tjest aliasem dla unsigned char(bardzo prawdopodobne), to zostanie użyte.
Jakiś programista,
Czy możesz sprawdzić moją odpowiedź w tym wątku i powiedzieć, czy moja odpowiedź jest prawidłowa, czy nie? stackoverflow.com/questions/15585267/… , moja odpowiedź jest przed ostatnią. Dziękuję bardzo.
R1S8K
14
  • Korzystanie z ADL (wyszukiwanie nazw zależnych od argumentów):

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

    wynik:

    *
    42
    
  • Możliwy byłby również niestandardowy manipulator strumienia.

  • Jednoargumentowy operator plus jest również zgrabnym idiomem ( cout << +i << endl).
pepper_chico
źródło
8
Czy KISS nie jest nadal obowiązującym paradygmatem?
πάντα ῥεῖ
1
@ πάνταῥεῖ oczywiście, ale nie zapominaj: wszystko powinno być tak proste, jak to tylko możliwe, ale nie prostsze
pepper_chico
4
@ πάνταῥεῖ ok, dalej wykonuj mnóstwo rzutów w stylu c w kodzie c ++, więc każdy może być produktywny tak, jak jest, w środowisku, do którego pasuje najbardziej.
pepper_chico
5
@ πάνταῥεῖ poważnie? Odlew w stylu funkcjonalnym to także odlewanie w stylu c. zmiana jednego z drugiego nie pomaga w niczym w opuszczeniu królestwa C, sprawdź: stackoverflow.com/a/4775807/1000282 . Pete-Becker skomentował to również w Twojej odpowiedzi, ale chyba przegapiłeś jego ostatni komentarz.
pepper_chico
3
To rozwiązanie jest bardzo eleganckie i skuteczne, ponieważ działa z szablonami. W rzeczywistości jest to jedyne rozwiązanie, które zauważyłem, które mi pasuje. Jedno zastrzeżenie: pierwsza funkcja ma błąd, ponieważ 'os' jest powiązane z jednym typem, a zatem wartość ze znakiem lub bez znaku zostanie wysłana do niewłaściwej wersji operatora << (). Poprawka jest dość prosta:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Georges
7

couttraktuje aajako charwartość ASCII, 5która jest znakiem niedrukowalnym, spróbuj typecastingu intprzed drukowaniem.

Nie martw się, dziecko
źródło
4

operator<<()Przeciążenia pomiędzy istreami charjest funkcją trzeciego. Możesz jawnie użyć funkcji składowej, aby traktować a char(lub a uint8_t) jako 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;
}

Wynik:

value is 5
R Sahu
źródło
2

Jak powiedzieli inni, problem występuje, ponieważ standardowy strumień traktuje znak ze znakiem i znak bez znaku jako pojedyncze znaki, a nie jako liczby.

Oto moje rozwiązanie z minimalnymi zmianami kodu:

uint8_t aa = 5;

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

Dodawanie "+0"jest bezpieczne z dowolną liczbą, w tym zmiennoprzecinkową.

W przypadku typów całkowitych zmieni typ wyniku na intif sizeof(aa) < sizeof(int). I nie zmieni typu, jeśli sizeof(aa) >= sizeof(int).

To rozwiązanie jest również dobre do przygotowania int8_tdo drukowania do przesyłania strumieniowego, podczas gdy inne rozwiązania nie są tak dobre:

int8_t aa = -120;

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

Wynik:

value is -120
bad value is 4294967176

Rozwiązanie PS z ADL podanym przez pepper_chico i πάντα ῥεῖ jest naprawdę piękne.

Siergiej
źródło