Что такое неподписанный символ?

479

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

Лэндон Кун
источник

Ответы:

548

В 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.

Fruny
источник
4
Чтобы было ясно, могли бы вы иметь 32-разрядные символы и 32-разрядные целые числа и иметь sizeof (int)! = Sizeof (char)? Я знаю, что стандарт говорит sizeof (char) == 1, но является ли относительный размер (int) основанным на фактической разнице в размере или разнице в диапазоне?
Джозеф Гарвин
14
+1. Но в C ++ есть четыре разных типа символов, wchar_t - один из них.
Эрик З
11
Начиная с c ++ 11 у вас есть 6 различных типов: char, знаковый char, unsigned char, wchar_t, char16_t, char32_t.
marcinj
12
@unheilig Обычно ставят пробел после, sizeofпотому что это не функция, а оператор. ИМХО, даже лучше, не использовать круглые скобки при определении размера переменной. sizeof *pили sizeof (int). Это быстро дает понять, относится ли это к типу или переменной. Кроме того, после него необходимо ставить круглые скобки return. Это не функция.
Патрик Шлютер
3
" char: это тип символьных литералов, подобных 'a'или '0'." верно в C ++, но не в C. В C 'a'есть int.
chux - Восстановить Монику
92

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

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

Существует резюме хорошего этого вопроса здесь .

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

Тодд Гамблин
источник
2
подписанный символ имеет минимальный диапазон от -127 до 127, а не от -128 до 127
12431234123412341234123
3
@ 12431234123412341234123: Технически верно, в том смысле, что стандарт С определяет минимальный диапазон от -127 до 127. Тем не менее, я призываю вас найти платформу, которая не использует арифметику дополнения до двух. Почти на каждой современной платформе фактический диапазон подписанных символов будет от -128 до 127.
Тодд Гамблин,
CHAR_BITСтандарт должен быть не менее 8 бит.
Мартинкунев
39

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

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

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

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

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

На самом деле этого достаточно! Поэтому, когда вы хотите иметь unsigned charвсе свои биты один, вы делаете

unsigned char c = (unsigned char)-1;

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

Йоханнес Шауб - Литб
источник
2
Почему бы просто не использовать UCHAR_MAX?
Николас
1
Потому что (unsigned type)-1это какая-то идиома. ~0нет.
Патрик Шлютер
1
если у меня есть что - то вроде этого int x = 1234и char *y = &x. Двоичное представление 1234 есть 00000000 00000000 00000100 11010010. Моя машина имеет прямой порядок байтов, поэтому она переворачивает ее и сохраняет в памяти 11010010 00000100 00000000 00000000LSB. Теперь основная часть. если я использую printf("%d" , *p). printfпрочтете первые байты 11010010только выход , -46но 11010010это 210так , почему же распечатать -46. Я действительно смущен, я думаю, что какой-то символ для целочисленного продвижения делает что-то, но я не знаю.
Сурадж Джейн
27

Как например использование неподписанного символа :

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диапазон a существенно смещен на -128. Очень простой и наивный (в основном неиспользуемый) метод преобразования RGB в оттенки серого заключается в усреднении всех трех цветовых компонентов, но это приводит к проблемам, когда значения цветовых компонентов являются отрицательными. Красный (255, 0, 0) составляет в среднем (85, 85, 85) при использовании unsigned charарифметики. Однако, если бы значения были signed chars (127, -128, -128), мы бы получили (-99, -99, -99), что будет (29, 29, 29) в нашем unsigned charпространстве, что неверно ,

Захари Гарретт
источник
13

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

jbleners
источник
2
Не очень хорошая идея: int8_tа не uint8_tявляются обязательными и не определены на архитектурах , где размер байт не ровно 8 бит. С другой стороны , signed charи unsigned charвсегда доступны и гарантированно трюма не менее 8 бит. Это может быть общий путь, но не самый безопасный .
chqrlie
2
Это комментарий, он не отвечает на вопрос.
Лундин
@chqrlie То есть, самый безопасный способ представить маленькое целое число, если вы хотите сохранить память, это сохранить signed charи unsigned char? Или вы бы порекомендовали лучшую "более безопасную" альтернативу в этом конкретном случае? Например придерживаться «настоящих» целочисленных типов signed intи unsigned intвместо этого по какой-то причине?
RobertS поддерживает Монику
@ RobertS-ReinstateMonica: Использование signed charи unsigned charявляется переносимым для всех соответствующих реализаций и сэкономит место на диске, но может привести к некоторому увеличению размера кода. В некоторых случаях можно было бы сэкономить больше места для хранения, сохраняя небольшие значения в битовых полях или отдельные биты обычных целочисленных типов. Нет абсолютного ответа на этот вопрос, уместность этого подхода зависит от конкретного случая под рукой. И этот ответ никак не касается вопроса.
Chqrlie
10

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

в то время как

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

Манна
источник
9

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

bk1e
источник
8

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

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

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

Джеймс Хопкин
источник
8

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

Зак Гохенур
источник
7

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

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

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

unsigned charэто сердце всей хитрости. Почти во всех компиляторах для платформы ALL это unsigned charпросто байт и целое число без знака (обычно) 8 битов, которое можно рассматривать как маленькое целое число или пакет битов.

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

ugasoft
источник
1
Битовая хитрость,
хитрый трюк
3
Это 0, которые вызывают проблемы. Чтобы избежать пагубной привычки, держитесь подальше от грубых кусочков.
DragonLord
5

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

Темный Шикари
источник
4

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

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

dbrien
источник
4

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

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


источник
4

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

NL628
источник
3

цитата из книги "C программирования laugage":

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

ZhaoGang
источник
2

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
Kalana
источник