Что означает для подписи чар? - PullRequest
18 голосов
/ 16 января 2009

Учитывая, что целые числа со знаком и без знака используют одни и те же регистры и т. Д. И просто по-разному интерпретируют битовые комбинации, а символы C в основном представляют собой 8-разрядные числа, в чем разница между знаковыми и беззнаковыми символами в C? Я понимаю, что подписанность char определяется реализацией, и я просто не могу понять, как она может когда-либо иметь значение, по крайней мере, когда char используется для хранения строк вместо математики.

Ответы [ 9 ]

22 голосов
/ 16 января 2009

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

Фактически, при работе в стесненных средах памяти, таких как встроенные 8-битные приложения, символ часто используется для математических вычислений, и тогда это имеет большое значение. Это связано с тем, что по умолчанию в C. нет типа byte.

19 голосов
/ 16 января 2009

В терминах значений, которые они представляют:

без знака char:

  • охватывает диапазон значений 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)

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

Update

Некоторые особенности реализации, упомянутые в комментариях:

10 голосов
/ 16 января 2009
#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

Это важно при сортировке строк.

3 голосов
/ 16 января 2009

Есть пара отличий. Наиболее важно, если вы переполняете допустимый диапазон символа, назначая ему слишком большое или маленькое целое число, и символ подписывается, результирующее значение определяется реализацией, или даже некоторый сигнал (в 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 ++ нет битов заполнения для всех трех типов символов.

2 голосов
/ 16 января 2009

«Что означает подпись символа?»

Традиционно набор символов ASCII состоит из 7-битных кодировок символов. (В отличие от 8-битного EBCIDIC.)

Когда был разработан и реализован язык Си, это было серьезной проблемой. (По разным причинам, например, для передачи данных через устройства с последовательным модемом.) Дополнительный бит используется как четность.

«Знак со знаком» идеально подходит для этого представления.

Двоичные данные, OTOH, просто принимают значение каждого 8-битного «фрагмента» данных, поэтому знак не требуется.

1 голос
/ 16 апреля 2013

Подпись работает примерно так же в char с, как и в других целочисленных типах. Как вы заметили, символы в действительности являются однобайтовыми целыми числами. ( Не обязательно 8-битный , хотя! Есть разница; байт может быть больше, чем 8 бит на некоторых платформах, а char s скорее привязаны к байту из-за определений char и sizeof(char). Макрос CHAR_BIT, определенный в <limits.h> или в C ++ <climits>, сообщит вам, сколько битов в char.).

Что касается того, почему вам нужен символ со знаком: в C и C ++ нет стандартного типа с именем byte. Для компилятора char являются байтами и наоборот, и он не различает их. Иногда, однако, вы хотите - иногда вы хотите, чтобы , чтобы char был однобайтовым числом, и в этих случаях (особенно в том, как маленький диапазон может иметь байт), вы также обычно заботитесь подписан ли номер или нет. Я лично использовал подпись (или неподписанность), чтобы сказать, что определенный char является (числовым) «байтом», а не символом, и что он будет использоваться численно. Без указанной подписи этот символ char действительно является символом и предназначен для использования в качестве текста.

Раньше я делал это, скорее. Теперь более новые версии C и C ++ имеют (u?)int_least8_t (в настоящее время typedef'd в <stdint.h> или <cstdint>), которые являются более явно числовыми (хотя в любом случае они обычно будут просто typedefs для знаковых и беззнаковых char типов) ).

1 голос
/ 16 января 2009

Арифметика в байтах важна для компьютерной графики (где 8-битные значения часто используются для хранения цветов). Кроме того, я могу вспомнить два основных случая, когда символ знака имеет значение:

  • преобразование в большее значение
  • функции сравнения

Гадость в том, что они не будут кусаться, если все ваши строковые данные 7-битные. Тем не менее, он обещает стать бесконечным источником неясных ошибок, если вы пытаетесь сделать свою C / C ++ программу 8-битной чистой.

0 голосов
/ 16 января 2009

В подписанных символах есть одна вещь: вы можете проверить c> = '' (пробел) и убедиться, что это обычный печатный символ ascii. Конечно, это не портативно, поэтому не очень полезно.

0 голосов
/ 16 января 2009

Единственная ситуация, которую я могу себе представить, это проблема, если вы решите делать математику на символах. Совершенно законно написать следующий код.

char a = (char)42;
char b = (char)120;
char c = a + b;

В зависимости от подписи символа, c может принимать одно из двух значений. Если символы без знака, то c будет (char) 162. Если они подписаны, то это будет переполнение, поскольку максимальное значение для подписанного символа равно 128. Я предполагаю, что большинство реализаций просто вернет (char) -32.

...