Вопрос

Учитывая, что подписанные и неподписанные целые числа используют одни и те же регистры и т.д. и просто по-разному интерпретируют битовые шаблоны, а символы 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)

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

Обновить

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

#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. Конечно, это не портативно, поэтому не очень полезно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top