Почему C не имел логического типа данных до C99?

Я понимаю, что вы можете просто #define некоторые целые числа, но почему C не имел выделенного логического типа данных до C99?

Это такое обычное явление в программировании и логике, что я не понимаю отсутствие явного типа и обозначения.


person xyz    schedule 11.03.2010    source источник
comment
-1: Какую проблему решает знание истории деталей реализации C Керниганом и Ритчи?   -  person S.Lott    schedule 11.03.2010
comment
@ S.Lott - извините, я не понял цель StackOverflow? Этот вопрос не связан с программированием? Из FAQ: это место для вопросов, на которые можно ответить. Пока ваш вопрос... подробный и конкретный, написанный ясно и просто, интересующий хотя бы еще одного где-нибудь программиста. Что еще я пропустил?   -  person Andy Shellam    schedule 11.03.2010
comment
@ S.Lott Я подозреваю, что вам следует удалить главу «Предыстория и история» из вашей книги по Python.   -  person Johannes Schaub - litb    schedule 11.03.2010
comment
@ S.Lott Где мой -1? По крайней мере, следуйте за своим сарказмом.   -  person xyz    schedule 11.03.2010
comment
@S.Lott - Это может не решить конкретную проблему прямо сейчас, но я обнаружил, что понимание проектных решений обычно помогает мне лучше понять язык.   -  person Edan Maor    schedule 11.03.2010
comment
Наличие типа bool может дать преимущества в скорости в некоторых областях, потому что компилятор может составить его представление. Можно предположить, что логическое значение всегда имеет только младший бит в 1 или 0, а все остальные биты в 0. Так, например, при выполнении return (int)!!some_boolean; оно может просто переместить логическое значение в возвращаемое значение, в то время как когда some_boolean является просто целым числом, оно имеет для ветвления, если нет специальной инструкции процессора. И для boolean = !boolean; тоже не нужна ветка (как для int). Например, можно просто перевернуть LSB.   -  person Johannes Schaub - litb    schedule 11.03.2010
comment
Есть огромная разница между историей — как фактами — и почему? в этом вопросе. Факты очевидны. Обоснование известно только Кернигану и Ричи. Лучшее, что мы можем сделать, это случайным образом угадать их обоснование. Я не люблю гадать, и - без их участия - на вопрос нельзя ответить в том виде, в каком он задан.   -  person S.Lott    schedule 11.03.2010
comment
Я должен сказать, что я нахожу Почему у языка Foo нет панели функций? вопросы должны носить характер аргументации. В любом случае, ответ таков: у K&C нет ничего, что ему нужно. А явный логический тип — это излишество.   -  person dmckee --- ex-moderator kitten    schedule 12.03.2010
comment
@S.Lott, C89 не был сделан Керниганом и Ричи. Это было сделано комиссией. Обоснование должно быть известно членам комиссии.   -  person Johannes Schaub - litb    schedule 12.03.2010
comment
@С. Лотт: Не нужно гадать; Ричи оставил след в истории...   -  person Norman Ramsey    schedule 12.03.2010


Ответы (11)


Если вы проведете немного времени в библиотеке, вам не придется строить догадки. Вот несколько утверждений, взятых из статьи Денниса Ритчи об эволюции C. Контекст таков, что Деннис строит язык Кена Томпсона B, который был реализован на очень маленьком PDP-7, машине со словесной адресацией. Из-за растущего интереса группа получила один из самых первых PDP-11. Денис пишет,

Появление PDP-11 выявило несколько недостатков семантической модели B. Во-первых, его механизмы обработки символов, унаследованные с небольшими изменениями от BCPL, были неуклюжими: использование библиотечных процедур для распределения упакованных строк по отдельным ячейкам с последующей перепаковкой или для доступа и замены отдельных символов стало казаться неудобным, даже глупым, на байт-ориентированная машина.

Модель B и BCPL подразумевала накладные расходы при работе с указателями: правила языка, определяя указатель как индекс в массиве слов, заставляли указатели представляться как индексы слов. Каждая ссылка на указатель генерировала преобразование шкалы времени выполнения из указателя в адрес байта, ожидаемый аппаратным обеспечением.

По всем этим причинам казалось, что схема типизации необходима, чтобы справляться с адресацией символов и байтов, а также для подготовки к грядущему оборудованию с плавающей запятой. Другие вопросы, особенно безопасность типов и проверка интерфейса, тогда казались не такими важными, как стали позже.

(Выделение мое.)

Далее в документе описываются попытки Денниса изобрести новую семантику указателей, заставить работать массивы и примириться с этой новомодной struct идеей. Понятия безопасности типов и отличия логических значений от целых чисел казались важными только намного позже :-)

person Norman Ramsey    schedule 11.03.2010

C на самом деле немного больше, чем язык ассемблера более высокого уровня. Да, у него есть управляющие структуры и еще много чего, и даже есть типы, которые ассемблеру точно не нужны.

Но язык был разработан десятилетия назад. И поскольку каждый логический результат сводится к отдельным битам в слове состояния процессора, очевидно, было достаточно просто использовать для него целочисленный тип данных. И это сделало компилятор, вероятно, немного менее сложным, поскольку вы можете опустить некоторую проверку типов (в более поздних языках управляющие структуры нуждаются в логическом значении, в C им просто нужно целочисленное значение либо 0, либо что-то еще) .

person Joey    schedule 11.03.2010
comment
Помните, что ранний C также использовал 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++, но я совершенно уверен, что любое ненулевое значение все равно будет оцениваться как «истина».

person Dan Puzey    schedule 11.03.2010
comment
Я всегда находил интересной вещь 0/-1, когда впервые обнаружил ее, особенно потому, что так много учителей / лекторов неправильно объясняли, что положительная 1 была true. - person Dan Puzey; 11.03.2010
comment
Положительное значение 1 является истинным в C, в том смысле, что операторы сравнения и логические операторы и/или/не оцениваются как 1 или 0 для истинного или ложного значения соответственно. Но лучше не думать о том, что true имеет значение, так как любое ненулевое значение истинно. - person R.. GitHub STOP HELPING ICE; 11.08.2010

ЦП не имеет «логического типа», они работают только с байтами и кратными им, поэтому в то время логический тип не имел смысла, поскольку не давал преимущества (зачем использовать тип, когда вы можете проверить только «равно 0» или "не является нулевым")

person Morfildur    schedule 11.03.2010
comment
Конечно, процессор имеет логические типы. Регистр флагов забит ими! - person xtofl; 11.03.2010
comment
Да, но они не универсальные. - person Morfildur; 11.03.2010

Я подозреваю, что было сочтено достаточным иметь целочисленный тип, где 0 был ложным, а все, что не 0, истинным.

person Vatine    schedule 11.03.2010
comment
Я думаю, что это лучший ответ. Большинство логических значений в любом случае обрабатываются как целые числа при компиляции на ассемблере. - person Rob Nicholson; 11.03.2010

Тип, который вы используете для хранения логического значения (обычно), воплощает компромисс между пространством и временем. Как правило, вы получите самые быстрые результаты (по крайней мере, для отдельной операции), используя int (обычно четыре байта). С другой стороны, если вы используете очень много, может иметь гораздо больше смысла использовать один байт или даже упаковать их, чтобы каждое сохраняемое вами значение использовало только один бит, но когда/если вы это сделаете, чтение или запись одного бита становится значительно дороже (и использует дополнительный код).

Поскольку не было ни одного действительно «правильного» ответа, они предоставили пользователю право принимать решение на основе требований программы, которую он писал.

Тогда реальный вопрос заключается в том, почему логический тип был добавлен в C99. Я предполагаю, что здесь задействовано несколько факторов. Во-первых, они поняли, что удобочитаемость и удобство для программиста теперь обычно важнее, чем обеспечение максимально возможной производительности. Во-вторых, теперь компиляторы выполняют гораздо более глобальный анализ, так что, по крайней мере, можно предположить, что кто-то может написать компилятор, который пытается выбрать представление, наиболее подходящее для конкретной программы (хотя я этого не знаю). я не знаю ни одного, который действительно так делает).

person Jerry Coffin    schedule 11.03.2010
comment
+1: мне понравилось упоминание о компромиссе между пространством и временем. Это очень важный момент. - person Arun; 12.03.2010
comment
В некоторых компиляторах int является самым быстрым форматом для хранения логического значения. Однако на многих (но не на всех) char будет столь же быстрым. Некоторые процессоры встроенных систем имеют специальный тип bit, который еще быстрее (хотя такие процессоры обычно не позволяют включать типы bit в другие структуры). Было бы глупо хранить логическое значение в int на платформе, где char будет столь же быстрым (или, для некоторых встроенных процессоров, даже быстрее). - person supercat; 11.06.2012
comment
Что сказал supercat, исходной целью для c, как правило, были машины с очень ограниченным объемом памяти, и вы почти всегда предпочитаете управлять битовой упаковкой вручную. Добавление логического типа в то время поощряло бы то, что тогда считалось бы ужасным выводом машинного кода. - person Bill K; 24.10.2017
comment
@supercat По-прежнему существует незначительное снижение размера кода / производительности из-за наличия _Bools вместо chars или ints в качестве целочисленных данных, где раньше не имело значения, было ли это 1 или 432 или любое другое значение, отличное от 0, теперь должно быть нормализовано либо в 0, либо в 1, по крайней мере, там, где это можно наблюдать. - person PSkocik; 18.03.2019
comment
Я бы добавил, что у падчерицы любви/ненависти С++ уже был выделенный логический тип, и его добавление было полезно для совместной работы. - person Deduplicator; 30.05.2020

Старый C на самом деле не «отсутствовал» логический тип — просто все целочисленные типы также считались подходящими для выполнения двойной функции — хранения логических значений. Я вижу две основные причины этого:

  • Процессоры с побитовой адресацией не были распространены (и до сих пор не распространены), поэтому компилятор на самом деле не сможет использовать «истинный логический» тип для экономии места - логическое значение все равно будет как минимум таким же большим, как char в любом случае (если вы надеялись получить к нему эффективный доступ).

  • Типы уже, чем int, в любом случае расширяются до int в выражениях, поэтому логические операторы по-прежнему будут работать с int операндами.

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

Помните, что в языке C есть набор операторов, которые производят логические результаты (определяемые как 0 или 1) — !, &&, ||, !=, ==, <, <=, > и >= — так что это только выделенный логический тип это не там.

person caf    schedule 12.03.2010

Исторические причины, вероятно:

CPL, на который сильно повлиял ALGOL, скорее всего, имел логический тип, но моего гугла-фу не хватило, чтобы найти ссылку на него. Но CPL был слишком амбициозен для своего времени, что привело к появлению урезанной версии под названием BCPL, которая имела то преимущество, что ее можно было реализовать на доступном оборудовании.

В BCPL был только один тип — «слово», которое интерпретировалось как ложное в логических контекстах, если 0, и как истинное, если ~0 (имеется в виду дополнение 0, которое будет представлять значение -1, если интерпретируется как целое число с дополнением до двух со знаком). Интерпретация любого другого значения зависит от реализации.

После все еще бестипового преемника B, C вновь ввела систему типов, но на нее все еще сильно повлияла бестиповая природа ее предшественников.

person Christoph    schedule 11.03.2010

Добавление отдельного «логического» типа, несовместимого с целыми числами, сделало бы компилятор более сложным, чем простое использование целых чисел для этой цели. Наличие отдельного логического типа, совместимого с целыми числами, делает необходимым указать возможные последствия сохранения значения, отличного от 0 или 1, в логическом объекте или выполнения числовых вычислений над логическим объектом, представление которого не содержит ни битового шаблона, связанного с " 0" или "1". Данный:

someBool = intFunction();
someInt = someBool;

требование, чтобы someInt получало значение 1, если intFunction возвращает любое ненулевое значение, как правило, делает вышеуказанное более дорогим, чем

someChar = intFunction();
someInt = someChar;

В тех случаях, когда потребуется предыдущая семантика, их можно было бы достичь без использования логического типа с помощью:

someChar = !!intFunction();
someInt = someChar;

Поскольку все, что можно сделать с помощью логических типов, можно сделать и без них, и во многих случаях код, использующий символьные типы, может быть более эффективным, чем логические типы, я бы предположил, что никогда не было (и до сих пор нет) никаких реальных потребность в них.

person supercat    schedule 23.10.2017
comment
И да, и нет. С семантической точки зрения someBool = intFunction(); в любом случае не имеет особого смысла, хотя, по-видимому, это широко используемая идиома. - person ratijas; 15.11.2018
comment
@ratijas: существует множество функций, которые используют тип возвращаемого значения int для передачи вызывающей стороне 0 или 1, либо потому, что они были разработаны до того, как существовал _Bool, либо потому, что они используют один и тот же тип возвращаемого значения для нескольких целей (например, int driverFunction(int functionCode, int param1, void *param2);). - person supercat; 16.11.2018
comment
Ага. И это путь Си. В некоторых языках с более высоким типом вы не будете использовать один и тот же тип возвращаемого значения для нескольких целей. Вместо этого вы должны использовать отдельные типы в объединении (возможно, алгебраические) — отсюда и система type. - person ratijas; 16.11.2018
comment
@ratijas: в ситуациях, когда функция может выполнять несколько видов операций, выбранных с помощью целого числа (обычная ситуация с такими вещами, как загружаемые драйверы устройств, которые должны поддерживать несколько операций с использованием одной точки входа), часто необходимо использовать универсальный комбинация типов аргументов и возвращаемых значений для различных целей. Передача значения true/false через int32_t более практична, чем использование void* для передачи адреса _Bool. - person supercat; 16.11.2018

Потому что они его не вставили. Извините, если это звучит язвительно, но в основном это не было определено как таковое.

Помните, что большинство людей #определяют ИСТИНА и ЛОЖЬ.

Вы можете сказать, что bool IS стандарт - но очевидно, что он НЕ БЫЛ стандартом до C99, который был сделан 10 лет назад ;) Они добавили его тогда, когда стало очевидно, что он отсутствует.

person TomTom    schedule 11.03.2010
comment
+1: Потому что они его не вставили. Единственный возможный ответ. - person S.Lott; 11.03.2010
comment
Это стандарт не только в программировании, но и в мыслительном процессе при решении задачи. - person xyz; 11.03.2010
comment
Помните, что большинство людей #define TRUE и FALSE - они ДОЛЖНЫ были это сделать из-за отсутствия какого-либо другого метода, так что на самом деле это не ответ на вопрос, почему. - person Andy Shellam; 11.03.2010

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

person Eugeniu Torica    schedule 11.03.2010