Является ли (uint64_t) -1 гарантией получения 0xffffffffffffffff?
Я знаю, что он хорошо определен стандартом C, что (unsigned)-1
должен давать 2 ^ n-1, i. е. целое число без знака со всеми его битами. То же самое касается (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
Ответы
Ответ 1
Да. См. C11 6.3.1.3 Целочисленные и беззнаковые целые числа:
1 Когда значение с целым типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.
2 В противном случае, если новый тип без знака, значение преобразуется путем многократного добавления или вычитания более чем максимального значения, которое может быть представлено в новом типе, пока значение не будет в диапазоне нового типа. 60)
3 В противном случае новый тип будет подписан и значение не может быть представлено в нем; либо результат определяется реализацией, либо генерируется сигнал, определяемый реализацией.
60) Правила описывают арифметику по математическому значению, а не значение данного типа выражения.
применяется случай 2, поэтому -1 уменьшается по модулю 0x10000000000000000, чтобы получить 0xffffffffffffffffff.
Ответ 2
Выражения 1
и -1
имеют тип int
. При преобразовании в uint64_t
принцип, что 2 n добавляется или вычитается до тех пор, пока значение не находится в диапазоне, поэтому результат всегда равен 2 n -1, в этом случай с n = 64. Следовательно, (uint64_t)-1
всегда 2 64 -1..
Выражение (int64_t)-1
оценивается как -1, поэтому одно и то же рассуждение относится к выражению (uint64_t)(int64_t)-1
, которое тоже всегда оценивается как 2 64 -1.
С другой стороны, (unsigned)-1
является положительным значением типа unsigned int
, которое может быть 2 16 -1, 2 32 -1, 2 64 -1 или различные другие значения в зависимости от платформы компиляции. Эти значения могут не вызывать 2 64 -1 при преобразовании в uint64_t
.
Ответ 3
Я предполагаю, что вы пишете (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-разрядные версии для значений с подписью и без знака.
- Для частного случая -1 вы также можете просто написать
UINT64_MAX
.
Ответ 4
Это вопрос, на который можно ответить несколькими строками кода.
#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 для каждого байта в слово/двойное слово и т.д.