TL; DR
C успадкував оператори !
і ~
з іншої мови. І те, &&
і ||
інше було додане роками пізніше.
Довга відповідь
Історично C розвивався з ранніх мов B, що базувався на BCPL, який базувався на CPL, який базувався на Algol.
Алгол , правнук C ++, Java та C #, визначив істинне та хибне таким чином, щоб зрозуміти програмістам інтуїтивно зрозуміло: "значення істини, які, розглядаються як двійкове число (істинне, що відповідає 1, а хибне 0), те саме, що і внутрішня цілісна цінність ”. Однак одним недоліком цього є те, що логічна та бітова не може бути однаковою операцією. На будь-якому сучасному комп’ютері ~0
дорівнює -1, а не 1, і ~1
дорівнює -2, а не 0. (Навіть на якомусь шестидесятирічному мейнфреймі, де ~0
представлено - 0 або INT_MIN
, ~0 != 1
будь-який процесор, коли-небудь виготовлений, і стандарт мови C вимагає цього протягом багатьох років, тоді як більшість його дочірніх мов навіть не намагаються підтримувати знаки та величини або доповнення взагалі.)
Альгол обійшов це, маючи різні режими та інтерпретуючи операторів по-різному в бульному та інтегральному режимі. Тобто, побітова операція була одиницею на цілі типи, а логічна операція - на булевих типах.
BCPL мав окремий булівський тип, але єдиний not
оператор , як для побітових, так і для логічних даних. Те, як цей ранній провісник С зробив цю роботу:
Rvalue істинного - це біт-шаблон, повністю складається з них; Rvalue false - нуль.
Зауважте, що true = ~ false
(Ви помітите, що термін rvalue перетворився на щось зовсім інше в мовах сімейства C. Сьогодні ми би назвали це "представлення об'єкта" в C.)
Це визначення дозволило б логічно і побіжно не використовувати однакові інструкції на машинній мові. Якби C пройшов цей маршрут, заголовки надсилали б весь світ #define TRUE -1
.
Але мова програмування B була слабо типованою і не мала булевих або навіть типів з плаваючою комою. Все було еквівалентом int
його наступника. Це дозволило мові визначити те, що сталося, коли програма використовувала як логічне значення значення, відмінне від істинного чи помилкового. Вперше він визначив виразний вираз як "не дорівнює нулю". Це було ефективно для міні-комп'ютерів, на яких він працював, у яких був прапор CPU нульового рівня.
На той час була альтернатива: ті ж процесори також мали негативний прапор, а значення правди BCPL було -1, тож B міг би замість цього визначити всі негативні числа як істинні, а всі негативні числа - як хибні. (Є один залишок такого підходу: багато системних викликів в UNIX, розроблених одночасно одними людьми, визначає всі коди помилок як від'ємні цілі числа. Багато системних викликів повертають одне з декількох різних від'ємних значень при відмові.) будьте вдячні: могло бути і гірше!
Але визначення TRUE
як 1
і FALSE
як 0
в B означало, що особистість true = ~ false
більше не тримається, і це відкинуло сильну типізацію, яка дозволила Альголу розмежуватись між бітовими та логічними виразами. Для цього потрібен новий логічний оператор, і дизайнери вибрали !
, можливо, тому, що не було рівним-вже !=
, що виглядає як вертикальна смуга через знак рівності. Вони не дотримувались тієї ж конвенції, &&
або ||
тому, що жодної ще не існувала.
Можливо, у них повинно бути: &
оператор в B порушений, як було заплановано. В B і C, 1 & 2 == FALSE
хоча 1
і 2
є обидві значення, і немає інтуїтивного способу виразити логічну операцію в B. Це була одна помилка, яку С намагався частково виправити додаванням &&
і ||
, але головна проблема в той час полягала в тому, щоб нарешті, запускайте коротке замикання на роботу та змушуйте програми працювати швидше. Підтвердженням цього є те, що немає ^^
: 1 ^ 2
значення truthy, хоча обидва його операнда є truthy, але він не може отримати користь від короткого замикання.