C: Что происходит (подробно) в x = ~ x, если x имеет тип char? - PullRequest
0 голосов
/ 02 июля 2018

Если у нас есть следующий код:

char x = -1;
x =~x;

На платформе x86 с компилятором MS VS (который частично поддерживает C99) - что происходит в деталях, когда он работает?

Насколько мне известно, происходит следующее (пожалуйста, исправьте меня, если я ошибаюсь):

  • x присваивается значение -1, которое представлено битовой комбинацией 0xff, поскольку символ представлен одним байтом.
  • Оператор ~ переводит x в int, то есть он внутренне работает с битовой комбинацией 0xffffffff.
  • Результат оператора ~ равен 0x00000000 (типа int).
  • Для выполнения назначения применяются целочисленные повышения (главным образом). Так как в нашем случае операнд справа является int, преобразование не происходит. Операнд с левой стороны преобразуется в int. Результат назначения - 0x00000000.
  • В качестве побочного эффекта левой стороне присвоения присваивается значение 0x00000000. Поскольку x имеет тип char, существует другое неявное преобразование, которое преобразует 0x00000000 в 0x00.

Есть так много вещей, которые на самом деле случаются - я нахожу это несколько запутанным. В частности: правильно ли мое понимание последнего неявного преобразования (из int в char)? Что произойдет, если результат присваивания не может быть сохранен в символе?

Ответы [ 3 ]

0 голосов
/ 02 июля 2018

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

В любом случае, битовая комбинация для 8-битного 2-значного комплемента действительно равна 0xFF независимо от его подписи. Но в случае, если char подписано, целочисленное продвижение сохранит знак, и у вас все равно будет значение -1, двоичное 0xFFFFFFFF на 32-битном компьютере. Но если бы char был без знака, -1 был бы преобразован в 255 после назначения, и целочисленное продвижение дало бы 255 (0x000000FF). Таким образом, вы получите другой результат.

Что касается целочисленного продвижения по номеру ~, у него есть только один оператор справа, и этот оператор продвигается.

Наконец, вы присваиваете результат обратно char, и результат снова будет зависеть от подписи. При присвоении от int до char у вас будет неявное «преобразование lvalue». Результат определяется реализацией - скорее всего вы получите младший байт int.


Из этого мы можем узнать:

  • Никогда не используйте char для хранения целочисленных значений или арифметики. Используйте его только для хранения символов. Вместо этого используйте uint8_t.
  • Никогда не выполняйте побитовую арифметику с операндами, которые потенциально подписаны или были подписаны без вывода сообщений посредством неявного продвижения.
  • Оператор ~ особенно опасен, если только операндом не является unsigned int или больший тип без знака.
0 голосов
/ 02 июля 2018

Насколько мне известно, происходит следующее (пожалуйста, исправьте меня, если я ошибаюсь):

x присваивается значение -1, которое представлено битовой комбинацией 0xff, поскольку символ представлен одним байтом.

1 является целочисленной константой типа int. - отрицает это до -1 и остается int. -1 присваивается char x. Если это char является со знаком , тогда x принимает значение -1. Если это char равно без знака , x принимает значение CHAR_MAX, которое также UCHAR_MAX. "битовый паттерн 0xff" здесь пока не актуален.

Оператор ~ переводит x в int, то есть он внутренне работает с битовой комбинацией 0xffffffff.

x повышается до int (или unsigned на редких машинах, где CHAR_MAX == UINT_MAX - мы будем игнорировать это). int составляет не менее 16 бит. Значение -1, когда оно закодировано как чрезвычайно общее дополнение 2, является шаблоном со всеми 1 битами. (Возможно другое кодирование - мы тоже это проигнорируем). Если x имеет значение UCHAR_MAX, то x будет иметь битовую комбинацию 00...00 1111 1111 - при условии 8-битового char. Возможны другие значения ширины - другое, что мы будем игнорировать.

Результат оператора ~ равен 0x00000000 (типа int).

Да, (кроме случаев CHAR_MAX == UINT_MAX, в этом случае это unsigned и значение 11 ... 11 0000 0000).

Для выполнения назначения применяются целочисленные повышения (главным образом). Так как в нашем случае операнд справа является int, преобразование не происходит. Операнд с левой стороны преобразуется в int. Результат выполнения задания - 0x00000000.

Здесь нет целочисленных рекламных акций из-за назначения. Акции уже произошли из-за ~. Произойдет смена типа, назначив int для char. Это не продвижение по службе. Результат имеет тип char. Как часть сужения, значение 0 не вызывает проблем с диапазоном и приводит к значению 0 и типу char. Значение 11 ... 11 0000 0000 будет проходить через поведение, определенное реализацией, и, скорее всего, приведет к значению 0 и, конечно, к типу char.

Если бы код был (x =~x) + 0, то char (x =~x) был бы повышен до int до добавления.

В качестве побочного эффекта левой стороне присвоения присваивается значение 0x00000000. Поскольку x имеет тип char, существует другое неявное преобразование, которое преобразует 0x00000000 в 0x00.

Указано в предыдущем.

Что произойдет, если результат присваивания не может быть сохранен в символе?

Это поведение, определяемое реализацией, значение которого сохраняется. Это может включать (редко) поднятие исключения.


Битовая маскировка и манипулирование лучше всего обрабатываются с использованием беззнаковых типов и математики.

0 голосов
/ 02 июля 2018

Действительно ~x - это тип int.

Преобразование обратно в char четко определено, если char равно unsigned. Конечно, это также четко определено, если значение находится в диапазоне, поддерживаемом char.

Если char равен signed, то преобразование ~x в char определяется реализацией, с возможностью повышения сигнала, определенного реализацией.

В вашем случае у вас есть платформа с дополнением 2 int и дополнением 2 char, и поэтому ~x наблюдается как 0.

Обратите внимание, что MSVC не полностью поддерживает любой C стандарт и не претендует на это.

...