Что такое беззнаковый символ?

Для чего используется unsigned char в C / C ++? Чем он отличается от обычного char?


person Landon Kuhn    schedule 16.09.2008    source источник


Ответы (17)


В C ++ существует три различных символьных типа:

  • char
  • signed char
  • unsigned char

Если вы используете символьные типы для текста, используйте неквалифицированный char:

  • это тип символьных литералов, таких как 'a' или '0'.
  • это тип, который составляет строки C, такие как "abcde"

Он также работает как числовое значение, но не указано, рассматривается ли это значение как знаковое или беззнаковое. Остерегайтесь сравнения символов через неравенство - хотя, если вы ограничиваете себя ASCII (0-127), вы почти в безопасности.

Если вы используете типы символов как числа, используйте:

  • signed char, что дает по крайней мере диапазон от -127 до 127. (Обычно от -128 до 127)
  • unsigned char, что дает вам по крайней мере диапазон от 0 до 255.

«По крайней мере», потому что стандарт C ++ дает только минимальный диапазон значений, который должен охватывать каждый числовой тип. sizeof (char) должен быть 1 (т. Е. Один байт), но теоретически байт может быть, например, 32-битным. sizeof по-прежнему будет сообщать свой размер как 1 - это означает, что вы могли иметь sizeof (char) == sizeof (long) == 1.

person Fruny    schedule 17.09.2008
comment
Для ясности, могли бы вы иметь 32-битные символы и 32-битные целые числа и иметь sizeof (int)! = Sizeof (char)? Я знаю, что в стандарте указано sizeof (char) == 1, но основывается ли относительный sizeof (int) на фактической разнице в размере или разнице в диапазоне? - person Joseph Garvin; 12.01.2009
comment
Джозеф, sizeof дает размер объектного представления типа. если вы скажете 32-битное int, это сначала мало что скажет. скорее всего, вы имеете в виду представление объекта (это физический размер, включая все биты заполнения). - person Johannes Schaub - litb; 14.01.2009
comment
в этом случае sizeof (int)! = sizeof (char) не может быть истинным, потому что char / unsigned / signed char используют все биты своего объектного представления для представления своих значений (так называемое представление значения) - person Johannes Schaub - litb; 14.01.2009
comment
Гарантированный диапазон signed char составляет от -127 до 127, но, предполагая дополнение 2, вы получите от -128 до 127. И это довольно безопасное предположение. - person Steve Jessop; 16.05.2012
comment
как получилось, что 1 байт может быть 32 бита? - person pseudonym_127; 16.05.2013
comment
+1. Но в C ++ есть четыре различных типа символов, wchar_t - один из них. - person Eric Z; 24.08.2013
comment
@Fruny Я заметил, что вы написали sizeof () с пробелом между ними, не могли бы вы объяснить его использование? На данный момент я ищу ответ по этому поводу. Заранее спасибо. - person Unheilig; 12.01.2014
comment
начиная с С ++ 11 у вас есть 6 различных типов: char, signed char, unsigned char, wchar_t, char16_t, char32_t. - person marcinj; 16.02.2014
comment
@ pseudonym_127 хороший вопрос. Я думаю, это потому, что технически размер бита не указан (хотя обычно это 8 бит). Надеюсь, кто-нибудь еще сможет это проверить. - person Celeritas; 09.08.2014
comment
@unheilig Обычно после sizeof ставится пробел, потому что это не функция, а оператор. Имхо даже лучший стиль - опускать круглые скобки при выборе размера переменной. sizeof *p или sizeof (int). Это позволяет быстро понять, применимо ли это к типу или переменной. Точно так же излишне ставить круглые скобки после return. Это не функция. - person Patrick Schlüter; 28.11.2014
comment
char: это тип символьных литералов, таких как 'a' или '0'. верно в C ++, но не в C. В C 'a' является int. - person chux - Reinstate Monica; 10.05.2016
comment
Вы говорите из любопытства, но теоретически байт может быть, например, 32-битным, но на самом деле байт равен 8 битам. Что мне не хватает? Спасибо. - person Brian; 28.03.2018
comment
байт в этом контексте относится к наименьшей адресуемой единице памяти. Стандарты C и C ++ требуют, чтобы байт был не менее 8 бит, но они не указывают максимум. Практически на всех современных компьютерах общего назначения (включая все, что совместимо с последними версиями posix) байт составляет ровно 8 бит, но специализированные платформы DSP и ретро-системы могут иметь байты большего размера. - person plugwash; 01.03.2019
comment
Начиная с C ++ 20 у вас есть 7 различных типов. char signed char unsigned char wchar_t char8_t char16_t char_32_t - person 김선달; 02.07.2021

Это зависит от реализации, так как стандарт C НЕ определяет подписи char. В зависимости от платформы char может быть signed или unsigned, поэтому вам нужно явно запросить signed char или unsigned char, если ваша реализация зависит от этого. Просто используйте char, если вы собираетесь представлять символы из строк, так как это будет соответствовать тому, что ваша платформа помещает в строку.

Разница между signed char и unsigned char такая, как и следовало ожидать. На большинстве платформ signed char будет 8-битным числом с дополнением до двух в диапазоне от -128 до 127, а unsigned char будет 8-битным целым числом без знака (от 0 до 255). Обратите внимание, что стандарт НЕ требует, чтобы типы char имели 8 бит, а только sizeof(char) возвращали 1. Вы можете получить количество бит в символе с CHAR_BIT в limits.h. Однако сегодня есть несколько платформ, на которых это будет что-то другое, кроме 8.

Подробное описание этой проблемы можно найти здесь.

Как уже упоминалось после того, как я опубликовал это, вам лучше использовать int8_t и uint8_t, если вы действительно хотите представлять небольшие целые числа.

person Todd Gamblin    schedule 16.09.2008
comment
знаковый символ имеет минимальный диапазон от -127 до 127, а не от -128 до 127 - person 12431234123412341234123; 28.01.2017
comment
@ 12431234123412341234123: Технически верно, поскольку стандарт C определяет от -127 до 127 как минимальный диапазон. Я призываю вас найти платформу, которая не использует арифметику с дополнением до двух. Практически на каждой современной платформе фактический диапазон подписанных символов будет от -128 до 127. - person Todd Gamblin; 06.02.2017
comment
CHAR_BIT должен быть не менее 8 бит по стандарту. - person martinkunev; 12.03.2019

Поскольку я чувствую, что это действительно необходимо, я просто хочу изложить некоторые правила C и C ++ (в этом отношении они одинаковы). Во-первых, все биты unsigned char участвуют в определении значения любого объекта типа unsigned char. Во-вторых, unsigned char явно указано без знака.

Теперь я обсуждал с кем-то, что происходит, когда вы конвертируете значение -1 типа int в unsigned char. Он отказался от идеи, что все биты результирующего unsigned char будут установлены в 1, потому что он беспокоился о представлении знаков. Но он не обязан. Из этого правила сразу следует, что преобразование делает то, что задумано:

Если новый тип беззнаковый, значение преобразуется путем многократного добавления или вычитания на единицу большего, чем максимальное значение, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа. (6.3.1.3p2 в черновике C99)

Это математическое описание. C ++ описывает это в терминах исчисления по модулю, которое подчиняется тому же правилу. В любом случае не гарантируется, что все биты целого числа -1 равны единице перед преобразованием. Итак, что у нас есть, чтобы мы могли утверждать, что в результирующем unsigned char все свои CHAR_BIT биты обращены в 1?

  1. Все биты участвуют в определении его значения, то есть в объекте не встречаются биты заполнения.
  2. Добавление только одного раза UCHAR_MAX+1 к -1 даст значение в диапазоне, а именно UCHAR_MAX

Собственно, хватит! Поэтому всякий раз, когда вы хотите иметь unsigned char все его биты одним, вы делаете

unsigned char c = (unsigned char)-1;

Из этого также следует, что преобразование - это не просто усечение битов более высокого порядка. Удачным событием для дополнения до двух является то, что это просто усечение, но то же самое не обязательно верно для других представлений знаков.

person Johannes Schaub - litb    schedule 14.01.2009
comment
Почему бы просто не использовать UCHAR_MAX? - person Nicolás; 05.01.2011
comment
Потому что (unsigned type)-1 - это своего рода идиома. ~0 нет. - person Patrick Schlüter; 28.11.2014
comment
если у меня есть что-то вроде этого int x = 1234 и char *y = &x. Двоичное представление 1234 равно 00000000 00000000 00000100 11010010. Моя машина работает с прямым порядком байтов, поэтому она меняет его и сохраняет в памяти 11010010 00000100 00000000 00000000 LSB идет первым. Теперь основная часть. если я использую printf("%d" , *p). printf будет читать первый байт 11010010 только на выходе будет -46, но 11010010 будет 210, так почему он печатает -46. Я действительно смущен, я думаю, что какое-то продвижение char в целочисленное что-то делает, но я не знаю. - person Suraj Jain; 17.08.2016

Например, использование unsigned char:

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 chars (127, -128, -128), мы бы получили (-99, -99, -99), что было бы (29, 29, 29) в нашем пространстве unsigned char, которое неверно.

person Zachary Garrett    schedule 17.09.2008

Если вы хотите использовать символ как небольшое целое число, самый безопасный способ сделать это - использовать типы int8_t и uint8_t.

person jbleners    schedule 16.09.2008
comment
Не очень хорошая идея: int8_t и uint8_t являются необязательными и не определены в архитектурах, где размер байта не равен 8 битам. И наоборот, signed char и unsigned char всегда доступны и гарантированно содержат не менее 8 бит. Это может быть распространенный способ, но не самый безопасный. - person chqrlie; 07.04.2015
comment
Это комментарий, он не отвечает на вопрос. - person Lundin; 24.11.2017
comment
@chqrlie Значит, самый безопасный способ представить маленькое целое число, если вы хотите сэкономить память, - это использовать signed char и unsigned char? Или вы порекомендуете более безопасную альтернативу в этом конкретном случае? Например, по какой-то причине придерживаться реальных целочисленных типов signed int и unsigned int? - person RobertS supports Monica Cellio; 21.12.2019
comment
@ RobertS-ReinstateMonica: Использование signed char и unsigned char переносимо для всех соответствующих реализаций и сэкономит место для хранения, но может вызвать некоторое увеличение размера кода. В некоторых случаях можно было бы сэкономить больше места для хранения, сохраняя небольшие значения в битовых полях или отдельных битах обычных целочисленных типов. На этот вопрос нет однозначного ответа, применимость этого подхода зависит от конкретного случая. И этот ответ в любом случае не отвечает на вопрос. - person chqrlie; 21.12.2019

unsigned char принимает только положительные значения ... например, от 0 до 255.

в то время как

signed char принимает как положительные, так и отрицательные значения ... например, от -128 до +127

person munna    schedule 22.01.2013

signed char имеет диапазон от -128 до 127; unsigned char имеет диапазон от 0 до 255.

char будет эквивалентен символу со знаком или без знака, в зависимости от компилятора, но это отдельный тип.

Если вы используете строки в стиле C, просто используйте char. Если вам нужно использовать символы для арифметики (довольно редко), укажите явно подписанный или неподписанный для переносимости.

person James Hopkin    schedule 16.09.2008

char и unsigned char не гарантируют, что они будут 8-битными типами на всех платформах, они гарантированно будут 8-битными или больше. На некоторых платформах есть 9-битные, 32-битные или 64-битные байты.. Однако наиболее распространенные сегодня платформы (Windows, Mac, Linux x86 и т. Д.) Имеют 8-битные байты.

person bk1e    schedule 17.09.2008

unsigned char - это байтовое значение без знака (от 0 до 255). Вы можете думать о char как о «персонаже», но на самом деле это числовое значение. Обычный char подписан, поэтому у вас есть 128 значений, и эти значения сопоставляются с символами с использованием кодировки ASCII. Но в любом случае то, что вы храните в памяти, является байтовым значением.

person Zac Gochenour    schedule 16.09.2008
comment
Обычный char подписан: нет, это зависит от реализации. И нет никакой гарантии, что диапазон значений беззнакового char составляет от 0 до 255: это по крайней мере, но он может быть шире. - person Fabio says Reinstate Monica; 20.06.2020
comment
char не может быть байтом. - person qwr; 08.07.2020

Что касается прямых значений, то обычный символ используется, когда известно, что значения находятся между CHAR_MIN и CHAR_MAX, в то время как беззнаковый символ обеспечивает двойной диапазон на положительном конце. Например, если CHAR_BIT равно 8, диапазон обычного char гарантированно будет только [0, 127] (потому что он может быть подписанным или беззнаковым), тогда как unsigned char будет [0, 255], а signed char будет [-127, 127] ].

С точки зрения того, для чего он используется, стандарты позволяют напрямую преобразовывать объекты POD (простые старые данные) в массив беззнаковых символов. Это позволяет вам исследовать представление и битовые шаблоны объекта. Такой же гарантии безопасного выбора типа не существует для char или signed char.

person Julienne Walker    schedule 16.09.2008
comment
На самом деле, это чаще всего будет [-128, 128]. - person RastaJedi; 24.04.2016
comment
Стандарты только формально определяют представление объекта как последовательность из unsigned char, а не как массив конкретно, и любое преобразование определяется только формально путем копирования из объект в реальный, объявленный массив из unsigned char, а затем проверяет последний. Неясно, может ли OR быть напрямую переинтерпретировано как такой массив с учетом арифметики указателя, то есть, будет ли массив == в этом использовании. В надежде прояснить этот вопрос был открыт основной вопрос №1701. К счастью, эта двусмысленность в последнее время меня действительно беспокоит. - person underscore_d; 30.08.2016
comment
@RastaJedi Нет, не будет. Не может. Диапазон -128 ... + 128 физически невозможно представить с помощью 8 бит. Эта ширина поддерживает только 2 ^ 8 == 256 дискретных значений, но -128 ... + 128 = 2 * 128 + 1 для 0 = 257. Представление величины знака допускает -127 ... + 127, но имеет 2 (биполярное) нули. Представление с дополнением до двух поддерживает один ноль, но составляет диапазон, имея еще одно значение на отрицательной стороне; допускает -128 ... + 127. (И так для обоих с большей разрядностью.) - person underscore_d; 30.08.2016
comment
Что касается моего 2-го комментария, разумно предположить, что мы можем взять указатель на 1-й unsigned char OR, а затем продолжить, используя ++ptr оттуда, чтобы прочитать каждый его байт ... но AFAICT, это не конкретно определено как разрешенное, поэтому нам остается сделать вывод, что это "вероятно, нормально" из множества других отрывков (и во многих отношениях простого существования memcpy) в Стандарте, сродни мозаике головоломка. Что не идеально. Что ж, может быть, формулировка со временем улучшится. Вот проблема CWG, о которой я упоминал, но не хватало места для ссылки - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701 - person underscore_d; 30.08.2016
comment
@underscore_d извините, это была опечатка. [-128, 127] - вот что я хотел напечатать: p. Да, я знаю о двойных нулях («положительный» и «отрицательный» ноль) со знаком / величиной. Я, должно быть, устал: p. - person RastaJedi; 31.08.2016

unsigned char - это суть всех хитростей. Почти во ВСЕХ компиляторах для ВСЕЙ платформы unsigned char - это просто байт и целое число без знака (обычно) из 8 бит, которое можно рассматривать как небольшое целое число или как набор битов.

В зависимости, как сказал кто-то другой, стандарт не определяет знак символа. Итак, у вас есть 3 разных типа char: char, signed char, unsigned char.

person ugasoft    schedule 16.09.2008
comment
Битовые уловки, также известные как бит-тидлинг или бит-хакинг, действительно вызывают привыкание ;-) - person chqrlie; 07.04.2015
comment
Проблемы возникают из-за нулей. Чтобы избежать зависимости от твидлинга, держитесь подальше от мелочей. - person DragonLord; 23.05.2016

Если вам нравится использовать различные типы определенной длины и подписи, вам, вероятно, лучше использовать uint8_t, int8_t, uint16_t и т. Д. Просто потому, что они делают именно то, что говорят.

person Dark Shikari    schedule 16.09.2008

unsigned char принимает только положительные значения: от 0 до 255, а signed char принимает положительные и отрицательные значения: от -128 до +127.

person NL628    schedule 24.11.2017

Некоторые поисковые запросы нашли это, где люди обсуждали это.

Беззнаковый символ - это, по сути, один байт. Таким образом, вы могли бы использовать это, если вам нужен один байт данных (например, возможно, вы хотите использовать его для включения и выключения флагов, которые будут передаваться функции, как это часто делается в Windows API).

person neuroguy123    schedule 16.09.2008

Беззнаковый символ использует бит, зарезервированный для знака обычного символа, в качестве другого числа. Это изменяет диапазон на [0 - 255], а не на [-128 - 127].

Обычно символы без знака используются, когда вам не нужен знак. Это будет иметь значение при выполнении таких вещей, как сдвиг бит (сдвиг расширяет знак) и других вещей при работе с char как байтом, а не с использованием его как числа.

person Community    schedule 16.09.2008

цитируется из книги "Занятия по программированию на языке Си":

Квалификатор signed или unsigned может применяться к char или любому целому числу. Беззнаковые числа всегда положительны или равны нулю и подчиняются законам арифметики по модулю 2 ^ n, где n - количество бит в типе. Так, например, если символы равны 8 битам, переменные типа unsigned char имеют значения от 0 до 255, а символы со знаком имеют значения от -128 до 127 (в машине с дополнением до двух). Независимо от того, являются ли простые символы знаковыми или беззнаковыми, определяется машиной -зависимые, но печатные символы всегда положительны.

person ZhaoGang    schedule 21.07.2017

signed char и unsigned char представляют 1 байт, но имеют разные диапазоны.

   Type        |      range
-------------------------------
signed char    |  -128 to +127
unsigned char  |     0 to 255

В signed char, если мы рассматриваем char letter = 'A', 'A' представляет двоичное число 65 в ASCII/Unicode, если 65 может быть сохранено, -65 также может быть сохранено. В ASCII/Unicode нет отрицательных двоичных значений, чтобы не беспокоиться об отрицательных значениях.

Пример

#include <stdio.h>

int main()
{
    signed char char1 = 255;
    signed char char2 = -128;
    unsigned char char3 = 255;
    unsigned char char4 = -128;

    printf("Signed char(255) : %d\n",char1);
    printf("Unsigned char(255) : %d\n",char3);

    printf("\nSigned char(-128) : %d\n",char2);
    printf("Unsigned char(-128) : %d\n",char4);

    return 0;
}

Вывод -:

Signed char(255) : -1
Unsigned char(255) : 255

Signed char(-128) : -128
Unsigned char(-128) : 128
person Kalana    schedule 04.01.2020
comment
char не гарантируется равным одному байту, а signed char гарантированно удерживает только диапазон [-127,127] (хотя почти все системы используют дополнение до двух и содержат не менее [-128,127]) - person qwr; 08.07.2020