Стандарт C явно указывает значение истинности как 0 или 1?

86

Мы знаем, что любые числа, которые не равны 0, рассматриваются как trueв C, поэтому мы можем написать:

int a = 16;

while (a--)
    printf("%d\n", a);  // prints numbers from 15 to 0

Однако мне было интересно, определены ли true / false как 1/ 0в C, поэтому я попробовал код ниже:

printf("True = %d, False = %d\n", (0 == 0), (0 != 0));  // prints: True = 1, False = 0

Стандарт C явно указывает значения истинности истинного и ложного как 1и 0соответственно?

Кевин Донг
источник
3
Думаю, этот ТАК вопрос актуален
Имран Али
3
Я пытаюсь запустить это gccс помощью, -std=c89и это дает тот же результат.
Кевин Донг
1
@Blackhole, с 15 до 0.
Артуро Торрес Санчес,
6
Почти дурацкий, но более чем за десять лет до SO / SE: c-faq.com/bool/bool2.html .
dave_thompson_085
1
То, что ложь равна нулю, является каноном, однако истина обычно считается «ненулевой». Однако, будучи программистами, мы все использовали 1 как «не ноль» по разным причинам. Вам предлагается не доверять истинному значению, равному ровно 1. В то время как (0 == 0) является одним в вашем примере, что-то вроде (12 == 12) может также легко иметь значение 12; тоже "правда".
Инженер

Ответы:

96

Указывает ли стандарт C явно значения истинности trueи falseas 0и 1соответственно?

Стандарт C определяет trueи falseкак макросы, в stdbool.hкоторых расширяются до 1и 0соответственно.

C11-§7.18:

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

true

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

false

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

Для операторов ==и !=стандарт говорит

C11-§6.5.9 / 3:

Операторы ==(равно) и !=(не равно) аналогичны операторам отношения, за исключением их более низкого приоритета. 108) Каждый из операторов дает результат, 1если указанное отношение истинно и 0если оно ложно. Результат имеет тип int. Для любой пары операндов верно ровно одно из отношений.

хаки
источник
20
Мне кажется, что вопрос 0 == 0и 0 != 0прочее, а не в значении макросов.
MM
9
Я думаю, что, когда он писал, trueон имел в виду «ценность истинного сравнения» или что-то в этом роде, а не макросtrue
ММ
1
@KevinDong; Да, в проекте C99 есть аналогичный абзац.
haccks
1
@haccks: вы можете без колебаний сказать об этом "идентичный". Я просто просмотрел вашу цитату, так как мне было лень искать по абзацам, поскольку я ссылаюсь на c99, когда когда-либо было необходимо. И я смог найти его, просто ctrlf
выполнив
2
@MooingDuck: NaN == NaNложно и NaN != NaNверно. С этим утверждением проблем нет.
kennytm
51

Это явно не указано в C11. Все операции на уровне языка вернут 1 как истину (и примет любое ненулевое значение, включая NaN, как истину).

  • Если вас беспокоит _Bool, тогда true должно быть 1, потому что стандарт требует, чтобы он содержал только 0 и 1. (§6.2.5 / 2).
  • Также в <stdbool.h>макросе trueрасширяется до 1(§7.18 / 3)
  • ==, !=, <, >, <=И >=возвращают 0 или 1 (§6.5.8 / 6, §6.5.9 / 3).
  • !, &&и ||вернуть 0 или 1 (§6.5.3.3 / 5, §6.5.13 / 3, §6.5.14 / 3)
  • defined расширяется до 0 или 1 (§6.10.1 / 1)

Но все стандартные библиотечные функции, например, islowerпросто говорят «ненулевое» для истинности (например, §7.4.1 / 1, §7.17.5.1 / 3, §7.30.2.1 / 1, §7.30.2.2.1 / 4).


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

§6.5.5.3 / 5 : Результатом оператора логического отрицания !является 0, если значение его операнда сравнивается не равным 0, 1, если значение его операнда сравнивается с 0.…

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

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

§6.5.13 / 3 : &&оператор должен выдать 1, если оба его операнда не равны 0; …

§6.5.14 / 3 : ||оператор должен выдать 1, если какой-либо из его операндов не равен 0; …

§6.10.1 / 1 :… он может содержать унарные операторные выражения в форме - defined identifier- или - defined ( identifier )- которые оцениваются в 1, если…

§7.4.1 (Функции классификации символов) / 1 : Функции в этом подпункте возвращают ненулевое значение (истина) тогда и только тогда, когда…

§7.18 / 3 : Остальные три макроса подходят для использования в #ifдирективах предварительной обработки. Это - true- который расширяется до целочисленной константы 1,…

§7.17.5.1 / 3 : atomic_is_lock_freeуниверсальная функция возвращает ненулевое значение (истина) тогда и только тогда, когда операции объекта не блокируются. …

§7.30.2.1 (Функции классификации широких символов) / 1 : Функции в этом подпункте возвращают ненулевое значение (истина) тогда и только тогда, когда…

§7.30.2.2.1 / 4 : iswctypeфункция возвращает ненулевое значение (истина) тогда и только тогда, когда…

Kennytm
источник
23

Есть две области стандарта, о которых вам нужно знать при работе с логическими значениями (под которыми я подразумеваю истинные / ложные значения, а не конкретный bool/_Boolтип C ) в C.

Первый связан с результатом выражений и может быть найден в различных частях C11 6.5 Expressions(например, операторов отношения и равенства). Суть в том, что всякий раз, когда логическое значение генерируется выражением, оно ...

... возвращает 1, если указанное отношение истинно, и 0, если оно ложно. Результат имеет тип int.

Итак, да, результатом любого выражения, генерирующего логическое значение, будет единица для истины или ноль для ложи. Это соответствует тому, что вы найдете в stdbool.hстандартных макросах, trueи falseони определены таким же образом.

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

Опять же, из разных частей 6.5вы увидите такие выражения, как:

||Оператор должен дать 1 , если любой из операндов сравнивать не равно 0; в противном случае возвращается 0. Результат имеет тип int.

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


Кроме того, язык, определяющий, какое значение используется для генерации и интерпретации логических значений, также появился в C99 и C89, так что они существуют уже довольно давно. Даже K&R (второе издание и первое издание ANSI-C ) уточняет это с помощью таких текстовых сегментов, как:

Выражения отношения, такие как i > jи логические выражения, связанные с помощью &&и ||, имеют значение, 1если оно истинно, а 0если ложно.

В тестовой части if, while, forи т.д., «правда» просто означает «не равно нулю».

&&Оператор ... возвращает 1 , если оба операнда сравнения равно нулю, 0 в противном случае.

||Оператор ... возвращает 1 , если либо его операнды сравнения равно нулю, и 0 в противном случае.

Макросы также stdbool.hпоявляются в C99, но не в C89 или K&R, поскольку этот файл заголовка в тот момент не существовал.

Paxdiablo
источник
2
обратите внимание, что ||, ==и !=т. д. yield int, не является логическим типом
MM
2
Голосую за правильный вопрос. Для меня вопрос также касается операторов отношения, а не макросов.
ckruczek
«В тестовой части if, while, forи т.д.,„правда“просто означает„не нуль“.» Это основная часть ответа, и, на мой взгляд, это неудачный выбор Денниса Ричи давным-давно. Любой, кто написал функции, которые обычно возвращают коды ошибок в качестве возвращаемого значения #define noErr 0и любой ненулевой код ошибки, является ошибкой. И тут проблема в простоте и красоте if ( ready_to_do_something() ){do_something();} не работает. Это должно быть: if ( !not_ready_to_do_something() ){do_something();}«Есть много лжи, но только одна правда». ИСТИНА должно быть 0.
Роберт Бристоу-Джонсон
Из любопытства, как первые черновики правил C определяли поведение «&&» и «||» в случае, когда операнды имели значения, отличные от 0 или 1? В цитируемом вами тексте говорится о «логических выражениях», соединенных && и ||, но что, если эти операторы соединяют вещи, отличные от логических выражений?
supercat
1
@sdenham, да. В самом раннем экземпляре K&R, который у меня есть (первое издание, тираж 14, один настолько ранний, что в нем упоминаются аппаратные характеристики четырех типичных машин, PDP-11, Honeywell-6000, IBM-370 и Interdata-8/32), A.7.6/7/10/11(реляционное / равенство / логическое и / логическое или) все указывают, что в результате он дает 0 или 1. Обновили ответ, чтобы включить это.
paxdiablo
10

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

Управляющие операторы работают как, например, ifоператор C11 6.4.8.1:

В обеих формах первая подгруппа выполняется, если выражение не равно 0.

whileи forт. д. имеют то же правило. Это не имеет ничего общего с «истинным» или «ложным».

Что касается операторов, которые предположительно дают логический результат, на самом деле они дают intзначение 1 или 0. Например, операторы равенства, C11 6.5.9:

Каждый из операторов возвращает 1, если указанное отношение истинно, и 0, если оно ложно.

Все вышесказанное объясняется тем, что в C не было логического типа до 1999 года, и даже когда он его получил, вышеуказанные правила не изменились. Таким образом, в отличие от большинства других языков программирования, где операторы и операторы выдают логический тип (например, C ++ и Java), они просто возвращают intзначение с нулевым или ненулевым значением. Например, sizeof(1==1)в C будет 4, а в C ++ - 1.

Фактический логический тип в C назван _Boolи требует современного компилятора. Заголовок stdbool.hопределяет макросы bool, trueи false, которые расширяются до _Bool, 1и 0соответственно (для совместимости с C ++).


Однако считается хорошей практикой программирования рассматривать управляющие операторы и операторы, как если бы они действительно требовали / давали логический тип. Некоторые стандарты кодирования, такие как MISRA-C, рекомендуют такую ​​практику. То есть:

if(ptr == NULL)вместо if(ptr).

if((data & mask) != 0)вместо if(data & mask).

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

if(c == '\0') 

Хорошо, замысел ясен, код самодокументируется.

против

if(c) 

Плохой. Может означать что угодно, и нам нужно искать тип, cчтобы понять код. Это целое число, указатель или символ?

Лундин
источник
1
sizeof(bool)зависит от реализации в C ++. См. Stackoverflow.com/questions/4897844/is-sizeofbool-defined .
Дэвид Хаммен
@DavidHammen Так же, как sizeof (0 == 0) определяется реализацией. Это просто пример.
Lundin
Я думал, что C изменил правила для логических типов. Другие типы типов uintN (включая «битовые» типы многих старых компиляторов) хранят младшие N бит значения и игнорируют любые старшие биты, в то время как новые логические типы эффективно «или» объединяют все биты.
supercat
1
Так должно быть if(ptr != NULL)или возможно if(!ptr)?
Матье К.
1
if(c == '\0')поддается особенно распространенной ошибке программирования для начинающих if(c = '\0'), поэтому я ее избегаю. Согласен, if(c)плохо ... так и должно быть, например,if(valveIsOpen)
ая
4

Я программировал на многих языках. Я видел true равным 1 или -1 в зависимости от языка. Логика, лежащая в основе истинного значения 1, заключалась в том, что бит был либо 0, либо 1. Логика, лежащая в основе истинного значения -1, заключалась в том, что! оператор был дополнением. Он изменил все 1 на 0 и все 0 на 1 в int. Итак, для int! 0 = -1 и! (- 1) = 0. Это меня настолько сбило с толку, что я не сравниваю что-то как == true, а вместо этого сравниваю это как! = False. Таким образом, мой стиль программирования работает на всех языках. Поэтому мой ответ - не беспокоиться об этом, а программировать так, чтобы ваш код работал правильно в любом случае.

Рассел Хэнкинс
источник
как может ! измените все 0 на 1 и все равно получите 0 для! 5?
codehot
@codeshot Не может. Но он подчеркивает, что не все языки обрабатывают операнд! как логическое. Некоторые угощают! как C ~ - то есть побитовое дополнение. В этом случае для определения результирующего значения в первую очередь необходимо знать тип переменной, поэтому! (Uint32_t) 5 будет 4,294,967,290. Но! 0 все равно 4 294 967 295, а 4 294 967 295 - правда.
Pegasus Epsilon
1

Этот ответ требует более внимательного изучения.

Фактическое определение в C ++ заключается в том, что все, кроме 0, считается истиной. Почему это актуально? Поскольку C ++ не знает, что такое целое число, по тому, как мы о нем думаем - мы создаем это значение, все, что он содержит, - это оболочка и правила того, что это означает. Однако он знает, что такое биты, составляющие целое число.

1 как целое число свободно представлено в битах, скажем, 8-битное целое число со знаком как 0000 0001. Часто то, что мы видим визуально, немного ложь, -1 - гораздо более распространенный способ представления этого из-за характера знака. целого числа. Я действительно не могу иметь в виду истинное собственное, почему? Поскольку это НЕ операция, это 1111 1110. Это действительно серьезная проблема для логического значения. Когда мы говорим о логическом, это всего лишь 1 бит - это действительно просто, 0 - ложь, а 1 - истина. Все логические операции просты. Вот почему «-1» следует обозначать как «истина» для целых чисел (со знаком). 1111 1111 NOT превращается в 0000 0000 --- логика верна, и все в порядке. Беззнаковые целые числа - это немного сложно, и в прошлом они использовались гораздо чаще - где 1 означает истину, потому что легко подразумевать логику, которая '

Вот и объяснение. Я говорю, что принятый здесь ответ неверен - в определении C / C ++ нет четкого определения. Логическое значение - это логическое значение, вы можете рассматривать целое число как логическое, но тот факт, что результат является целым числом, ничего не говорит о фактически выполняемой операции, является побитовым.

Томми
источник
4
Вопрос касался C, а не C ++.
glglgl
0

Это произошло из-за операторов отношения в вашем printfутверждении.

Оператор ==и оператор!=

Поскольку (0 == 0)верно так, он дает значение1

тогда как, (0 != 0)это не так, дает значение 0.

SKD
источник