TL; DR
C унаследовал операторы !
and ~
от другого языка. Оба &&
и ||
были добавлены годами позже другим человеком.
Длинный ответ
Исторически, C развивался из ранних языков B, который был основан на BCPL, который был основан на CPL, который был основан на Algol.
Алгол , прадедушка C ++, Java и C #, определил истину и ложь таким образом, который стал понятен программистам: «Значения истины, которые рассматриваются как двоичное число (истина соответствует 1 и ложь - 0), такой же, как внутренняя интегральная стоимость ». Однако одним из недостатков этого является то, что логическое и побитовое не может быть одной и той же операцией: на любом современном компьютере~0
равно -1, а не 1, и ~1
равно -2, а не 0. (Даже на каком-то шестидесятилетнем мэйнфрейме, где ~0
представляет - 0 или INT_MIN
, ~0 != 1
на каждом процессоре когда - либо делал, и стандарт языка C потребовал его в течение многих лет, в то время как большинство его дочерние языков не удосужилось поддержка знаковой-и величина или one's-комплемента на всех.)
Алгол работал над этим, используя разные режимы и по-разному интерпретируя операторы в логическом и интегральном режимах. То есть побитовая операция была для целочисленных типов, а логическая - для логических типов.
BCPL имел отдельный логический тип, но один not
оператор , как для побитового, так и для логического типа. Способ, которым этот ранний предшественник Си сделал эту работу:
Rvalue истины - это битовый паттерн, полностью состоящий из единиц; R-значение false равно нулю.
Обратите внимание, что true = ~ false
(Вы заметите, что термин rvalue эволюционировал для обозначения чего-то совершенно другого в языках семейства C. Сегодня мы бы назвали это «представлением объекта» в C.)
Это определение позволит логически и поразрядно не использовать одну и ту же инструкцию машинного языка. Если бы C пошел по этому пути, заголовочные файлы по всему миру сказали бы#define TRUE -1
.
Но язык программирования B был слабо типизирован и не имел булевых или даже типов с плавающей точкой. Все было эквивалентомint
в его преемнике, C. Это сделало для языка хорошей идеей определить, что происходит, когда программа использует значение, отличное от true или false, в качестве логического значения. Сначала он определил истинное выражение как «не равное нулю». Это было эффективно на миникомпьютерах, на которых он работал, с нулевым флагом ЦП.
В то время была альтернатива: те же процессоры также имели отрицательный флаг, и значение истинности BCPL было равно -1, так что B мог бы вместо этого определить все отрицательные числа как истинные, а все неотрицательные числа как ложные. (Есть один остаток этого подхода: UNIX, разработанная одними и теми же людьми в одно и то же время, определяет все коды ошибок как отрицательные целые числа. Многие из ее системных вызовов возвращают одно из нескольких различных отрицательных значений при сбое.) Так что будьте благодарны: это могло быть и хуже!
Но определение « TRUE
как» 1
и « FALSE
как» 0
в B означало, что тождество true = ~ false
больше не сохранялось, и оно отбросило строгую типизацию, которая позволяла Algol устранять неоднозначность между битовыми и логическими выражениями. Это потребовало нового логического оператора not, и разработчики выбрали !
, возможно, потому, что уже не равное было !=
, которое выглядит как вертикальная черта через знак равенства. Они не следовали тому же соглашению, что &&
и||
потому что ни один еще не существовал.
Возможно, они должны иметь: &
оператор в B сломан, как задумано. В B и в C, 1 & 2 == FALSE
хотя 1
и 2
являются истинными значениями, и нет никакого интуитивного способа выразить логическую операцию в B. Это была одна ошибка, которую C пытался частично исправить, добавив &&
и ||
, но основной проблемой в то время было наконец, установите короткое замыкание на работу и заставьте программы работать быстрее. Доказательством этого является то, что нет ^^
: 1 ^ 2
истинное значение, хотя оба его операнда истинны, но оно не может извлечь выгоду из короткого замыкания.