2016-05-25 2 views
1

Вот программа, чей сборник выход заставляет меня плакать:2^32 - 1 не входит в uint32_t?

#include <inttypes.h> 

int main() 
{ 
     uint32_t limit = (1 << 32) - 1; // 2^32 - 1 right? 
} 

и здесь выход компиляции:

~/workspace/CCode$ gcc uint32.c 
uint32.c: In function ‘main’: 
uint32.c:5:29: warning: left shift count >= width of type [-Wshift-count-overflow] 
     uint32_t limit = (1 << 32) - 1; // 2^32 - 1 right? 

Я думал, что (1 << 32) - 1 равно 2^32 - 1 и что целые числа без знака на 32 бита варьируются от 0 до 2^32 - 1, разве это не так? Где я неправ?

+0

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

+0

Возможно, проблема заключается в том, что (1 << 32) требует 1 бит больше пространства (33 бита) для хранения значения - поэтому компилятор подходит, поскольку вы работаете с 32-битной переменной. –

+1

с 32-битным int, 2^32 - 1 будет соответствовать (без знака) ... проблема есть, 2^32 не будет, и это часть выражения, которое вы вычисляете 2^32-1 с. Однако по другим причинам вы, скорее всего, получите правильный ответ, так как uint32_t будет по модулю 2^32. – Dmitri

ответ

4

У вас есть две ошибки:

  • 1 имеет тип int, так что вы вычисляя начальное значение как int, а не как uint32_t.
  • Как сказано в предупреждении, операторы сдвига должны иметь аргумент сдвига меньше ширины типа. 1 << 32 is undefined поведение если int не имеет значения 32 бит. (uint32_t)1 << 32 также не определено.

(также, обратите внимание, что 1 << 31 будет определено поведение, а также, если int составляет 32 бита, из-за переполнения)

Поскольку арифметика выполняется по модулю 2^32 в любом случае, более простой способ сделать это просто

uint32_t x = -1; 
uint32_t y = (uint32_t)0 - 1; // this way avoids compiler warnings 
+0

Это похоже на C++ для меня! –

+0

@Ian: Хорошо. :П. – Hurkyl

+0

- это 1-й вид, преобразованный в uint32_t? Как это работает? – Simonlbc

4

Предупреждение верно, самый старший бит в 32-битном номере - это 31-й бит (индекс 0), поэтому наибольший сдвиг перед переполнением составляет 1 << 30 (30 из-за знакового бита). Даже если вы делаете -1 в какой-то момент, результат должен быть сохранен в 1 << 32, и он будет сохранен в int (что в данном случае бывает 32 бита). Следовательно, вы получаете предупреждение.

Если вам действительно нужно, чтобы получить максимум от 32 bit unsigned int вы должны сделать это аккуратный способ:

#include <stdint.h> 

uint32_t limit = UINT32_MAX; 
+1

'1 << 31' также не определено поведение (смещение в знаковый бит) –

+1

... если' int' шире 32 бит. – AnT

1

Компилятор использует int внутри вашего примера при попытке вычислить целевую константу. Представьте себе, что у компилятора не было никакой оптимизации и было создано сборщик для вашей смены. Число 32 будет большим для 32-битной команды int shift.

Кроме того, если вы хотите, чтобы все биты были установлены, используйте ~ 0

+0

'~ 0u' более подходит для бит-возиться, особенно учитывая, что OP хочет получить результат без знака. – AnT

+0

В частности, OP хочет получить результат 'uint32_t' с максимальным значением, поэтому даже' ~ 0u' ошибочно, если 'unsigned int' компилятора меньше 32 бит. –