Усечение int до char — это определено?

unsigned char a, b;
b = something();
a = ~b;

Статический анализатор пожаловался на усечение в последней строке, предположительно из-за того, что b повышается до int до того, как его биты перевернуты, и результат будет иметь тип int.

Меня интересует только последний байт продвигаемого int — если b было 0x55, мне нужно, чтобы a было 0xAA. Мой вопрос: говорит ли спецификация C что-нибудь о том, как происходит усечение, или это определено/не определено реализацией? Гарантируется ли, что a всегда будет присвоено ожидаемое значение, или это может пойти не так на соответствующей платформе?

Конечно, приведение результата перед назначением отключит статический анализатор, но я хочу знать, безопасно ли вообще игнорировать это предупреждение.


person Amarghosh    schedule 04.05.2011    source источник
comment
Я бы сказал, что это ложное предупреждение. Я только что прогнал ваш код через статический анализатор clang, и он не пожаловался. Каков тип возврата something()   -  person JeremyP    schedule 04.05.2011
comment
@Jeremy, это пример кода для иллюстрации сценария. Реальный код похож на mask1[0] = ~mask2[0];, где оба являются массивами типа unsigned char. Видимо, мой статический анализатор не такой умный, как clang :)   -  person Amarghosh    schedule 04.05.2011


Ответы (5)


Усечение происходит, как описано в 6.3.1.3/2 документа Стандарт C99

... если новый тип не имеет знака, значение преобразуется путем многократного добавления или вычитания на единицу больше, чем максимальное значение, которое может быть представлено в новом типе, пока значение не окажется в диапазоне нового типа.


Пример для CHAR_BIT == 8, sizeof (unsigned char) == 1, sizeof (int) == 4

Итак, 0x55 преобразуется в int, в 0x00000055, затем инвертируется в 0xFFFFFFAA и

      0xFFFFFFAA
    + 0x00000100 /* UCHAR_MAX + 1 */
    ------------
      0xFFFFFEAA

    ... repeat lots and lots of times ...

      0x000000AA

или, просто 0xAA, как и следовало ожидать

person pmg    schedule 04.05.2011
comment
Errrr ... пример довольно неправильный, LOL, но вы поняли идею :) - person pmg; 04.05.2011

Стандарт C определяет это для беззнаковых типов:

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

В этом случае, если ваш unsigned char равен 8 битам, это означает, что результат будет уменьшен по модулю 256, а это означает, что если b было 0x55, a действительно станет 0xAA.

Но учтите, что если unsigned char шире 8 бит (что вполне допустимо), вы получите другой результат. Чтобы убедиться, что в результате вы получите переносимое 0xAA, вы можете использовать:

a = ~b & 0xff;

(Побитовое и должно быть оптимизировано на платформах, где unsigned char составляет 8 бит).

Также обратите внимание, что если вы используете тип со знаком, результат определяется реализацией.

person caf    schedule 04.05.2011
comment
Меня беспокоит не переполнение (поскольку здесь используется оператор ~) - может ли усечение 0xFFFFFFAA до char привести к, скажем, 0xFF (msb) вместо 0xAA (lsb)? - person Amarghosh; 04.05.2011
comment
Нет, сокращение всегда выполняется по модулю 2^n, где n — количество битов в символе. Endianness не имеет значения - person Gunther Piez; 04.05.2011
comment
@Amarghosh Нет, он будет использовать младший значащий байт (байты) независимо от порядка следования байтов. - person Klas Lindbäck; 04.05.2011
comment
@Amarghosh: часть цитаты из потому что результат не может быть представлен... все еще актуален. Поскольку значение 0xFFFFFFAA не может быть представлено в виде 8-битного unsigned char, оно будет уменьшено по модулю 256, что приведет к 0xAA. - person caf; 04.05.2011
comment
Использование uint8_t на самом деле не помогает, потому что оно не может существовать, если CHAR_BIT не равно 8. Простое использование #if и #error будет работать так же хорошо. - person R.. GitHub STOP HELPING ICE; 04.05.2011

Он будет вести себя так, как вы этого хотите. Безопасно приводить значение.

person Klas Lindbäck    schedule 04.05.2011

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

Причина этого в том, что ~ для небольших целочисленных переменных является потенциальной ошибкой в ​​​​более сложных выражениях из-за неявного целочисленного продвижения в C. Представьте, что у вас есть такое выражение, как

a = ~b >> 4;

Он не будет сдвигаться нулями, как можно было бы ожидать.

Если ваш статический анализатор настроен на включение MISRA-C, вы, например, получите это предупреждение для каждого оператора ~, потому что MISRA требует, чтобы результат любой операции над небольшими целочисленными типами был явно приведен к ожидаемому типу, в данном случае unsigned char .

person Lundin    schedule 04.05.2011

Возьмем случай с машиной Win32.
Целое число составляет 4 байта, и преобразование его в char приведет к точному результату, как если бы были удалены оставшиеся 3 байта.

Поскольку вы конвертируете char в char, не имеет значения, до чего он продвигается.
~b will add 3 bytes at the left change 0s to 1 and then remove... It does not affect your one right byte.

Одна и та же концепция будет применима для разных архитектур (будь то 16-битная или 64-битная машина).

Предполагая, что это прямой порядок байтов

person Mayank    schedule 04.05.2011
comment
Я пытаюсь описать концепцию здесь... Предположим, что это 32-битная машина... 64-битные машины или другая архитектура не будут иметь никакого значения в том, что касается концепции.3 - person Mayank; 04.05.2011
comment
@Mayank: ради других пользователей SO, которые могут прочитать ваш ответ в будущем, важно, чтобы он не содержал дезинформации. - person Paul R; 04.05.2011
comment
@Paul: Спасибо за комментарий. Я позабочусь об этом в будущем - person Mayank; 04.05.2011
comment
@Mayank: вы, вероятно, могли бы отредактировать приведенный выше ответ, чтобы сделать его более точным и общеприменимым. - person Paul R; 04.05.2011
comment
@Mayank: это немного лучше - я бы удалил последнюю строку о прямом порядке следования байтов, так как это не имеет значения. - person Paul R; 04.05.2011
comment
@Paul: я упомянул порядок следования байтов, чтобы сделать left и right вещи, упомянутые в ответе, более понятными. - person Mayank; 04.05.2011