Если char c = 0x80, почему printf ("% d \ n", c << 1) выдает -256? - PullRequest
5 голосов
/ 23 октября 2010
#include<stdio.h>
int main(void)
{
  char c = 0x80;
  printf("%d\n", c << 1);
  return 0;
}

В этом случае вывод -256. Если я напишу c << 0, то получится -128.

Я не понимаю логику этого кода.

Ответы [ 5 ]

18 голосов
/ 23 октября 2010

char может быть подписано на вашей платформе, в этом случае 0x80 представляет -128 (при условии дополнения до двух).

Когда char используется в качестве операнда с оператором <<, ему присваивается значение int (по-прежнему -128). Поэтому, когда вы применяете левый сдвиг, вы получаете -256. Технически, смещение отрицательных значений определяется реализацией не определено, но то, что вы видите, является типичным поведением.

6 голосов
/ 23 октября 2010

Уже ваша отправная точка проблематична:

char c = 0x80;

Если (как, по-видимому, в вашем случае) char является типом со знаком, вы присваиваете целочисленную константу 128 типу, который гарантированно будет содержать значения только до 127. Затем ваш компилятор может выбрать какое-то определенное для реализации значение (в моем случае -128) или выдать ошибку диапазона.

Затем вы выполняете сдвиг влево на это отрицательное значение. Это дает неопределенное поведение. Всего у вас есть несколько вариантов реализации, определенных плюс поведение, определяющее результат:

  • подпись char
  • выбор способа конвертации 128 в signed char
  • ширина char
  • знаковое представление int (есть три варианта)
  • выбор того, как реализовать (или нет) сдвиг влево на минус int

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

В заключение некоторые рекомендации:

  • выберите подходящую константу для инициализации переменной
  • не делайте арифметику с простым char
  • не делать сдвиг влево на типах со знаком
3 голосов
/ 23 октября 2010

c назначено 0x80. Предполагая 8-битные байты, его значение в двоичном представлении равно 10000000. Очевидно, на вашей платформе char является подписанным типом. Так, 0x80 (т.е. 10000000) соответствует -128.

Когда << применяется к значению char, оно повышается до int, и знак сохраняется. Таким образом, при сдвиге влево с 32-разрядными целыми числами он становится 11111111111111111111111100000000 (дополнение к двум), что равно -256.

1 голос
/ 23 октября 2010

Просто примечание.С точки зрения снизу вверх, побитовое смещение (и маскирование) основано на длине слова архитектуры (выраженной в битах).Длина слова варьируется от архитектуры к архитектуре.

См. Эту вики-страницу для определения длины слова по архитектуре

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

См. эту вики-страницу для интересных диаграмм сдвига битов

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

К счастью, сегодня у нас есть 8, 16, 32 и 64-битные длины слов,и исключительно 8-битная длина символов.Во времена древних вычислений архитектура могла иметь длину слова 12, 15 или 23 бита (и т. Д., Ad nauseum).

0 голосов
/ 23 октября 2010

Интересно, почему ваш компилятор не жалуется на предупреждение, что 0x80 не умещается в char, который на вашей платформе может представлять только значения от -0x80 до 0x7F.

Попробуйте этот фрагмент кода:

 #include <stdio.h>
 #include <limits.h>
 #include <stdlib.h>

 int main() {
      printf("char can represent values from %d to %d.\n", CHAR_MIN, CHAR_MAX);
      return EXIT_SUCCESS;
 }

Ваша ситуация называется OVERFLOW.

...