2016-07-05 13 views
1

я не могу найти где-нибудь в стандарте C, что оправдывает следующее:Есть ли механизм в C11 для обеспечения ожидаемых типов отрицательных целых констант?

int n = -0x80000000 // set n to -2^31 

Предположим, реализация которых ИНТ составляет 32 бита. Очевидная проблема состоит в том, что целочисленная константа имеет тип unsigned int, согласно таблице в стандарте проекта комитета в пункте 6.4.4.1 пункта 5. Затем отрицание рассчитывается в соответствии с пунктом 6.5.3.3, пункт 3:

Результат унарного оператора является отрицательным его (продвинутым) операндом. Целые рекламные акции выполняются в операнде, и результат имеет продвинутый тип.

Выполнение целочисленных рекламных акций не изменяет тип (unsigned int остается без знака int). Затем берется отрицание. Так как результат сохраняет продвинутый тип, он уменьшается по модулю 2^32, производя 2^31 (так что отрицание не имеет эффекта).

Назначение отказа от значения диапазона к типу Int покрывается следующее:

6.3.1.3 Подпись и целые числа без знака

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

2 В противном случае, если новый тип без знака, значение преобразуется путем многократного добавления или вычитания больше, чем максимальное значение, которое может быть представлено в новом типе , пока значение находится в диапазоне от нового типа один , 60)

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

Итак, в конце мы получим реализацию, определенную реализацией, когда мы пытаемся присвоить действительное значение int объекту int (предполагая дополнение 2 без представления ловушки).

Ниже будет стандартной гарантированно иметь ожидаемый результат:

int n = -(long long)0x80000000 // set n to -2^31 

Таким образом, вы действительно должны бросить до правомерно сделать присваивание диапазона, или я что-то отсутствует?

+0

Примечание о стандарте, гарантированном ожидаемом результате, -> отзыв 'int' может быть 16-бит. – chux

+0

Да, я предполагаю 32-битный int. – Kyle

+1

Проблема на самом деле проста, не используйте hex для описания значений, которые могут стать отрицательными. Значения шестнадцатеричных значений являются хорошим инструментом, когда вы хотите описать битовые шаблоны, которые будут использоваться как маски в битовых операциях и так далее. Когда вы хотите описать значения для подписанной арифметики, представление битов должно быть неактуальным. –

ответ

2

Боюсь вас заметить Назначение невписываемого значения для типа Int покрыта следующим образом: не относится к

unsigned int n = -0x80000000 // set n to -2^31 

n имеет тип unsigned int и значение 2^31 не вне диапазона для 32 бит unsigned int.

EDIT: так как вы изменили этот вопрос и сделали nint, тем применяется для 32 бит и менее int с и комментарий неправилен для больших int типов:

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

EDIT: второй фрагмент кода int n = -(long long)0x80000000 // set n to -2^31 является правильным, поскольку значение -2147483648 действительно помещается в 32 бита int.

Обратите внимание, что правильный способ инициализации n к этому значению является это (предполагая 32 бит Интс):

int n = -2147483647 - 1; // set n to -2^31 

Этот вариант используется в стандартных заголовков <limits.h> и <stdint.h>. Он не использует броски, поэтому ответ на последний вопрос: no, вам действительно не нужно бросать, чтобы действительно сделать задание в диапазоне от -2^31 до 32 бит int.

+0

Хороший улов, это было случайно, я исправил его, чтобы ввести int. – Kyle

+0

@Kyle: второй фрагмент кода также использует 'unsigned int'. Вычисление отличается, но результат идентичен и правилен, если 'unsigned int' имеет не менее 32 битов значений. – chqrlie

+0

Спасибо, я исправил и второй фрагмент. – Kyle

1

Если INT_MAX является 0x7fffffff, то как шестнадцатеричный буквального, 0x80000000 имеет тип unsigned int или больше, и применяя - к ней безопасно. Это было бы неверно для десятичных литералов. Если INT_MAX были больше 0x7fffffff, тогда отрицание уже было бы безопасным, как int.

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

+0

Да, применение - безопасно, но вопрос заключается в присвоении результата типу int. – Kyle

+0

Мой ответ здесь не имеет смысла без коррекции. Я просто сделанный на вопрос. Предполагается, что n имеет тип int, а не unsigned int. – Kyle

+0

* На практике это всегда определяется * ... на самом деле! Современные компиляторы выполняют контр-интуитивные оптимизации в присутствии потенциального неопределенного поведения, которое может сделать это предположение терпит неудачу. – chqrlie

1

int n = -(long long)0x80000000

вам действительно нужно, чтобы бросить до правомерно сделать присваивание диапазона, или я что-то отсутствует?

Как насчет: int n = -0x80000000LL;

Без литья.

+0

Хорошая точка, но очень похожая проблема по-прежнему существует при назначении -2^63 длинному длинному, чего нельзя избежать. Можно было бы сделать (2^63-1) -1, но все же громоздко. Во всяком случае проблема заключается в ненадежности «-», что, по-видимому, я правильно понимаю, что это ненадежно. – Kyle

+0

Это то, что вы получаете для разработки языка программирования, в котором '-' является оператором, а не частью числовой константы. Lisp: '(- 123)' --- унарный минус применяется к '123'; '(+ -123 1)' --- добавить константу '1' в' -123'. Если у вас есть hex, знак идет после префикса '# x-FF' ---' -255' десятичного числа. – Kaz

0

Просто сделайте int n = INT_MIN.

Или если вам необходимо int n = -2147483648 (то же, что и литье до длинного).

Смешивание шестнадцатеричных литералов и отрицательных чисел в C обычно плохое, так как шестнадцатеричные литералы предполагают определенный формат подписывания.

мы получаем реализацию определяется поведение при попытке присвоить действительное значение INT к Int объекту

Если вы обеспокоены реализации определяется поведением, то почему вы используете наивный int типа и не int32_t?

int имеет определенный размер и формат подписанности, который является корнем проблемы. int32_t гарантированно будет дополнять 32 бита.

+0

Спасибо, но есть другие проблемы, которые являются трудными из-за этой проблемы, это просто упрощенный пример, чтобы указать на вопрос. В тех других случаях не так легко обойти. Например, макрос, который отрицает аргумент типа целочисленного типа, в принципе ненадежен, если из-за этого он должен принимать подписанные типы. – Kyle

+0

@Kyle Макрос, который отрицает аргумент, но принимает неподписанные типы в качестве аргумента, действительно ошибочен и опасен. Такие макросы предпочтительно следует переписывать в функции, поэтому существует повышенная безопасность типов. – Lundin

0

Относительное поведение подшитых и неподписанных типов в C является беспорядком, который возник, когда Комитет C89 попытался сформулировать правила, которые были бы максимально согласованы с поведением ранее существовавших компиляторов, которые часто несовместимы друг с другом ,

На самом деле нет никакого переносного способа гарантировать, что выражения, включающие целочисленные константы, будут работать ожидаемым образом, за исключением того, что они будут вручную защищены тем, что они гарантированы достаточно большим, чтобы удерживать все промежуточные значения; если вам не нужно ничего, кроме 64 бит, достаточно «длинного длинного» или «беззнакового длинного»; значения могут быть принудительно привязаны к этим типам с использованием суффикса UL или ULL.

 Смежные вопросы

  • Нет связанных вопросов^_^