C99 - почему ложь и истина определены как 0 и 1, а не как ((bool) 0) и ((bool) 1)?

Просто наткнулся на утверждение, которое не удалось, поскольку оно сравнивало false с типом возврата функции, поскольку сама функция вернула bool, а assert проверил не только значение, но и тип возвращаемого значения на соответствие ложному, чтобы гарантировать, что возвращается bool. Теперь проблема в том, что C99 определяет bool как _Bool и _Bool даже не обязательно того же размера, что и int (на самом деле, по моему опыту, на большинстве платформ в настоящее время он часто имеет такой же размер, как unsigned char), не говоря уже о том, чтобы быть таким же type (что на самом деле невозможно, поскольку _Bool является встроенным типом языка в C99), но определяет false и true как 0 и 1 без какого-либо преобразования типов, а определения препроцессора без преобразования типа по умолчанию будут иметь значение int. Если бы C99 вместо этого определил false и true как ((bool) 0) и ((bool) 1), они всегда были бы типа bool, независимо от того, как определяется _Bool. Итак, есть ли веская причина, чтобы они всегда определялись как int, даже если bool не является int на этой платформе или это просто ошибка языка, которую следует исправить с помощью C1x?


person Kaiserludi    schedule 08.07.2011    source источник


Ответы (4)


false и true определены как целочисленные константы 0 и 1 соответственно, потому что это именно то, что стандарт C99 определяет в разделе 7.16:

‹СНИП>

Остальные три макроса подходят для использования в директивах предварительной обработки #if. Они есть

верно

который расширяется до целочисленной константы 1,

ложь

который расширяется до целочисленной константы 0, и

‹СНИП>

ИЗМЕНИТЬ: как показывают комментарии ниже, кажется, я немного неверно истолковал вопрос и должен был указать причину, по которой стандарт определяет это именно так. Одна из причин, по которой я могу думать, заключается в том, что true и false должны использоваться в #if директивах предварительной обработки (как упоминается в цитате из стандарта).

Причина, по которой ((bool) 0) или ((bool) 1) не работают в #if директивах предварительной обработки, заключается в том, что стандарт не допускает этого. В разделе 6.10.1 говорится:

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

person Sander De Dycker    schedule 08.07.2011
comment
Хороший ответ, но указание фактической цитаты сделало бы его отличным ответом. - person Nemo; 08.07.2011
comment
@Nemo: блок кода содержит прямую цитату из стандарта C99, но я мог бы указать это явно и сделать это как цитату (теперь исправлено). - person Sander De Dycker; 08.07.2011
comment
@ Роб: Мне очень жаль - тебе придется объяснить, что ты имеешь в виду. Разве ответ не хорош, потому что он слишком очевиден? - person Sander De Dycker; 08.07.2011
comment
Да, эта стандартная спецификация, конечно, является причиной того, почему стандартные реализации определяют константы таким образом, но это не отвечает на вопрос, есть ли у стандарта веская причина, по которой они расширяются до целого числа, а не до логического значения. . - person Kaiserludi; 08.07.2011
comment
@Sander: он имеет в виду, что ваш ответ ничего не добавляет к обсуждению. ОП знает, что они определены как целые, он спрашивает, почему было принято решение. - person houbysoft; 08.07.2011
comment
@Kaiserludi: понял. Похоже, я неправильно истолковал вопрос. Теперь я понимаю, что имел в виду @Rob. Я предполагаю, что причина того, что они не относятся к типу _Bool, заключается в том, что они должны использоваться в директивах предварительной обработки #if, как упоминается в приведенной выше цитате. - person Sander De Dycker; 08.07.2011
comment
@houbysoft @Rob: Я считаю, что использование директив предварительной обработки #if является одним из аргументов. - person Nemo; 08.07.2011
comment
Только что протестировано: это действительно приведет к ошибкам в GCC: `#ifdef false #undef false #endif #ifdef true #undef true #endif #define false ((bool) 0) #define true ((bool) 1) #if false # endif `пока это будет нормально:` / * # ifdef false #undef false #endif #ifdef true #undef true #endif #define false ((bool) 0) #define true ((bool) 1) * / #if false #endif `Так что, похоже, причина в совместимости директив препроцессора. - person Kaiserludi; 08.07.2011
comment
@Kaiserludi: это потому, что в стандарте сказано, что выражение, управляющее условным включением, должно быть целочисленным постоянным выражением, за исключением того, что: оно не должно содержать приведение ;. Еще раз приношу свои извинения за первоначальную путаницу с моей стороны. - person Sander De Dycker; 08.07.2011
comment
(bool)+1 и (bool)+0 будут действительны в #if, не так ли? bool будет заменен на _Bool, который затем будет заменен на 0. - person R.. GitHub STOP HELPING ICE; 01.10.2011
comment
@Р. : интересный подход. Но было бы неплохо, если бы стандарт определял true и false именно так? - person Sander De Dycker; 01.10.2011
comment
Поскольку единственное место, где это может иметь значение, это sizeof true и sizeof false, я думаю, это было бы просто глупо ... - person R.. GitHub STOP HELPING ICE; 01.10.2011

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

Например, что это за тип (_Bool)0 & (_Bool)1? Вы можете подумать, что выражение имеет тип _Bool, но на самом деле §6.5.10 определяет семантику &:

... Обычные арифметические преобразования выполняются с операндами ...

«обычные арифметические преобразования» имеют очень специфическое значение в стандарте C. Он определен в §6.3.1.8 и включает в себя следующее:

... целочисленные акции выполняются для обоих операндов ...

«целочисленные рекламные акции» также являются определенным термином из §6.3.1.1:

Если int может представлять все значения исходного типа, значение преобразуется в int; в противном случае он преобразуется в целое число без знака. Они называются целочисленными промоакциями. 48) Все остальные типы не меняются целочисленными промоакциями.

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

person Stephen Canon    schedule 08.07.2011
comment
зачем использовать двоичное И для двух логических значений? - person dascandy; 08.07.2011
comment
@dascandy: Что, если бы два операнда были вызовами функций, которые возвращали _Bool, и что, если бы у вызовов функций были побочные эффекты, которые вы не хотели бы терять из-за короткого замыкания? - person jamesdlin; 09.07.2011
comment
Не думал об этом. Спасибо! Хорошая причина. Я думаю, что это немного лаконично, но было бы неплохо. - person dascandy; 11.07.2011
comment
@jamesdlin: Если вам небезразличны побочные эффекты функций, вы, скорее всего, заботитесь о порядке, в котором вызываются две функции. На самом деле я однажды попытался сделать что-то вроде того, что вы предложили, и это немного Мне плохо, когда конкретный компилятор решил изменить порядок вызовов ... Теперь я бы сказал, что почти всегда вредно использовать несколько функций с побочными эффектами в качестве операндов для арифметических / побитовых операторов. - person R.. GitHub STOP HELPING ICE; 01.10.2011

Во-первых, хотя _Bool может не быть int, требуется, чтобы _Bool мог принимать значения 0 и 1, поэтому расширение true и false до 1 и 0 нормально.

C99 §6.2.5 / 2: объект, объявленный как тип _Bool, достаточно велик, чтобы хранить значения 0 и 1.

Кроме того, для обратной совместимости true и false разумно быть ints, потому что все логические операторы возвращают int.

C99 §6.5.3.3 / 5: результат оператора логического отрицания ! равен 0, если значение его операнда не равно 0, 1, если значение его операнда сравнивается с 0. Результат имеет тип int. Выражение !E эквивалентно (0==E).

C99 §6.5.8 / 6: каждый из операторов < (меньше), > (больше), <= (меньше или равно) и >= (больше или равно) должен вывести 1, если указанное отношение истинно, и 0, если оно ложно. 90) Результат имеет тип int.

C99 §6.5.9 / 3: операторы == (равно) и != (не равно) аналогичны операторам отношения, за исключением их более низкого приоритета. 91) Каждый из операторов возвращает 1, если указанное отношение истинно, и 0, если оно ложно. Результат имеет тип int. Для любой пары операндов истинно ровно одно из отношений.

C99 §6.5.13 / 3: оператор && возвращает 1, если оба его операнда не равны 0; в противном случае возвращается 0. Результат имеет тип int.

C99 §6.5.14 / 3: оператор || возвращает 1, если любой из его операндов не равен 0; в противном случае возвращается 0. Результат имеет тип int.

И, наконец, как @ Sander De Dycker упомянул, что стандартные, определенные true и false, должны быть расширены таким образом (C99 §7.16 / 3).

person kennytm    schedule 08.07.2011
comment
Но обычно вы проверяете логические операторы, не используя true или false, и пишете такой код, как if (foo) и if (! Foo) (поскольку TRUE-макрос, определяемый реализацией, гарантированно будет только! = FALSE, но не 1, и если вы напишете код вроде if (foo == true), вы можете случайно приобрести эту привычку для TRUE). Кроме того, если избежать неявных приведений при сравнении результатов логического оператора с истинным / ложным было бы проблемой, то сохранение этих результатов в bool или проверка bool на истинное / ложное будет иметь ту же проблему, и для стандарта было бы лучше определить bool как внутр. - person Kaiserludi; 08.07.2011

Все остальные ответы пытаются использовать стандарт, чтобы оправдать, почему стандарт определяет вещи определенным образом, что я считаю неудовлетворительным. Стандарт определяет не только типы, но также операторы и препроцессор, поэтому, если C99 вводил логический тип, почему бы не изменить все логические операторы, чтобы вычислить значение этого типа, и не расширить препроцессор для поддержки логических типов?

Сделать это можно, но нужно сложнее. Разработчикам стандартов и составителям компиляторов было намного проще внести только минимально необходимые изменения, чтобы добавить в язык новый логический тип. Поскольку все логические операции по-прежнему оцениваются как тип int, все компиляторы до C99 могут быть обновлены для поддержки C99 без необходимости изменять свой код оценки типа для всех основных операторов, а разработчики стандартов могут быть более уверены в том, что новый Логическая функция не случайно внесла несоответствия в части стандарта, которые раньше были хороши. Все, что им нужно было сделать, это убедиться, что к _Bool применяются «обычные арифметические преобразования», и тогда все остальное будет гарантированно работать.

Это не техническая причина. Это практично.

person Rob Kennedy    schedule 08.07.2011