Что значит, что символ должен быть подписан?
-
19-08-2019 - |
Вопрос
Учитывая, что подписанные и неподписанные целые числа используют одни и те же регистры и т.д. и просто по-разному интерпретируют битовые шаблоны, а символы C в основном являются всего лишь 8-битными целыми числами, в чем разница между подписанными и неподписанными символами в C?Я понимаю, что знаковость char определяется реализацией, и я просто не могу понять, как это вообще может что-то изменить, по крайней мере, когда char используется для хранения строк, а не для выполнения математики.
Решение
Это не будет иметь значения для строк. Но в Си вы можете использовать символ для математики, когда это будет иметь значение. Р>
Фактически, при работе в стесненных средах памяти, таких как встроенные 8-битные приложения, символ часто используется для математических вычислений, и тогда это имеет большое значение. Это связано с тем, что по умолчанию в Си нет типа byte
.
Другие советы
С точки зрения ценностей, которые они представляют:
неподписанный символ:
- охватывает диапазон значений
0..255 (00000000..11111111)
значения переполняются вокруг нижнего края следующим образом:
0 - 1 = 255 (00000000 - 00000001 = 11111111)
значения переполняются вокруг верхнего края, поскольку:
255 + 1 = 0 (11111111 + 00000001 = 00000000)
оператор побитового сдвига вправо (
>>
) совершает логический сдвиг:10000000 >> 1 = 01000000 (128 / 2 = 64)
подписанный символ:
- охватывает диапазон значений
-128..127 (10000000..01111111)
значения переполняются вокруг нижнего края следующим образом:
-128 - 1 = 127 (10000000 - 00000001 = 01111111)
значения переполняются вокруг верхнего края, поскольку:
127 + 1 = -128 (01111111 + 00000001 = 10000000)
оператор побитового сдвига вправо (
>>
) происходит ли арифметический сдвиг:10000000 >> 1 = 11000000 (-128 / 2 = -64)
Я включил двоичные представления, чтобы показать, что поведение переноса значений является чистой, последовательной двоичной арифметикой и не имеет ничего общего с символом, который подписан / без знака (ожидайте сдвигов вправо).
Обновить
Некоторое специфичное для реализации поведение, упомянутое в комментариях:
- символ != подписанный символ.Тип "char" без "signed" или "unsinged" определяется реализацией, что означает, что он может действовать как тип со знаком или без знака.
- Переполнение целого числа со знаком приводит к неопределенному поведению, при котором программа может делать все, что угодно, включая сброс содержимого ядра или переполнение буфера.
#include <stdio.h>
int main(int argc, char** argv)
{
char a = 'A';
char b = 0xFF;
signed char sa = 'A';
signed char sb = 0xFF;
unsigned char ua = 'A';
unsigned char ub = 0xFF;
printf("a > b: %s\n", a > b ? "true" : "false");
printf("sa > sb: %s\n", sa > sb ? "true" : "false");
printf("ua > ub: %s\n", ua > ub ? "true" : "false");
return 0;
}
[root]# ./a.out
a > b: true
sa > sb: true
ua > ub: false
Это важно при сортировке строк.
Есть несколько отличий. Что наиболее важно, если вы переполняете допустимый диапазон символа, назначая ему слишком большое или маленькое целое число, и символ подписывается, результирующее значение определяется реализацией, или даже некоторый сигнал (в C) может быть повышен, как для всех типов со знаком. , Сравните это с тем случаем, когда вы назначаете что-то слишком большое или маленькое для беззнакового символа: значение оборачивается, вы получите точно определенную семантику. Например, присвоив -1 неподписанному символу, вы получите UCHAR_MAX. Поэтому, когда у вас есть байт, например число от 0 до 2 ^ CHAR_BIT, вы должны использовать неподписанный символ для его хранения.
Знак также имеет значение при переходе к функциям vararg:
char c = getSomeCharacter(); // returns 0..255
printf("%d\n", c);
Предположим, что значение, присвоенное c, будет слишком большим для представления char, и машина использует дополнение до двух. Многие реализации ведут себя в случае, когда вы присваиваете слишком большое значение для символа, в котором битовый шаблон не изменится. Если int сможет представлять все значения char (что и есть для большинства реализаций), то char передается в int перед передачей в printf. Таким образом, ценность того, что передается, будет отрицательной. Повышение до int сохранит этот знак. Таким образом, вы получите отрицательный результат. Однако если char без знака, то значение без знака, и повышение до int даст положительное значение int. Вы можете использовать unsigned char, тогда вы получите точно определенное поведение как для присваивания переменной, так и для передачи в printf, которая затем выведет что-то положительное. Р>
Обратите внимание, что все символы char, unsigned и char имеют ширину не менее 8 бит. Не требуется, чтобы символ был в точности шириной 8 бит. Однако для большинства систем это правда, но для некоторых вы обнаружите, что они используют 32-битные символы. Байт в C и C ++ определен так, чтобы иметь размер char, поэтому байт в C также не всегда точно равен 8 битам.
Другое отличие состоит в том, что в C беззнаковый символ не должен иметь битов заполнения. То есть, если вы обнаружите, что CHAR_BIT равно 8, то значения без знака должны быть в диапазоне 0 .. 2 ^ CHAR_BIT-1. То же самое верно для символа, если он без знака. Что касается знакового символа, вы не можете предполагать что-либо о диапазоне значений, даже если вы знаете, как ваш компилятор реализует материал знака (дополнение к двум или другие параметры), в нем могут быть неиспользуемые биты заполнения. В C ++ нет битов заполнения для всех трех типов символов. Р>
" что означает подпись символа? "
Традиционно набор символов ASCII состоит из 7-битных кодировок символов. (В отличие от 8-битного EBCIDIC.)
Когда был разработан и реализован язык C, это было серьезной проблемой. (По разным причинам, например, для передачи данных через устройства с последовательным модемом.) Дополнительный бит имеет такие же значения, как четность.
A " подписанный символ " случается, идеально подходит для этого представления.
Двоичные данные, OTOH, просто принимают значение каждого 8-битного " chunk " данных, поэтому никаких признаков не требуется.
Арифметика в байтах важна для компьютерной графики (где 8-битные значения часто используются для хранения цветов).Помимо этого, я могу вспомнить два основных случая, когда знак символа имеет значение:
- преобразование в больший int
- функции сравнения
Самое неприятное, что они вас не укусят, если все ваши строковые данные 7-битные.Однако это обещает стать нескончаемым источником непонятных ошибок, если вы пытаетесь сделать свою программу на C / C ++ 8-разрядной чистой.
Подпись работает в char
почти так же, как и в других целочисленных типах. Как вы заметили, символы в действительности являются однобайтовыми целыми числами. ( Не обязательно 8-битный , хотя! Есть разница; байт может быть больше, чем 8 бит на некоторых платформах, а sizeof(char)
s скорее привязаны к байту из-за определений CHAR_BIT
и <limits.h>
. Макрос <climits>
, определенный в byte
или в C ++ (u?)int_least8_t
, сообщит вам, сколько битов в <stdint.h>
.).
Что касается того, почему вам нужен символ со знаком: в C и C ++ нет стандартного типа с именем <cstdint>
. Для компилятора <=> - это байты и наоборот, и он не различает их. Иногда, однако, вы хотите - иногда вы хотите, чтобы это <=> было однобайтовым числом, и в этих случаях (особенно в том, как маленький диапазон может иметь байт), вы также обычно все равно, номер подписан или нет. Я лично использовал подпись (или неподписанность), чтобы сказать, что определенный <=> (числовой) & Quot; byte & Quot; а не персонаж, и что он будет использоваться численно. Без указанной подписи <=> действительно является символом и предназначен для использования в качестве текста.
Раньше я делал это, скорее. Теперь более новые версии C и C ++ имеют <=> (в настоящее время typedef'd в <=> или <=>), которые являются более явно выраженными (хотя в любом случае они обычно будут просто typedefs для подписанных и неподписанных типов <=>). ). р>
Единственная ситуация, которую я могу себе представить, это проблема, если вы решите выполнять математику на символах Совершенно законно написать следующий код.
char a = (char)42;
char b = (char)120;
char c = a + b;
В зависимости от подписи символа, c может принимать одно из двух значений. Если символы без знака, то c будет (char) 162. Если они подписаны, то это будет переполнение, поскольку максимальное значение для подписанного символа равно 128. Я предполагаю, что большинство реализаций просто вернет (char) -32. Р>
В подписанных символах важно то, что вы можете протестировать c > = '' (пробел) и убедиться, что это обычный печатный символ ascii. Конечно, это не портативно, поэтому не очень полезно.