Как представить отрицательные значения символов в шестнадцатеричном формате?

Следующий код

char buffer[BSIZE];
...
if(buffer[0]==0xef)
...

Дает предупреждение компилятора "сравнение всегда ложно из-за ограниченного диапазона типов данных". Предупреждение исчезнет, ​​когда я поменяю чек на

if(buffer[0]==0xffffffef)

Это кажется очень нелогичным. Как правильно проверить char против конкретного значения байта в шестнадцатеричном? (кроме как сделать его без знака)

5 ответов

Решение

Чтобы понять почему buffer[0] == 0xef вызывает предупреждение, и buffer[0] == 0xffffffef нет, вы должны точно понимать, что происходит в этом выражении.

Во-первых, == Оператор сравнивает значение двух выражений, а не базовое представление - 0xef является числом 239 и будет сравниваться только равным этому числу; также 0xffffffef это число 4294967279 и будет сравниваться только равным этому.

Там нет разницы между константами 0xef а также 239 в C: оба имеют тип int и то же значение. Если твой char имеет диапазон от -128 до 127, то при оценке buffer[0] == 0xef buffer[0] повышен до int, что оставляет его значение без изменений. Поэтому он никогда не может сравниться равным 0xef так что предупреждение верное.

Тем не менее, существует потенциальная разница между константами 0xffffffef и 4294967279; десятичные константы всегда подписаны, но шестнадцатеричная константа может быть без знака. В вашей системе он имеет неподписанный тип - возможно unsigned int (потому что значение слишком велико для хранения в int, но достаточно маленький, чтобы хранить в unsigned int). Когда вы оцениваете buffer[0] == 0xffffffef, buffer[0] повышен до unsigned int, Это оставляет любое положительное значение без изменений, но отрицательные значения преобразуются путем добавления UINT_MAX + 1 им; с char который имеет диапазон от -128 до 127, повышенные значения находятся в любом из диапазонов от 0 до 127 или от 4294967168 до 4294967295. 0xffffffef лежит в этом диапазоне, поэтому сравнение может вернуть true.


Если вы храните битовые комбинации, а не числа, вы должны использовать unsigned char на первом месте. В качестве альтернативы вы можете проверить битовую комбинацию объекта, указав на него указатель unsigned char *:

if (((unsigned char *)buffer)[0] == 0xef)

(Это, очевидно, удобнее сделать, используя отдельную переменную типа unsigned char *).

Как говорит PaulR, вы также можете использовать buffer[0] == '\xef' - это работает, потому что '\xef' определяется как int константа со значением, что char объект с битовой комбинацией 0xef будет иметь при преобразовании в int; например. в системе дополнения 2s с подписанными символами, '\xef' константа со значением -17.

Что случилось с:

if (buffer[0] == '\xef')

?

Это происходит потому, что buffer содержимое имеет тип char, Делать их unsigned char буду работать:

if ((unsigned char) (buffer[0]) == 0xef)

Четко изложить причину: char является подписанным или неподписанным, определяется реализацией. Если ваш компилятор лечит char как подписано по умолчанию тогда 0xef будет больше, чем максимально возможный signed char (что 127 или 0x7f) и поэтому ваше сравнение всегда будет ложным. Отсюда и предупреждение.

Возможные решения предоставлены другими ответами.

Сделайте это так же, как с любым другим отрицательным числом:

if (buffer[0]== -0x6f)

Но обычно вы хотите использовать unsigned char в качестве типа данных:

unsigned char buffer[BSIZE];
...
if(buffer[0]==0xef)

Причины использовать подписанный символ очень редки. Еще более редкими являются причины использовать "char без спецификации знака", которые могут быть подписаны или не подписаны на разных платформах.

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