Подтвердить что ты не робот

Тип целочисленных литералов и ~ в C

Я начинаю C, и меня смущает следующий пример, найденный в книге ответов C.

Один из способов найти размер unsigned long long в вашей системе - это ввести:

printf("%llu", (unsigned long long) ~0);

Я не знаю, почему этот синтаксис работает?

В моей системе int - 32 бита, а long long - 64 бита.
Я ожидал, что, поскольку 0 является константой целочисленного типа, ~0 вычисляет отрицание 32-битного целого числа, которое затем преобразуется в unsigned long long оператором литья. В результате это должно дать 2 32 - 1.

Как бы то ни было, похоже, что оператор ~ уже знает, что он должен действовать на 64 бита?
Компилятор интерпретирует эту инструкцию как printf("%llu", ~(unsigned long long)0);? Это не звучит правильно, так как литье и ~ имеют одинаковый приоритет.

4b9b3361

Ответ 1

Как-то похоже, что оператор ~ уже знает, что он должен действовать на 64 бита?

Это не оператор ~, это литье. Вот как конверсия целых чисел выполняется в соответствии со стандартом:

6.3.1.3 Целочисленные и беззнаковые целые числа

  • Когда значение с целым типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.
  • В противном случае, если новый тип без знака, значение преобразуется путем многократного добавления или вычитания более чем максимального значения, которое может быть представлено в новом типе, пока значение не будет в диапазоне нового типа.
  • В противном случае новый тип подписан и значение не может быть представлено в нем; либо результат определяется реализацией, либо генерируется сигнал, определяемый реализацией.

Значение подписанных int ~0 соответствует -1 для систем с двумя дополнительными представлениями отрицательных значений. Он не может быть представлен unsigned long long, поэтому первая маркерная точка не применяется.

Используется вторая маркерная точка: новый тип без знака, поэтому MAX из unsigned long long добавляется к -1 один раз, чтобы получить результат в диапазоне unsigned long long. Это имеет тот же эффект, что и знак расширения -1 до 64 бит.

Ответ 2

0 имеет тип int, а не unsigned int. Таким образом, ~0 (на машинах, которые используют два целочисленных представления, которые используются сегодня), равно -1, а не 2 32 - 1.

Предполагая, что 64-разрядный unsigned long long, (unsigned long long) -1 равен -1 по модулю 2 64 который равен 2 64 - 1.

Ответ 3

0 является int

~0 по-прежнему является int, а именно значением -1.

Отбрасывание int в unsigned long long означает просто совпадение с типом, который printf ожидает с преобразованием llu.

Однако значение -1, расширенное без знака long long, должно быть 0xffffffff для 4 байтов int и 0xffffffffffffffff для 8 байтов int.

Ответ 4

Согласно проекту Комитета N1570:

6.5.3.3 Унарные арифметические операторы

  1. Результатом оператора ~ является побитовое дополнение его (продвинутый) операнд (то есть каждый бит в результате устанавливается, если и только если соответствующий бит в преобразованном операнде не установлен). целые рекламные акции выполняются в операнде, и результат продвинутого типа. Если продвигаемый тип является "неподписанным типом", выражение ~ E эквивалентно максимальному значению, представленному в этом введите минус E ".

§6.2.6.2 Язык 45:

(одно дополнение). Какое из них относится к определению реализации, равно как и значение со знаковым битом 1 и всеми битами значений 0 (для первого двух) или со знакомным битом и всеми битами значений 1 (для одного дополнения), представляет собой ловушку или нормальное значение. В случае знака, величины и дополнения, если это представление является нормальным значением, оно называется отрицательным нулем.

Следовательно, поведение кода:

printf("%llu", (unsigned long long) ~0);

На некоторых машинах реализована реализация и не определена - не в соответствии с ожидаемым — зависят от внутренних представлений целых чисел в машине.

В соответствии с разделом 6.5.3.3, утвержденным способом написания кода будет:

printf("%llu", (unsigned long long) ~0u);

Кроме того, тип ~0u - это unsigned int, где при отправке его в unsigned long long int, для которого строка формата llu. Чтобы напечатать ~0u с помощью строки формата %u.

Чтобы узнать основную концепцию литья типов, вы можете читать: Что конкретно представляет собой тип в C/С++?