Что такое `unsigned char`?
Проблема: Использование unsigned char
в C/C++
В C/C++ существует тип данных unsigned char
, который часто вызывает вопросы у разработчиков. В частности, возникает необходимость понять, для чего он используется и чем он отличается от обычного char
.
С одной стороны, char
может быть знаковым (signed) или беззнаковым (unsigned), в зависимости от реализации компилятора, и, как правило, используется для хранения символов. С другой стороны, unsigned char
является беззнаковым типом, который может хранить только неотрицательные значения в диапазоне от 0 до 255.
Следует рассмотреть конкретные случаи, когда использование unsigned char
оправдано, например, при работе с двоичными данными, изображениями или сетевыми протоколами, где важно избежать знаковых значений и работать только с положительными целыми числами.
Пожалуйста, дайте разъяснения по следующими вопросам:
- Когда следует использовать
unsigned char
вместо обычногоchar
? - Какие примеры использования
unsigned char
вы можете привести в контексте работы с данными? - Как различия между
signed char
,unsigned char
и обычнымchar
могут повлиять на производительность или поведение программы?
5 ответ(ов)
В C++ есть три разных типа символов:
char
signed char
unsigned char
1. char
Если вы используете символьные типы для текста, применяйте неквалифицированный char
:
- это тип символьных литералов, таких как
'a'
или'0'
(в C++ это именно так, в C их тип -int
) - это тип, из которого состоят C-строки, например,
"abcde"
Хотя char
также работает как числовое значение, его знак (подписанность или беззнаковость) не определен. Будьте осторожны при сравнении символов через неравенства - хотя если вы ограничите себя символами ASCII (0-127), то вы вряд ли столкнетесь с проблемами.
2. signed char
/ 3. unsigned char
Если вы используете символьные типы как числа, используйте:
signed char
, который дает вам по крайней мере диапазон от -127 до 127. (обычно от -128 до 127)unsigned char
, который дает вам по крайней мере диапазон от 0 до 255. Это может быть полезно для отображения октета, например, в виде шестнадцатеричного значения.
"По крайней мере", потому что стандарт C++ только указывает минимальный диапазон значений, который каждый числовой тип должен покрывать. sizeof(char)
должен равняться 1 (т.е. один байт), но в теории байт может составлять, например, 32 бита. sizeof
все равно будет указывать его размер как 1
- это означает, что у вас может быть sizeof(char) == sizeof(long) == 1
.
Поскольку считаю, что это действительно необходимо, хочу изложить некоторые правила C и C++ (в этой части они одинаковы). Первое: все биты переменной типа unsigned char
участвуют в определении значения любого объекта типа unsigned char
. Второе: unsigned char
явно обозначен как беззнаковый.
Недавно у меня произошла дискуссия с одним человеком о том, что происходит при преобразовании значения -1
типа int
в unsigned char
. Он был против того, чтобы считать, что результирующий unsigned char
имеет все свои биты, установленные в 1, поскольку его беспокоила проблема представления знака. Но ему не о чем беспокоиться. Исходя из данного правила, преобразование выполняется должным образом:
Если новый тип беззнаковый, значение преобразуется путем многократного сложения или вычитания большего на единицу, чем максимальное значение, которое может быть представлено в новом типе, пока значение не окажется в пределах нового типа. (параграф
6.3.1.3p2
в черновике C99)
Это математическое описание. В C++ это описывается в терминах модульной арифметики, что приводит к тому же правилу. В любом случае, что не гарантируется, так это то, что все биты целого числа -1
равны 1 до преобразования. Так на чем основано наше утверждение, что результирующий unsigned char
имеет все свои CHAR_BIT
битов, установленные в 1?
- Все биты участвуют в определении его значения — то есть, в объекте нет битов заполнения.
- Если сложить один раз
UCHAR_MAX + 1
к-1
, мы получим значение в пределах диапазона, а именноUCHAR_MAX
.
Этого вполне достаточно! Так что, когда вам нужно создать unsigned char
, у которого все биты равны 1, вы можете сделать следующее:
unsigned char c = (unsigned char)-1;
Также следует отметить, что преобразование — это не просто усечение старших битов. Удачное обстоятельство для дополнительного представления заключается в том, что в этом случае действительно происходит только усечение, но это не обязательно верно для других представлений знака.
unsigned char
часто используется в компьютерной графике, где обычно (хотя не всегда) каждому компоненту цвета соответствует один байт. Обычно RGB (или RGBA) цвет представляется как 24 (или 32) бита, где каждый бит — это unsigned char
. Поскольку значения unsigned char
находятся в диапазоне [0, 255], их интерпретация выглядит следующим образом:
- 0 означает полное отсутствие данного компонента цвета.
- 255 означает 100% насыщенности данного цветового пигмента.
Таким образом, цвет RGB для красного будет представлен как (255, 0, 0) — это значит (100% красного, 0% зеленого, 0% синего).
Почему бы не использовать signed char
? Аритметика и побитовые операции становятся проблематичными. Как уже было объяснено, диапазон signed char
сдвинут на -128. Простой и наивный (в основном неиспользуемый) способ конвертации RGB в оттенки серого — усреднение всех трех цветовых компонентов, однако это приводит к проблемам, когда значения цветовых компонентов отрицательные. Красный цвет (255, 0, 0) усредняется до (85, 85, 85) при использовании арифметики unsigned char
. Однако, если бы значения были signed char
(127, -128, -128), мы бы получили (-99, -99, -99), что в пространстве unsigned char
эквивалентно (29, 29, 29), что является некорректным результатом.
signed char
имеет диапазон от -128 до 127, а unsigned char
— от 0 до 255.
Тип char
будет эквивалентен либо signed char
, либо unsigned char
, в зависимости от компилятора, но это отдельный тип.
Если вы работаете со строками в C-стиле, просто используйте char
. Если вам нужно использовать символы для арифметических операций (что довольно редко), указывайте явно signed
или unsigned
для обеспечения переносимости.
unsigned char
в языке C занимает только неотрицательные значения, то есть диапазон от 0 до 255.
С другой стороны, signed char
может принимать как положительные, так и отрицательные значения, его диапазон составляет от -128 до +127.
Как изменить цвет вывода echo в Linux
Разница между const int*, const int * const и int * const?
Почему переменные нельзя объявлять в операторе switch?
Как вывести список символов из .so файла?
`unsigned int` против `size_t`: когда и что использовать?