Цикл значений символов без знака C

#c #types #casting #char

Вопрос:

Я (думаю, что я) понимаю, как работает математика с различными типами переменных. Например, если я превышу максимальный предел unsigned int переменной, она вернется к 0 .

Я не понимаю поведения этого кода с unsigned char :

 #include<iostream>

int main() {
    unsigned char var{ 0 };
    for(int i = 0; i < 501;   i) {
        var  = 1;
        std::cout << var << 'n';
    }
}
 

Это просто выводит 1...9 данные , затем некоторые символы и заглавные буквы, а затем просто ничего не печатает. Он не возвращается к значениям 1...9 и т. Д.

С другой стороны, если я приведу к int этому перед печатью:

 #include<iostream>

int main() {
    unsigned char var{ 0 };
    for(int i = 0; i < 501;   i) {
        var  = 1;
        std::cout << (int)var << 'n';
    }
}
 

Он печатает с 1...255 , а затем выполняет обратный цикл 0...255 .

Это почему? Похоже, что unsgined char переменная выполняет цикл (как мы видим из приведения int).

Безопасно ли заниматься математикой с unsigned char переменными? Какое поведение я вижу здесь?

Комментарии:

1. Какой компилятор компилирует это, и какую командную строку вы используете для его запуска? Неповторяющееся поведение 1..9 может быть связано с тем, как используемая вами оболочка работает с конкретными непечатаемыми символами (а не с C ).

2. @JohnFilleau я использую g (GCC) 11.1.0 как g test.cpp -o test . И просто бежит, как ./test

3. Вы видите что-то другое? например, повторяющиеся 1..9?

4. Я вижу повторяющиеся 1..9, а также непечатаемые обозначения до и после набора ASCII. Но 1..9 определенно повторяется.

Ответ №1:

Почему он не выводит ожидаемое целочисленное значение?

Проблема не в цикличности char . Проблема заключается в операции вставки для std::ostream объектов и 8-разрядных целочисленных типов. Функции, не являющиеся членами operator<< для этих типов, обрабатывают все 8-разрядные целые числа ( char , signed char , и unsigned char ) как их типы символов ASCII.

оператор<

Канонический способ обработки вывода 8-разрядных целочисленных типов-это то, как вы это делаете. Я лично предпочитаю это вместо этого:

 char foo;
std::cout <<  foo;
 

Унарный оператор преобразует char тип в integer тип, который затем вызывает функцию целочисленной печати.

Обратите внимание, что переполнение целых чисел определяется только для unsigned целочисленных типов. Если вы повторите это с char или signed char , поведение не определено стандартом. ЧТО-то обязательно произойдет, потому что мы живем в реальности, но поведение переполнения может отличаться от компилятора к компилятору.

Почему он не повторяет 0..9 символов

Я протестировал это с помощью g компиляции и bash на Ubuntu 20.04. Мои непечатаемые символы в некоторых случаях обрабатываются как явные символы, а в других случаях ничего не печатается. Неповторяющееся поведение должно быть связано с тем, как ваша оболочка обрабатывает эти непечатаемые символы. Мы не можем ответить на этот вопрос без дополнительной информации.

Ответ №2:

В этом случае символы без знака не рассматриваются как числа. Этот тип данных буквально представляет собой байт:

 1 byte = 8 bits = 0000 0000 which means 0.
 

То, что печатает cout, — это символ, представляющий тот байт, который вы изменили, добавив к нему 1.

Например:

 0 = 0000 0000
1 = 0000 0001
2 = 0000 0010
.
.
.
9 = 0000 1001
 

Затем здесь начинаются другие символы, которые не связаны с числами.
Таким образом, если вы приведете его к int, он даст вам числовые представления этого байта, что даст вам результат 0-255.

Надеюсь, это прояснит ситуацию!

Правка: Сделал объяснение более ясным.

Комментарии:

1. «Символы без знака-это не числа» , так оно и есть. Просто cout к ним относятся необычно.

2. unsigned char является целым числом

3. @HolyBlackCat Я имел в виду, что в этом случае они не рассматриваются как числа, но да, я неправильно выразился.