Я знаю, что это хорошо определено стандартом C, что (unsigned)-1
должен дать 2 ^ n-1, т.е. е. целое число без знака со всеми установленными битами. То же самое касается (uint64_t)-1ll
, Тем не менее, я не могу найти что-то в стандарте C11, который определяет, как (uint64_t)-1
интерпретируется.
Итак, вопрос: есть ли какая-либо гарантия в стандарте C, что из следующего верно?
(uint64_t)-1 == (uint64_t)(unsigned)-1 //0x00000000ffffffff
(uint64_t)-1 == (uint64_t)(int64_t)-1 //0xffffffffffffffff
Да. См. C11 6.3.1.3 Целые числа со знаком и без знака:
1 Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.
2 В противном случае, если новый тип является беззнаковым, значение преобразуется путем многократного сложения или вычитания значения, превышающего максимальное значение, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа.60)
3 В противном случае новый тип подписывается и значение не может быть представлено в нем; либо результат определяется реализацией, либо определяется сигнал реализации.
60) Правила описывают арифметику математического значения, а не значения данного типа выражения.
Случай 2 применяется, поэтому -1 уменьшается по модулю 0x10000000000000000, чтобы получить 0xffffffffffffffff.
Выражения 1
а также -1
иметь тип int
, Когда преобразовано в uint64_t
Принцип 2N складывается или вычитается, пока не будет применено значение в диапазоне, поэтому результат всегда равен 2N-1, в этом случае с n = 64. Следовательно, (uint64_t)-1
всегда 264-1 ..
Выражение (int64_t)-1
оценивается как -1, поэтому то же самое рассуждение применимо к выражению (uint64_t)(int64_t)-1
, который тоже всегда оценивается в 264-1.
С другой стороны, (unsigned)-1
является положительным значением типа unsigned int
это может быть 216-1, 232-1, 264-1 или различные другие значения в зависимости от платформы компиляции. Эти значения могут не дать 264-1 при конвертации в uint64_t
,
Я предполагаю, что ты пишешь (uint64_t)-1
вместо -1ULL
потому что вы не хотите делать предположения о размере unsigned long long
? Если так, это хорошо. Тем не менее, есть альтернатива, которая еще не была упомянута (и на самом деле не отвечает на ваш вопрос), но может избавить вас от многих беспокойств, обойдя вопрос стороной:
Хорошая привычка всегда использовать UINT64_C(x)
вместо (uint64_t)x
, Это макрос, определенный в <stdint.h>
который автоматически добавляет U
, UL
, или же ULL
по мере необходимости. Таким образом, UINT64_C(-1)
решает либо -1U
, -1UL
, или же -1ULL
в зависимости от вашей цели. Это гарантированно всегда работает правильно.
Обратите внимание, что (uint64_t)x
на самом деле даже не работает правильно в целом. Например,
(uint64_t)2147483648 // RISKY
генерирует предупреждение на некоторых компиляторах, потому что значение 2147483648 (2 ^ 31) слишком велико, чтобы поместиться в 32-разрядное целое число, а следующее даже удаленно не работает:
(uint64_t)1000000000000000000 // RISKY
Однако, если вы используете UINT64_C()
вместо этого тогда все золотое
UINT64_C(2147483648) // GOOD
UINT64_C(1000000000000000000) // GOOD
UINT64_C(-1) // GOOD
Заметки:
_C
суффикс означает «постоянный».<stdint.h>
Существуют также 8-, 16- и 32-разрядные версии для значений со знаком и без знака.UINT64_MAX
,На этот вопрос можно ответить с помощью нескольких строк кода.
#include <stdio.h>
main()
{
int x;
unsigned int y;
x = -1;
printf("\n0x%08x", x);
y = (unsigned int) -1;
printf("\n0x%08x", y);
}
Запуск этого кода на компиляторе Eclipse / Microsoft C приводит к:
0xffffffff
0xffffffff
Подобная программа может показать вам, что uint64_t
производит.
Наконец, если вы понимаете, как компьютеры используют числа дополнения 2 для добавления чисел, то вы поймете, что -1 для слов любого количества битов (8, 32, 64 и т. Д.) Всегда все ff для каждого байта в слове / двойное слово и т. д.