uint8_t не можна друкувати cout


146

У мене є дивна проблема щодо роботи з цілими числами в C ++.

Я написав просту програму, яка встановлює значення змінної, а потім друкує її, але вона працює не так, як очікувалося.

Моя програма має лише два рядки коду:

uint8_t aa = 5;

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

Вихід цієї програми є value is

Тобто він друкує порожнім для aa.

Коли я переходжу uint8_tдо uint16_tвищевказаного коду, працює як шарм.

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

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

2
Можливий дублікат поведінки
ioint

Відповіді:


151

Він насправді не друкує порожній, але, швидше за все, символ 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 - це те саме?
палм

9
Дивіться зв'язане запитання, використовуючи тип (var) - це те саме, що (тип) var є таким же, як і C C - спробуйте це з const тощо, це видаляє!
палм

13
Відповідь "Ні. Стрічки в стилі c не перешкоджають c ++ з кількох причин". щоб "не є int (var) та (int) var насправді те саме?" Звичайно, це здається так, ніби ви не усвідомлювали int(var)і (int)varмаєте абсолютно те саме значення. int(var)відштовхується саме в тих випадках, коли (int)varсаме з тих самих причин, бо це означає саме те саме. (Я можу зрозуміти, чому ви все одно ходили б тут, тому я не кажу, що вам потрібно користуватися static_cast. Я просто думаю, що коментарний слід тут трохи

46

uint8_tшвидше за все, буде typedefдля unsigned char. ostreamКлас має спеціальну перевантаження unsigned char, тобто друкує символ з номером 5, який є не для друку, отже , порожній простір.


14
Я б хотів, щоб стандартні справді трактували std :: uint8_t як окремий тип, а не просто friggin 'typedef. Немає жодної розумної причини застосовувати символьну семантику до цих типів при використанні спільно з об'єктами потоку.
antred

37

Додавання оператора unary + перед змінною будь-якого примітивного типу даних дасть друковане числове значення замість символу ASCII (у випадку типу char).

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

Це добре, але чому c ++ не трактує так, uint8_tяк unsigned charце були б числові значення?
R1S8K

@ R1S8K це тому, що хоча uint8_tце лише тип def unsigned char, unsigned charвін обробляється так ostreamсамо, як charі друкує його значення ASCII.
Суворий

@Harsh Спасибі людино! так що це тип def, unsigned charщо пояснює багато. Тож єдине ціле число int, правда?
R1S8K

@ R1S8K Ну, найменшим цілим типом буде те, short intщо займає 2 байти. Існує також кілька інших варіантів цілого типу.
Суворий

@Harsh Також я думаю, що при програмуванні та оголошенні змінних не має значення, якщо я декларую змінну, яка матиме справу лише з невеликими числами, що не перевищують, як 250, з великим розміром реєстру на кшталт longабо intтому, що компілятор оптимізував би використання оперативної пам'яті або спалаху відповідно до того, що заповнює цей реєстр, я правий?
R1S8K

16

Це тому, що оператор виводу трактує uint8_tподібне char( uint8_tяк правило, лише псевдонім для unsigned char), тому він друкує символ за допомогою коду ASCII (який є найпоширенішою системою кодування символів) 5.

Див., Наприклад, цю посилання .


Чому? компілятор C трактує це як число. Я думаю, що C ++ на даний момент відрізняється.
R1S8K

@PerchEagle Якщо ви прочитаєте пов'язану посилання, ви побачите, що оператор перевантажений як для символів, так signedі для unsignedсимволів (крім звичайного, charякий у C ++ - це третій окремий тип). Тож, якщо uint8_tце псевдонім для unsigned char(дуже ймовірно), це те, що буде використано.
Якийсь програміст чувак

Чи можете ви перевірити мою відповідь на цю тему і сказати мені, чи правильна моя відповідь? stackoverflow.com/questions/15585267/… , моя відповідь перед останньою. Дуже дякую.
R1S8K

14
  • Використання 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 все ще не є дійсною парадигмою ??
πάντα ῥεῖ


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

5
@ πάνταῥεῖ серйозно? Функціональний стиль акторського складу - це лише кастинг стилю. зміна одного з іншого не допомагає ні в чому залишати область С, перевірте: stackoverflow.com/a/4775807/1000282 . Піт-бекер також прокоментував це на вашу відповідь, але, здається, ви пропустили його останній коментар.
pepper_chico

3
Це рішення є дуже елегантним та ефективним, оскільки працює з шаблонами. Насправді це єдине рішення, яке я помітив, яке працює на мене. Однак, один застереження, перша функція має помилку, оскільки 'os' прив’язаний до одного типу, і тому або підписане, або непідписане значення буде надіслано неправильній версії оператора << (). Виправлення досить просте:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Жорж


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

Як говорили інші, перш ніж проблема виникає, оскільки стандартний потік розглядає підписані знаки та неподписані знаки як одиничні символи, а не як числа.

Ось моє рішення з мінімальними змінами коду:

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, надане перцем_chico та πάντα ῥεῖ, справді прекрасне.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.