Переполнение подписанных/неподписанных назначений и его результаты

Я читаю книгу Страуструпа «Язык программирования С++, 4-е издание», и у меня есть три вопроса относительно переполнения заданий (особенно для подписанных/беззнаковых символов, как показано в книге). Во-первых, согласно стандартному пункту 5/4:

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

(За исключением случаев, когда целевая переменная не имеет знака - в этом случае результат четко определен). Но относится ли это определение и к назначениям?

Потому что, на мой взгляд, в книге много противоположных утверждений, все в главе 6. Первое соответствует вышеупомянутому абзацу, а следующие комментарии — нет:

Переменные трех типов char можно свободно назначать друг другу. Однако присвоение слишком большого значения signed char по-прежнему не определено. Например:

void g(char c, signed char sc, unsigned char uc)
{
    c = 255; //implementation-defined if plain chars are signed and have 8 bits
    c = sc; //OK
    c = uc; //implementation-defined if plain chars are signed and if uc's value is too large
    sc = uc; //implementation-defined if uc's value is too large
    uc = sc; //OK: conversion to unsigned
    sc = c; //implementation-defined if plain chars are unsigned and if c's value is too large
    uc = c; //OK: conversion to unsigned
}

Первый вопрос: поскольку присвоение слишком большого значения является UB, то почему в комментариях говорится, что это определяется реализацией?

Далее у нас есть следующий пример:

Чтобы быть конкретным, предположим, что char составляет 8 бит:

signed char sc = -160;
unsigned char uc = sc; //uc == 116 (because 256-160==116)
cout << uc; //print 't'

Второй вопрос: помимо того факта, что первое задание якобы UB, какую именно формулу использовал автор, чтобы получить 116? В моем тесте uc получил значение 96.

И последняя цитата:

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

signed char sc = 1023; //implementation-defined

Правдоподобные результаты 127 и -1.

Третий вопрос: опять же, помимо того факта, что это противоречит тому, что было сказано ранее о UB, почему возможные результаты 127 и -1? Я предполагаю, что это как-то связано с дополнением до единиц и до двух, но какие точные формулы используются?


person Quentin    schedule 31.07.2013    source источник
comment
Не могли бы вы предоставить главу с вышеупомянутым текстом, чтобы можно было проверить это в версии PDF.   -  person SChepurin    schedule 31.07.2013
comment
@SChepurin, это глава 6.   -  person Quentin    schedule 31.07.2013
comment
И номер раздела? Я не думаю, что книга резко изменилась по сравнению с третьим изданием, поскольку там гл. 6 — Выражения и операторы.   -  person SChepurin    schedule 31.07.2013
comment
@SChepurin На самом деле это 6.2.3.1 (глава 6 — Типы и объявления) и 10.5.2.1 (глава 10 — Выражения).   -  person Quentin    schedule 31.07.2013
comment
Третье издание - глава 4 Типы и объявления и ничего похожего на то, о чем вы спрашиваете.   -  person SChepurin    schedule 31.07.2013


Ответы (1)


1) Реализация определена как «класс UB» - другими словами, это все еще UB, просто реализация отвечает за объяснение того, как она работает, а не «вы вообще не можете полагаться на эту операцию». Таким образом, компилятору по-прежнему разрешено взорвать ваш компьютер, если вы назначите значения char вне допустимого диапазона. Но реализация также может определить, что «она обрезает его до 8-битного эквивалентного значения».

2) 256 - 160 = 96 на моем калькуляторе. Держу пари, что и на твоем тоже. Может у автора был другой калькулятор? Или это одна из тех вещей, которые были изменены в последнюю минуту с -150 на -160, но забыли изменить конечный результат.

3) Поскольку это «определяется реализацией», это может оказаться почти чем угодно. Поскольку значение равно 0x3ff в шестнадцатеричном формате, мы можем представить себе либо 0xff, либо 0x7f в качестве правдоподобных значений, в зависимости от того, как это решит реализация. Я ожидаю, что БОЛЬШИНСТВО компиляторов будет использовать значение 0xff.

person Mats Petersson    schedule 31.07.2013
comment
2) Хорошо, но зачем вычитать из 256? 3) Почему 0x7f? - person Quentin; 31.07.2013
comment
2) Потому что это то, что происходит, когда вы обрезаете число. 3) Потому что это максимальное положительное число, которое может содержать тип, другими словами, значение, которое вы получите, если не измените его знак в значении со знаком. Можно представить, что это разумный ответ на слишком большое значение, точно так же, как получение -1... В конце концов, это разрешено. Вряд ли это будет 124 или 75, но технически они тоже допустимы, если разработчик решил задокументировать это таким образом. - person Mats Petersson; 31.07.2013