С ролями и символом подписи

Итак, в последнее время я прочитал о проблеме, касающейся трех различных типов в 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.

Другие вопросы по тегам