Я понимаю, что вы можете просто #define
некоторые целые числа, но почему C не имел выделенного логического типа данных до C99?
Это такое обычное явление в программировании и логике, что я не понимаю отсутствие явного типа и обозначения.
Я понимаю, что вы можете просто #define
некоторые целые числа, но почему C не имел выделенного логического типа данных до C99?
Это такое обычное явление в программировании и логике, что я не понимаю отсутствие явного типа и обозначения.
Если вы проведете немного времени в библиотеке, вам не придется строить догадки. Вот несколько утверждений, взятых из статьи Денниса Ритчи об эволюции C. Контекст таков, что Деннис строит язык Кена Томпсона B, который был реализован на очень маленьком PDP-7, машине со словесной адресацией. Из-за растущего интереса группа получила один из самых первых PDP-11. Денис пишет,
Появление PDP-11 выявило несколько недостатков семантической модели B. Во-первых, его механизмы обработки символов, унаследованные с небольшими изменениями от BCPL, были неуклюжими: использование библиотечных процедур для распределения упакованных строк по отдельным ячейкам с последующей перепаковкой или для доступа и замены отдельных символов стало казаться неудобным, даже глупым, на байт-ориентированная машина.
Модель B и BCPL подразумевала накладные расходы при работе с указателями: правила языка, определяя указатель как индекс в массиве слов, заставляли указатели представляться как индексы слов. Каждая ссылка на указатель генерировала преобразование шкалы времени выполнения из указателя в адрес байта, ожидаемый аппаратным обеспечением.
По всем этим причинам казалось, что схема типизации необходима, чтобы справляться с адресацией символов и байтов, а также для подготовки к грядущему оборудованию с плавающей запятой. Другие вопросы, особенно безопасность типов и проверка интерфейса, тогда казались не такими важными, как стали позже.
(Выделение мое.)
Далее в документе описываются попытки Денниса изобрести новую семантику указателей, заставить работать массивы и примириться с этой новомодной struct
идеей. Понятия безопасности типов и отличия логических значений от целых чисел казались важными только намного позже :-)
C на самом деле немного больше, чем язык ассемблера более высокого уровня. Да, у него есть управляющие структуры и еще много чего, и даже есть типы, которые ассемблеру точно не нужны.
Но язык был разработан десятилетия назад. И поскольку каждый логический результат сводится к отдельным битам в слове состояния процессора, очевидно, было достаточно просто использовать для него целочисленный тип данных. И это сделало компилятор, вероятно, немного менее сложным, поскольку вы можете опустить некоторую проверку типов (в более поздних языках управляющие структуры нуждаются в логическом значении, в C им просто нужно целочисленное значение либо 0, либо что-то еще) .
char*
до того, как void*
был стандартизирован как общий тип указателя. У процессора не было понятия void
, поэтому язык использовал указатель данных с наименьшим разрешением, понятным аппаратному обеспечению. Более абстрактные понятия, такие как void*
, появились позже. C очень близок к чистому языку, и абстрагирование true/false в новый тип данных, не отражающий то, что может однозначно понимать аппаратное обеспечение, не имело особого смысла (bool
было бы просто char
с дополнительными ограничений, достаточно просто, чтобы typedef
самостоятельно, если вам это действительно нужно).
- person bta; 11.03.2010
Было принято (и до сих пор в некоторых случаях) считать ноль ложным, а любое ненулевое значение - истинным. Это дает преимущества для сокращений: например, вместо while (remaining != 0)
можно просто использовать while (remaining)
.
В некоторых языках стандартизировано истинное значение -1. Причина этого в том, что в нотации с дополнением до двух (которую большинство компьютеров используют для представления отрицательных чисел) побитовое не 0 равно -1 (в 8-битном двоичном коде 11111111
равно десятичному -1).
Со временем стало понятно, что использование констант, определяемых компилятором, предотвратит большую потенциальную путаницу. Прошло некоторое время с тех пор, как я занимался C++, но я совершенно уверен, что любое ненулевое значение все равно будет оцениваться как «истина».
true
.
- person Dan Puzey; 11.03.2010
ЦП не имеет «логического типа», они работают только с байтами и кратными им, поэтому в то время логический тип не имел смысла, поскольку не давал преимущества (зачем использовать тип, когда вы можете проверить только «равно 0» или "не является нулевым")
Я подозреваю, что было сочтено достаточным иметь целочисленный тип, где 0 был ложным, а все, что не 0, истинным.
Тип, который вы используете для хранения логического значения (обычно), воплощает компромисс между пространством и временем. Как правило, вы получите самые быстрые результаты (по крайней мере, для отдельной операции), используя int (обычно четыре байта). С другой стороны, если вы используете очень много, может иметь гораздо больше смысла использовать один байт или даже упаковать их, чтобы каждое сохраняемое вами значение использовало только один бит, но когда/если вы это сделаете, чтение или запись одного бита становится значительно дороже (и использует дополнительный код).
Поскольку не было ни одного действительно «правильного» ответа, они предоставили пользователю право принимать решение на основе требований программы, которую он писал.
Тогда реальный вопрос заключается в том, почему логический тип был добавлен в C99. Я предполагаю, что здесь задействовано несколько факторов. Во-первых, они поняли, что удобочитаемость и удобство для программиста теперь обычно важнее, чем обеспечение максимально возможной производительности. Во-вторых, теперь компиляторы выполняют гораздо более глобальный анализ, так что, по крайней мере, можно предположить, что кто-то может написать компилятор, который пытается выбрать представление, наиболее подходящее для конкретной программы (хотя я этого не знаю). я не знаю ни одного, который действительно так делает).
int
является самым быстрым форматом для хранения логического значения. Однако на многих (но не на всех) char
будет столь же быстрым. Некоторые процессоры встроенных систем имеют специальный тип bit
, который еще быстрее (хотя такие процессоры обычно не позволяют включать типы bit
в другие структуры). Было бы глупо хранить логическое значение в int
на платформе, где char
будет столь же быстрым (или, для некоторых встроенных процессоров, даже быстрее).
- person supercat; 11.06.2012
_Bool
s вместо char
s или int
s в качестве целочисленных данных, где раньше не имело значения, было ли это 1 или 432 или любое другое значение, отличное от 0, теперь должно быть нормализовано либо в 0
, либо в 1
, по крайней мере, там, где это можно наблюдать.
- person PSkocik; 18.03.2019
Старый C на самом деле не «отсутствовал» логический тип — просто все целочисленные типы также считались подходящими для выполнения двойной функции — хранения логических значений. Я вижу две основные причины этого:
Процессоры с побитовой адресацией не были распространены (и до сих пор не распространены), поэтому компилятор на самом деле не сможет использовать «истинный логический» тип для экономии места - логическое значение все равно будет как минимум таким же большим, как char
в любом случае (если вы надеялись получить к нему эффективный доступ).
Типы уже, чем int
, в любом случае расширяются до int
в выражениях, поэтому логические операторы по-прежнему будут работать с int
операндами.
... так что похоже, что не было достаточно убедительного случая, чтобы выделенный логический тип действительно приносил практические преимущества.
Помните, что в языке C есть набор операторов, которые производят логические результаты (определяемые как 0 или 1) — !
, &&
, ||
, !=
, ==
, <
, <=
, >
и >=
— так что это только выделенный логический тип это не там.
Исторические причины, вероятно:
CPL, на который сильно повлиял ALGOL, скорее всего, имел логический тип, но моего гугла-фу не хватило, чтобы найти ссылку на него. Но CPL был слишком амбициозен для своего времени, что привело к появлению урезанной версии под названием BCPL, которая имела то преимущество, что ее можно было реализовать на доступном оборудовании.
В BCPL был только один тип — «слово», которое интерпретировалось как ложное в логических контекстах, если 0
, и как истинное, если ~0
(имеется в виду дополнение 0
, которое будет представлять значение -1
, если интерпретируется как целое число с дополнением до двух со знаком). Интерпретация любого другого значения зависит от реализации.
После все еще бестипового преемника B, C вновь ввела систему типов, но на нее все еще сильно повлияла бестиповая природа ее предшественников.
Добавление отдельного «логического» типа, несовместимого с целыми числами, сделало бы компилятор более сложным, чем простое использование целых чисел для этой цели. Наличие отдельного логического типа, совместимого с целыми числами, делает необходимым указать возможные последствия сохранения значения, отличного от 0 или 1, в логическом объекте или выполнения числовых вычислений над логическим объектом, представление которого не содержит ни битового шаблона, связанного с " 0" или "1". Данный:
someBool = intFunction();
someInt = someBool;
требование, чтобы someInt получало значение 1, если intFunction возвращает любое ненулевое значение, как правило, делает вышеуказанное более дорогим, чем
someChar = intFunction();
someInt = someChar;
В тех случаях, когда потребуется предыдущая семантика, их можно было бы достичь без использования логического типа с помощью:
someChar = !!intFunction();
someInt = someChar;
Поскольку все, что можно сделать с помощью логических типов, можно сделать и без них, и во многих случаях код, использующий символьные типы, может быть более эффективным, чем логические типы, я бы предположил, что никогда не было (и до сих пор нет) никаких реальных потребность в них.
someBool = intFunction();
в любом случае не имеет особого смысла, хотя, по-видимому, это широко используемая идиома.
- person ratijas; 15.11.2018
int
для передачи вызывающей стороне 0 или 1, либо потому, что они были разработаны до того, как существовал _Bool
, либо потому, что они используют один и тот же тип возвращаемого значения для нескольких целей (например, int driverFunction(int functionCode, int param1, void *param2);
).
- person supercat; 16.11.2018
int32_t
более практична, чем использование void*
для передачи адреса _Bool
.
- person supercat; 16.11.2018
Потому что они его не вставили. Извините, если это звучит язвительно, но в основном это не было определено как таковое.
Помните, что большинство людей #определяют ИСТИНА и ЛОЖЬ.
Вы можете сказать, что bool IS стандарт - но очевидно, что он НЕ БЫЛ стандартом до C99, который был сделан 10 лет назад ;) Они добавили его тогда, когда стало очевидно, что он отсутствует.
Потому что никто не может предусмотреть всего, включая отсутствующий тип данных в языке программирования.
return (int)!!some_boolean;
оно может просто переместить логическое значение в возвращаемое значение, в то время как когда some_boolean является просто целым числом, оно имеет для ветвления, если нет специальной инструкции процессора. И дляboolean = !boolean;
тоже не нужна ветка (как для int). Например, можно просто перевернуть LSB. - person Johannes Schaub - litb   schedule 11.03.2010