С ролями и символом подписи
Итак, в последнее время я прочитал о проблеме, касающейся трех различных типов в C: char/unsigned char/signature char. Проблема, с которой я сейчас сталкиваюсь, не та, с которой я сталкивался до сих пор (моя программа работает корректно на всех протестированных компьютерах и предназначена только для прямым порядком байтов (в основном на всех современных настольных компьютерах и серверах, использующих Windows/Linux, верно?). Я часто использую символ массив, который я определил для хранения "строки" (конечно, не настоящей строки) как временных переменных. Например, вместо добавления другого символа в стек, я просто повторно использую один из членов, например, массив [0]. Однако я основал эту тактику на тот факт, что символ всегда будет подписан, пока я сегодня не прочитал, что это на самом деле зависит от реализации. Что произойдет, если у меня теперь есть символ и я назначу ему отрицательное значение?
char unknownsignedness = -1;
Если бы я написал
unsigned char A = -1;
Я думаю, что приведение в стиле C будет просто переосмысливать биты, и значение, которое A представляет как тип без знака, становится другим. Прав ли я, что эти броски в стиле C- просто переосмысление битов? Я имею в виду подписанные <-> беззнаковые преобразования.
Так что, если у реализации есть char как unsigned, моя программа перестанет работать как задумано? Возьми последнюю переменную, если я сейчас сделаю
if (A == -1)
Сейчас я сравниваю неподписанный символ со значением знака со знаком, поэтому будет ли это просто сравнивать биты, не заботящиеся о подписи, или это вернет false, потому что, очевидно, A не может быть -1? Я запутался, что происходит в этом случае. Это также мое самое большое беспокойство, так как я часто использую такие символы.
4 ответа
Следующий код печатает No
:
#include <stdio.h>
int
main()
{
unsigned char a;
a = -1;
if(a == -1)
printf("Yes\n");
else
printf("No\n");
return 0;
}
Код a = -1
присваивает значение, определенное реализацией a
; на большинстве машин будет 255. Тест a == -1
сравнивает unsigned char
для int
, поэтому применяются обычные правила продвижения по службе; следовательно, это интерпретируется как
`(int)a == -1`
поскольку a
255, (int)a
по-прежнему 255, и тест дает ложь.
unsigned char a = -1;
ИСО / МЭК 9899:1999 гласит в 6.3.1.3/2:
если новый тип является беззнаковым, значение преобразуется путем многократного сложения или вычитания на единицу больше максимального значения, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа
Мы добавляем (UCHAR_MAX+1)
в -1
один раз, и результат UCHAR_MAX
что, очевидно, находится в диапазоне unsigned char
,
если (а == -1)
В 6.3.1.8/1 есть длинный отрывок:
Если оба операнда имеют одинаковый тип, дальнейшее преобразование не требуется.
В противном случае, если оба операнда имеют целочисленные типы со знаком или оба имеют целочисленные типы без знака, операнд с типом ранга преобразования с меньшим целым числом преобразуется в тип операнда с большим рангом.
В противном случае, если операнд с целым типом без знака имеет ранг, больший или равный рангу типа другого операнда, тогда операнд с целым типом со знаком преобразуется в тип операнда с целым типом без знака.
В противном случае, если тип операнда с целочисленным типом со знаком может представлять все значения типа операнда с целым типом без знака, тогда операнд с целочисленным типом без знака преобразуется в тип операнда с целочисленным типом со знаком.
В противном случае оба операнда преобразуются в тип целого без знака, соответствующий типу операнда с целым типом со знаком.
Звание unsigned char
меньше, чем у int
,
Если int
может представлять все значения, которые unsigned char
может (что обычно имеет место), то оба операнда преобразуются в int
и сравнение возвращает false
,
Если int
не может представлять все значения в unsigned char
, что может случиться на редких машинах с sizeof(int)==sizeof(char)
тогда оба преобразуются в unsigned int
, -1
превращается в UINT_MAX
который случается так же, как UCHAR_MAX
и сравнение возвращает true
,
unsigned char A = -1;
результаты в 255. Нет реинтерпретации при присваивании или инициализации. -1
это просто куча 1
биты в двоичной записи дополнения и 8 из них дословно скопированы.
Сравнения немного отличаются, так как буквальное -1
имеет int
тип.
if (A == -1)
будет делать повышение (неявное приведение) (int)A
перед сравнением, так что в итоге вы сравниваете 255 с -1. Не равный.
И да, вы должны быть осторожны с простым char
,
Я думаю, что на этот вопрос лучше всего ответить быстрым примером (предупреждение: C++, но см. Объяснение моих рассуждений):
char c = -1;
unsigned char u = -1;
signed char s = -1;
if (c == u)
printf("c == u\n");
if (s == u)
printf("s == u\n");
if (s == c)
printf("s == c\n");
if (static_cast<unsigned char>(s) == u)
printf("(unsigned char)s == u\n");
if (c == static_cast<char>(u))
printf("c == (char)u\n");
Выход:
s == c
(unsigned char)s == u
c == (char)u
C обрабатывает значения по-разному, когда используется как есть, но вы правы в том, что приведение будет просто интерпретировать биты. Я использовал C++ static_cast
здесь вместо этого, чтобы показать, что компилятор в порядке с этим приведением. В C вы бы просто приводили префикс типа в круглых скобках. Нет проверки компилятора, чтобы убедиться, что приведение безопасно в C.