uint8_t нельзя напечатать с помощью cout


153

У меня странная проблема с работой с целыми числами в C ++.

Я написал простую программу, которая устанавливает значение переменной и затем распечатывает его, но она не работает должным образом.

В моей программе всего две строчки кода:

uint8_t aa = 5;

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

Результатом этой программы является value is

То есть для aa.

Когда я перехожу uint8_tна uint16_tприведенный выше код, он работает как шарм.

Я использую 64-разрядную версию Ubuntu 12.04 (Precise Pangolin), и моя версия компилятора:

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

2
Возможный дубликат поведения uint8_t iostream
phuclv

Ответы:


160

На самом деле он не печатает пробел, но, скорее всего, это символ ASCII со значением 5, который не печатается (или невидим). Существует ряд невидимых кодов символов ASCII , большинство из которых ниже значения 32, которое на самом деле является пустым.

Вы должны преобразовать aaв unsigned intдля вывода числового значения, поскольку ostream& operator<<(ostream&, unsigned char)пытается вывести видимое символьное значение.

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;

24
Поскольку приведения в стиле C не одобряются, не лучше ли было бы сделать static_cast?
Тим Сегин,

37
Его нужно преобразовать в int. Приведение - это один из способов сделать это, но не единственный. +aaтоже работает.
Пит Беккер,

5
разве int (var) и (int) var на самом деле не одно и то же?
Paulm

9
См. Связанный вопрос с использованием type (var) такой же, как (type) var, он такой же, как приведение C - попробуйте с помощью const и т.д., он удаляет его!
Paulm

13
Ответ: «Нет. Приведение в стиле c не рекомендуется для C ++ по ряду причин». на "разве int (var) и (int) var на самом деле не одно и то же?" "уверен" создает впечатление, что вы не понимаете int(var)и (int)varимеете то же значение. int(var)не рекомендуется в тех случаях, когда (int)varесть, по точно таким же причинам, потому что это означает одно и то же. (Я могу понять, почему вы все равно пошли на это здесь, поэтому я не говорю, что вам нужно использовать static_cast. Я просто думаю, что след комментариев здесь немного излишне запутанный.)

49

uint8_tскорее всего будет typedefдля unsigned char. ostreamКласс имеет специальную перегрузку unsigned char, т.е. печатает символ с номером 5, который является не для печати, следовательно , пустое пространство.


18
Я хочу, чтобы стандарт действительно рассматривал std :: uint8_t как отдельный тип, а не просто чертово typedef. Нет разумной причины применять символьную семантику к этим типам при использовании вместе с объектами потока.
antred

41

Добавление унарного оператора + перед переменной любого примитивного типа данных даст печатное числовое значение вместо символа ASCII (в случае типа char).

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl; // value is 5

Это хорошо, но почему в С ++ не рассматриваются uint8_tкак unsigned charчисловые значения?
R1S8K

@ R1S8K это связано с тем, что, хотя uint8_tэто просто определение типа unsigned char, оно unsigned charсамо обрабатывается ostreamаналогично charи выводит его значение ASCII.
Суровый

@Harsh Спасибо, чувак! так что это определение типа, unsigned charкоторое многое объясняет. Итак, единственное целое число int, верно?
R1S8K

@ R1S8K Ну, наименьший целочисленный тип будет занимать short int2 байта. Также есть несколько других вариантов целочисленного типа.
Суровый

@Harsh Также я думаю, что при программировании и объявлении переменных не имеет значения, объявляю ли я переменную, которая будет иметь дело только с небольшими числами, не превышающими 250, с большим размером регистра, например, longили intпотому что компилятор оптимизирует использование ОЗУ или флэш-памяти согласно тому, что заполняет этот регистр, я прав?
R1S8K

17

Это потому, что оператор вывода обрабатывает uint8_tкак a char( uint8_tобычно это просто псевдоним для unsigned char), поэтому он печатает символ с кодом ASCII (который является наиболее распространенной системой кодировки символов) 5.

См., Например, эту ссылку .


Зачем? компилятор C рассматривает его как число. Я думаю, что на этом этапе С ++ отличается.
R1S8K

@PerchEagle Если вы прочтете ссылку по ссылке, вы увидите, что оператор перегружен как для символов, так signedи для unsignedсимволов (помимо простого, charкоторый в C ++ на самом деле является третьим отдельным типом). Так что если uint8_tэто псевдоним unsigned char(очень вероятно), он и будет использоваться.
Какой-то чувак-программист

Не могли бы вы проверить мой ответ в этой ветке и сказать, правильный ли мой ответ? stackoverflow.com/questions/15585267/… , мой ответ перед последним. Огромное спасибо.
R1S8K

15
  • Использование ADL (поиск имени в зависимости от аргумента):

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

    выход:

    *
    42
    
  • Также возможен настраиваемый манипулятор потока.

  • Оператор унарного плюса - это тоже изящная идиома ( cout << +i << endl).

8
Разве KISS - не действующая парадигма ??
πάντα ῥεῖ

1
@ πάνταῥεῖ, конечно, но не забывайте: все должно быть сделано как можно
проще

4
@ πάνταῥεῖ хорошо, продолжайте делать тонны приведений в стиле c в коде c ++, тогда любой волен быть продуктивным таким, какой он есть, в среде, которая ему больше всего подходит.
pepper_chico

5
@ πάνταῥεῖ серьезно? Приведение в функциональном стиле - это также просто приведение в стиле C. изменение одного из другого ни в чем не помогает при выходе из области C, проверьте: stackoverflow.com/a/4775807/1000282 . pete-becker прокомментировал это и по поводу вашего ответа, но вы, похоже, пропустили его последний комментарий.
pepper_chico

3
Это решение очень элегантное и эффективное, поскольку работает с шаблонами. Фактически, это единственное решение, которое я обнаружил, которое работает для меня. Одно предостережение: первая функция имеет ошибку, потому что 'os' привязан к одному типу, и поэтому либо знаковое, либо беззнаковое значение будет отправлено в неправильную версию operator << (). Исправление достаточно простое:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Жорж,

7

coutобрабатывается aaкак charзначение ASCII, 5которое является непечатаемым символом, попробуйте привести тип intперед печатью.


4

operator<<()Перегрузки между istreamи charявляется функцией , не являющихся членами. Вы можете явно использовать функцию-член, чтобы рассматривать char(или uint8_t) как 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;
}

Выход:

value is 5

2

Как уже говорилось ранее, проблема возникает из-за того, что стандартный поток обрабатывает подписанные char и unsigned char как отдельные символы, а не как числа.

Вот мое решение с минимальными изменениями кода:

uint8_t aa = 5;

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

Добавление "+0"безопасно с любым числом , включая плавающую точку.

Для целочисленных типов он изменит тип результата на intif sizeof(aa) < sizeof(int). И он не изменит тип, если sizeof(aa) >= sizeof(int).

Это решение также хорошо подходит для подготовки int8_tк печати для потоковой передачи, в то время как некоторые другие решения не так хороши:

int8_t aa = -120;

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

Выход:

value is -120
bad value is 4294967176

PS Решение с ADL от pepper_chico и πάντα ῥεῖ действительно красивое.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.