В стандарте языка указано, что для для представления целочисленных типов (integral types) допускается использовать один из трёх способов:
В связи с чем возникает несколько вопросов:
В настоящее время где-нибудь вообще используется представление целых чисел, отличное от 2's complement?
Допускает ли стандарт, чтобы в рамках одной и той же реализации использовались различные представления для целочисленных типов? Например, чтобы char был 2's complement, а int был 1's complement?
Как различные представления целых согласуются с битовыми операциями? Например, пусть есть такой код:
unsigned char a = 1;
a = ~a;
И пусть в некоторой реализации используется восьмибитный char и шестнадцатибитный int, и для представления целых знаковых чисел применяется 1's complement, т.е. отрицательные значения мы храним в обратном коде. У меня сложилось впечатление, что для вычисления значения переменной a могут быть проделаны следующие преобразования:
a подвергнется целочисленному расширению до int, ибо операнд битового отрицания, согласно стандарту, может быть расширен. Т.е. набор битов 00000001 преобразуется в 00000000 00000001.11111111 11111110.-1.-1 будет приведено к беззнаковому целому unsigned char по модулю 256. Т.е. переменная a примет значение 255, которое кодируется следующей последовательностью бит: 11111111. Что несколько странно, ибо используя битовое отрицание к последовательности бит 00000001, я ожидаю получить 11111110, а не 11111111. Допускается ли стандартом такая работа битового отрицания, или приведённый выше пример в корне не верен?
Говорят, что системы типа UNISYS 2200 (1, 2) еще встречаются кое-где в реальной жизни. Штатный компилятор С в этих системах использует 1's-complement представление.
См. также
Are there any non-twos-complement implementations of C?
Exotic architectures the standards committees care about
Прямого запрета на смешение представлений в стандарте нет. Да и нет в таком запрете никакого смысла, ибо никакой пользы он бы не принес - стандарт и так не гарантирует вам никакой "синхронизированности" между представлениями разных знаковых целых типов. Например, каждый целый тип может обладать своим количеством и расположением padding-битов.
Ваша интерпретация работы оператора ~ совершенно верна. Да, такое поведение допускается. Да, поведение "малых" беззнаковых типов в таких контекстах существенно зависит от выбранного представления знаковых типов. Точнее, побитовое поведение знаковых типов очевидным образом зависит от выбранного представления, а "малые" беззнаковые типы уже становятся заложниками этого поведения через посредство integral promotions.
Можно также отметить, что применение ~ к знаковому значению может породить и trap representation.
(Свою роль тут также сыграл сделанный на этапе формирования C89/90 выбор в пользу выполнения расширения беззнаковых типов к знаковому int, как описано в Rationale к стандарту (см. вторую часть моего ответа здесь), вопреки имевшей место тогда юниксовой традиции расширения беззнаковых типов к unsigned int.)
Сборка персонального компьютера от Artline: умный выбор для современных пользователей