2014-11-30 6 views
1

Я понимаю, что присваиваю подписанное значение int больше, чем оно может обрабатывать. Также я должен использовать %d для подписанных и %u для неподписанных. Точно так же я не должен присваивать значение -ve без знака. Но если я выполняю такие задания и использую printf, как показано ниже, я получаю результаты, показанные ниже.Неподписанные и подписанные int и printf

Мое понимание состоит в том, что в каждом случае число преобразованных двоичных представлений его двоих представляет собой то же самое для -1 или 4294967295. Вот почему %u для подписанных отпечатков 4294967295 путем игнорирования -ve левый бит. При использовании% d для подписанного int он использует самый левый бит как флаг -ve и печатает -1. Аналогично %u для неподписанных принтов без знака, но %d заставляет обрабатывать номер как подписанный и, таким образом, печатает -1. Это верно?

signed int si = 4294967295; 
unsigned int ui = 4294967295; 

printf("si = u=%u d=%d\n", si, si); 
printf("ui = u=%u d=%d\n", ui, ui); 

Выход:

si = u=4294967295 d=-1 
ui = u=4294967295 d=-1 

signed int si = -1; 
unsigned int ui = -1; 

printf("si = u=%u d=%d\n", si, si); 
printf("ui = u=%u d=%d\n", ui, ui); 

Выход:

si = u=4294967295 d=-1 
ui = u=4294967295 d=-1 
+1

Обратите внимание, что мой ответ на этот вопрос объясняет, что [-1, преобразованный в unsigned всегда будет максимальным значением без знака для этого типа] (http://stackoverflow.com/q/22801069/1708801) ... поэтому присваивание ' 1' к значению без знака - это всегда корректное поведение. –

+0

Но просто повторная интерпретация отрицательного интегрального значения как интегрального значения 'unsigned 'не определена.Точно так же, как переинтерпретация слишком большого «неподписанного» интегрального значения как интегрального значения 'signed' не является. – Deduplicator

+0

Вы можете найти «std :: numeric_limits» полезным. Переход от uin32_t к uint64_t гораздо читабельнее. std :: numeric_limits :: max(), становится std :: numeric_limits :: max() и т. д. –

ответ

1

Есть несколько вещей, здесь происходит, давайте начнем с того, что использование неправильного спецификатор формата до printf - неопределенное поведение, которое m что результаты вашей программы непредсказуемы, то, что на самом деле происходит, зависит от многих факторов, включая ваш компилятор, архитектуру, уровень оптимизации и т. д.

Для конверсий с подписью/без знака, которые определены соответствующими стандартами, оба C и C++ сделать его реализации определяется поведение, чтобы преобразовать значение, которое больше, чем хранится в подписанном целочисленного типа, с ++ проект стандарта C:

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

, например gcc решает использовать один и тот же convention as unsigned:

Для преобразования к типу ширины N, значение уменьшается по модулю 2^N, чтобы быть в пределах диапазона типа; сигнал не поднимается.

При назначении -1 на беззнаковое значение как в C и C++ результат всегда будет максимальное значение без знака типа, из проекта C++ стандарт:

Если тип назначения без знака , результирующее значение представляет собой наименьшее значение целое число без знака, сравнимое с исходным целым числом (по модулю 2n, где n равно количество битов, используемых для представления неподписанного типа). [Примечание: в представлении дополнений двоичного кода это преобразование является концептуальным, и нет изменений в битовой схеме (если нет усечения). -end примечание]

Формулировки от C99 легче усваивается:

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

Таким образом, мы имеем следующее:

-1 + (UNSIGNED_MAX + 1) 

результатом которого является UNSIGNED_MAX

Что касается printf и неправильный спецификатор формата мы можем видеть Сформируйте проект C99 стандартный раздел 7.19.6.1Функция fprintf говорит:

Если спецификация преобразования недействительна, поведение равно undefined.248) Если какой-либо аргумент не является правильным типом для соответствующей спецификации преобразования , поведение не определено.

fprintf охватывает printf относительно спецификаторов формата и C++ падает обратно в С по отношению к printf.

+0

@Deduplicator вы имеете в виду в контексте 'printf'? –

+1

@Deduplicator Я добавил цитату, которая делает ее UB в контексте 'printf', и независимо от того, будет ли преобразование работать при оптимизации, компилятор может что-либо сделать с UB. –

+0

Не забывайте формулировку для вариационных функций, в которой говорится, что не имеет значения, было ли передано целочисленное значение 'signed' или' unsigned', если значение представлено в обоих. – Deduplicator

2

Именно поэтому% u для подписанных отпечатков 4294967295 путем игнорирования -ve крайнего левого разряда. При использовании% d для подписанного int он использует самый старший бит как флаг -ve и печатает -1.

В случае без знака, то «левый» или наиболее значимым бит не игнорируется, и не является отрицательным; скорее он имеет значение места из 2 .

В отрицательном случае бит знака не является флагом; вместо этого это бит с величиной места -2 .

В обоих случаях значение целого числа равно сумме место значений всех двоичных цифр (бит) установлен в 1.

Кодирование подписанных значений таким образом, известен как two's complement. Это не единственная возможная кодировка; то, что вы описали, называется значком и величиной, и дополнением - еще одна возможность. Тем не менее, эти альтернативные кодировки редко встречаются на практике, не в последнюю очередь потому, что два дополнения - это то, как арифметика работает на современном оборудовании во всех, кроме, возможно, самых загадочных архитектур.

+0

Отлично, так и не подумал ... – glglgl

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