Неявные типы приводятся в выражениях с битовыми сдвигами - PullRequest
1 голос
/ 15 сентября 2011

В приведенном ниже коде почему 1 байт anUChar автоматически преобразуется в 4 байта для получения желаемого результата 0x300 (вместо 0x0, если anUChar останется размером 1 байт):

unsigned char anUChar = 0xc0; // only the two most significant bits are set
int anInt = anUChar << 2; // 0x300 (correct)

Но в этом коде, направленном на 64-битный результат, автоматического преобразования в 8 байтов не происходит:

unsigned int anUInt = 0xc0000000; // only the two most significant bits are set
long long aLongLong = anUInt << 2; // 0x0 (wrong, means no conversion occurred)

И только размещение явного типакастовые работы:

unsigned int anUInt = 0xc0000000;
long long aLongLong = (long long)anUInt << 2; // 0x300000000 (correct)

И самое главное, будет такое же поведение в программе, нацеленной на 64-битные машины?

Кстати, какой изэти два наиболее правильные и портативные: (type)var << 1 или ((type)var) << 1?

Ответы [ 4 ]

2 голосов
/ 15 сентября 2011

Преобразование происходит . Проблема в том, что выражение anUInt << 2 представляет собой unsigned int, поскольку anUInt представляет собой unsigned int.

Преобразование anUInt в long long (на самом деле это преобразование в данном конкретном случае) является правильным решением.

Ни (type)var << 1, ни ((type)var) << 1 не являются более корректными или переносимыми, поскольку приоритет оператора строго определен Стандартом. Тем не менее, последний, вероятно, лучше , потому что его легче понять людям, которые небрежно смотрят на код. Другие могут не согласиться с этим утверждением.

EDIT:

Обратите внимание, что в вашем первом примере:

unsigned char anUChar = 0xc0; 
int anInt = anUChar << 2; 

... результат выражения anUChar << 2 - это не unsigned char, как вы могли бы ожидать, а int из-за интегрального продвижения.

Операнды operator<< являются целочисленными или перечислимыми (см. Стандарт 5.8 / 1). Когда вызывается бинарный оператор, который ожидает операнды арифметического или перечислимого типа, компилятор пытается преобразовать оба операнда в один и тот же тип, чтобы выражение могло привести к общему типу. В этом случае интегральное продвижение выполняется для обоих операндов (5/9). Когда unsigned char принимает участие в интегральном повышении, оно будет преобразовано в int, если ваша платформа может вместить все возможные значения unsigned char в int, в противном случае оно будет преобразовано в unsigned int (4.5 /1).

2 голосов
/ 15 сентября 2011

char всегда повышается до int во время арифметики. Я думаю, что это определенное поведение в стандарте C.

Однако int автоматически не повышается до long long.

При в некоторых ситуациях некоторые компиляторы (Visual Studio) на самом деле предупреждают вас об этом, если вы попытаетесь сместить влево меньшее целое число и сохранить его в большем.

Кстати, какой из двух наиболее правильный и переносимый: (type)var << 1 или ((type)var) << 1?

Оба хороши и портативны. Хотя я предпочитаю первый, так как он короче. Кастинг имеет более высокий приоритет, чем смена.

1 голос
/ 15 сентября 2011

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

На 64-битных машинах ваш второй фрагмент кода будет в равной степени проблематичным, поскольку типы int обычно также имеют 32-битную ширину. (Между x86 и x64 long long int обычно всегда 64 и int 32 бита, только long int зависит от платформы.)

(В духе C ++ я бы записал преобразование как (unsigned long long int)(anUInt) << 2, напоминающее синтаксис конструктора преобразования. Первый набор скобок - просто потому, что имя типа состоит из нескольких токенов.)

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

1 голос
/ 15 сентября 2011

Из-за целочисленных повышений . Для большинства операторов (например, <<), char операнды сначала повышаются до int.

Это не имеет никакого отношения к тому, куда идет результат расчета. Другими словами, тот факт, что ваш второй пример присваивает long long, не влияет на продвижение операндов.

...