Почему #define TRUE (1==1) в логическом макросе C, а не просто как 1?

Я видел определения в C

#define TRUE (1==1)
#define FALSE (!TRUE)

Это необходимо? В чем преимущество простого определения TRUE как 1 и FALSE как 0?


person Community    schedule 09.06.2013    source источник
comment
И еще: #define TRUE (’/’/’/’); #define FALSE (’-’-’-’) (взято со страницы coding-guidelines.com/cbook/cbook1_1.pdf 871)   -  person osgx    schedule 09.06.2013
comment
Нет, на самом деле это паранойя невежественных... недоосведомленных. В C 1 и 0 делают одно и то же при любых обстоятельствах.   -  person Jens    schedule 09.06.2013
comment
@osgx Что это значит?   -  person mrgloom    schedule 09.07.2019


Ответы (8)


Этот подход будет использовать фактический тип boolean (и разрешать в true и false), если компилятор его поддерживает. (в частности, С++)

Однако было бы лучше проверить, используется ли C++ (с помощью макроса __cplusplus), и действительно использовать true и false.

В компиляторе C это эквивалентно 0 и 1.
(обратите внимание, что удаление круглых скобок нарушит это из-за порядка операций)

person SLaks    schedule 09.06.2013
comment
Это неверно, bool здесь не используются. Результатом 1==1 является int. (см. stackoverflow.com/questions /7687403/ .) - person Mat; 09.06.2013
comment
@Mat: Даже на C++ с типом boolean? - person SLaks; 09.06.2013
comment
Вопрос помечен как C, но действительно в C++ реляционные операторы возвращают true или false. - person Mat; 09.06.2013
comment
@Mat: я предполагаю, что такой код написан в заголовках C, чтобы хорошо работать с C++ - person SLaks; 09.06.2013
comment
C++ имеет такое же неявное преобразование интегральных значений в логические значения, что и C, но я не понимаю, как это связано с этим. - person Mat; 09.06.2013
comment
@SLaks Если бы он хотел хорошо играть с C ++, он бы #define TRUE true и #define FALSE false всякий раз, когда определяется __cplusplus. - person Nikos C.; 09.06.2013
comment
@НикосС. Но вы не написали заголовок - может быть, тот, кто написал, подумал, что он умничает. - person Sebastian Redl; 10.06.2013
comment
@Mat Неявные преобразования не применяются в контекстах varargs, поэтому этот тип определения хорошо работает там. - person Barmar; 11.06.2013
comment
@Barmar: учитывая, что правила продвижения применяются к varargs, вы все равно получите целые числа, а не логические значения (если только у вас нет логических значений больше, чем целые числа?). - person Mat; 11.06.2013
comment
Логические типы @Mat не существуют в языке C. Вот почему вам нужно использовать int 0 и 1 - person ol_v_er; 12.06.2013
comment
@Mat Хорошо, я не знал. Спасибо. Но, возможно, большая часть кода находится не в C11... - person ol_v_er; 12.06.2013
comment
@Mat К сожалению, многие разработчики все еще работают со старыми версиями C (по разным причинам), поэтому это хороший способ обеспечить совместимость со старыми версиями, а не работать только с последней версией. - person NitroG42; 12.06.2013
comment
@slake я так понимаю? Это сайт вопросов и ответов, а не вопросов и предположений. - person Isaac Rabinovitch; 14.06.2013
comment
@IsaacRabinovitch: Мы не можем не догадываться о намерениях человека, написавшего этот код. - person SLaks; 14.06.2013
comment
Они могли просто #define TRUE (!0) #define FALSE (!TRUE) - person Ayxan Haqverdili; 29.08.2020

Ответ — переносимость. Числовые значения TRUE и FALSE не важны. Что важно, так это то, что оператор типа if (1 < 2) оценивается как if (TRUE), а оператор типа if (1 > 2) оценивается как if (FALSE).

Конечно, в C (1 < 2) оценивается как 1, а (1 > 2) оценивается как 0, поэтому, как говорили другие, с точки зрения компилятора нет никакой практической разницы. Но позволяя компилятору определять TRUE и FALSE в соответствии с его собственными правилами, вы делаете их значения явными для программистов и гарантируете согласованность в вашей программе и любой другой библиотеке (при условии, что другая библиотека соответствует стандартам C... вы был бы поражен).


Немного истории
Некоторые BASIC определяют FALSE как 0, а TRUE как -1. Как и многие современные языки, они интерпретировали любое ненулевое значение как TRUE, но оценивали логические выражения, которые были истинными, как -1. Их операция NOT была реализована путем добавления 1 и перестановки знака, потому что это было эффективно. Таким образом, «НЕ x» стало -(x+1). Побочным эффектом этого является то, что значение, подобное 5, оценивается как TRUE, а NOT 5 оценивается как -6, что также равно TRUE! Найти такой баг не весело.

Рекомендации
Учитывая правила де-факто, согласно которым ноль интерпретируется как FALSE, а любое ненулевое значение интерпретируется как TRUE, вам следует никогда не сравнивайте логические выражения с TRUE или FALSE. Примеры:

if (thisValue == FALSE)  // Don't do this!
if (thatValue == TRUE)   // Or this!
if (otherValue != TRUE)  // Whatever you do, don't do this!

Почему? Потому что многие программисты используют ярлык обработки ints как bools. Они не совпадают, но компиляторы обычно это допускают. Так, например, совершенно законно писать

if (strcmp(yourString, myString) == TRUE)  // Wrong!!!

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

0, если yourString == myString
‹0, если yourString < myString
>0, если yourString > myString

Таким образом, строка выше возвращает TRUE только тогда, когда yourString > myString.

Правильный способ сделать это либо

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))

or

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)

Так же:

if (someBoolValue == FALSE)     // Redundant.
if (!someBoolValue)             // Better.
return (x > 0) ? TRUE : FALSE;  // You're fired.
return (x > 0);                 // Simpler, clearer, correct.
if (ptr == NULL)                // Perfect: compares pointers.
if (!ptr)                       // Sleazy, but short and valid.
if (ptr == FALSE)               // Whatisthisidonteven.

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

Разве это того не стоит?

person Adam Liss    schedule 09.06.2013
comment
(1==1) не более переносим, ​​чем 1. Собственные правила компилятора — это правила языка C, которые ясно и недвусмысленно относятся к семантике равенства и операций отношения. Я никогда не видел, чтобы компилятор ошибался в этом. - person Keith Thompson; 10.06.2013
comment
На самом деле известно, что значение, возвращаемое strcmp, меньше, равно или больше 0. Не гарантируется, что оно будет -1, 0 или 1, и в дикой природе есть платформы, которые не возвращают эти значения для ускорения реализации. . Итак, если strcmp(a, b) == TRUE, то a > b, но обратное значение может быть неверным. - person Maciej Piechotka; 10.06.2013
comment
@MaciejPiechotka - Совершенно верно; спасибо, что указали на ошибку. Я исправил свое описание. - person Adam Liss; 10.06.2013
comment
@KeithThompson - Возможно, переносимость была неправильным термином. Но факт остается фактом: (1==1) — логическое значение; 1 нет. - person Adam Liss; 10.06.2013
comment
@AdamLiss: в C (1==1) и 1 являются константными выражениями типа int со значением 1. Они семантически идентичны. Я полагаю, что вы можете написать код, рассчитанный на читателей, которые этого не знают, но где он заканчивается? - person Keith Thompson; 10.06.2013
comment
Числовое значение TRUE, безусловно, важно. Это важная часть булевой алгебры, которая определяет ее таким образом, чтобы поддерживать правильное соответствие. между булевыми операциями и целочисленными. - person jthill; 10.06.2013
comment
'not' 5 действительно равно -6 на битовом уровне. - person woliveirajr; 10.06.2013

Трюк с (1 == 1) полезен для определения TRUE способом, прозрачным для C, но обеспечивающим лучшую типизацию в C++. Один и тот же код может быть интерпретирован как C или C++, если вы пишете на диалекте под названием «Чистый C» (который компилируется как C или C++) или если вы пишете заголовочные файлы API, которые могут использоваться программистами C или C++.

В единицах перевода C 1 == 1 имеет точно такое же значение, как 1; и 1 == 0 имеет то же значение, что и 0. Однако в единицах перевода C++ 1 == 1 имеет тип bool. Поэтому макрос TRUE, определенный таким образом, лучше интегрируется в C++.

Примером лучшей интеграции является то, что, например, если функция foo имеет перегрузки для int и для bool, то foo(TRUE) выберет перегрузку bool. Если TRUE просто определено как 1, то это не будет хорошо работать в C++. foo(TRUE) захочет перегрузку int.

Конечно, C99 представил bool, true и false, и их можно использовать в файлах заголовков, которые работают с C99 и с C.

Тем не мение:

  • эта практика определения TRUE и FALSE как (0==0) и (1==0) предшествовала C99.
  • все еще есть веские причины держаться подальше от C99 и работать с C90.

Если вы работаете в смешанном проекте C и C++ и не хотите использовать C99, вместо этого определите строчные буквы true, false и bool.

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif

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


В случае, если объяснение C++ не было ясным, вот тестовая программа:

#include <cstdio>

void foo(bool x)
{
   std::puts("bool");  
}

void foo(int x)
{
   std::puts("int");  
}

int main()
{
   foo(1 == 1);
   foo(1);
   return 0;
}

Выход:

bool
int

Что касается вопроса из комментариев о том, как перегружаются функции C++, относящиеся к смешанному программированию на C и C++. Они просто иллюстрируют разницу типов. Веская причина для того, чтобы константа true была bool при компиляции как C++, - это чистая диагностика. На самых высоких уровнях предупреждений компилятор C++ может предупредить нас о преобразовании, если мы передаем целое число в качестве параметра bool. Одной из причин написания на чистом C является не только то, что наш код более переносим (поскольку его понимают компиляторы C++, а не только компиляторы C), но и то, что мы можем извлечь пользу из диагностических мнений компиляторов C++.

person Kaz    schedule 09.06.2013
comment
Отличный и недооцененный ответ. Совсем не очевидно, что два определения TRUE будут различаться в C++. - person user4815162342; 10.06.2013
comment
Как перегруженные функции относятся к коду, который компилируется как на C, так и на C++? - person Keith Thompson; 10.06.2013
comment
@KeithThompson Речь идет не только о перегрузке, но и о правильном вводе текста в целом, перегрузка - это просто наиболее практичный пример, когда дело касается. Конечно, код C++ без перегрузок, шаблонов и всего того сложного, удаленного для C-совместимости, вообще не заботится о типах, но это не значит, что нужно отвергать концептуальную ограничения типов на данном языке. - person Christian Rau; 10.06.2013
comment
@ChristianRau: Что вы имеете в виду, не особо заботясь о типах? Типы занимают центральное место в языке C; каждое выражение, значение и объект в программе на языке C имеет четко определенный тип. Если вы хотите определить что-то по-разному в C и в C++ (в редких случаях, когда вам действительно нужно написать код, который компилируется как в C, так и в C++), вы можете использовать #ifdef __cplusplus, чтобы выразить свое намерение гораздо больше. четко. - person Keith Thompson; 10.06.2013
comment
@KeithThompson Да, я знаю, насколько важны типы. Просто без всех вещей с учетом типов, таких как перегрузка и шаблоны, такие вещи, как различие между bool и int, не имеют большого значения на практике, поскольку они неявно конвертируются друг в друга (и в C на самом деле то же самое, обратите внимание на кавычки), и не так много ситуаций, в которых вам действительно нужно устранить неоднозначность между ними. не много было, вероятно, слишком тяжелым, намного меньше по сравнению с кодом, использующим шаблоны и перегрузку, возможно, было бы лучше. - person Christian Rau; 10.06.2013
comment
@KeithThompson Если вы хотите определить что-то по-разному в C и в C++... - я также знаю это, и я никогда не защищал (0==0) как подходящий способ определить TRUE, не зависящий от языка, и я согласен что блок #ifdef __cplusplus намного понятнее. Но тем не менее этот макрос - способ сделать это, пусть и не самый лучший. Единственное, что вызвало мой комментарий, это то, что вы прыгнули на пример с перегруженными функциями, тогда как это было просто примером более внутренней и концептуальной проблемы, необходимости правильного ввода (что вы, как предполагает ваш предыдущий комментарий, также высоко цените) . - person Christian Rau; 10.06.2013
comment
@Kaz, я знаю, что некоторых людей (по разным причинам) не волнует C99 или новее, но, по крайней мере, ваш блок typedef должен проверять, определен ли уже bool, в этом случае используйте это определение, это будет real< /i> чистый C, чтобы он компилировался семантически одинаковым для всех платформ и всех ISO C или C++, если на то пошло - person Sebastian; 10.07.2014

#define TRUE (1==1)
#define FALSE (!TRUE)

эквивалентно

#define TRUE  1
#define FALSE 0

in C.

Результатом операторов отношения является 0 или 1. 1==1 гарантированно будет оцениваться как 1, а !(1==1) гарантированно будет оцениваться как 0.

Нет абсолютно никаких причин использовать первую форму. Обратите внимание, что первая форма, однако, не менее эффективна, поскольку почти во всех компиляторах константное выражение вычисляется во время компиляции, а не во время выполнения. Это разрешено в соответствии с этим правилом:

(C99, 6.6p2) «Константное выражение может оцениваться во время трансляции, а не во время выполнения, и, соответственно, может использоваться в любом месте, где может быть константа».

PC-Lint даже выдаст сообщение (506, логическое значение константы), если вы не используете литерал для макросов TRUE и FALSE:

Для C TRUE должно быть определено как 1. Однако в других языках используются числа, отличные от 1, поэтому некоторые программисты считают, что !0 перестраховывается.

Также в C99 определения stdbool.h для логических макросов true и false напрямую используют литералы:

#define true   1
#define false  0
person ouah    schedule 09.06.2013
comment
У меня есть сомнения, TRUE заменяется при каждом использовании на 1 == 1, в то время как простое использование 1 заменяет 1, не является ли первый метод дополнительными накладными расходами для сравнения ... или он сделан оптимизированным компилятором? - person pinkpanther; 09.06.2013
comment
Константные выражения @pinkpanther обычно оцениваются во время компиляции и поэтому не вызывают никаких накладных расходов. - person ouah; 09.06.2013
comment
1==1 переходит в 1 вместо неопределенного ненулевого значения? - person djechlin; 09.06.2013
comment
1==1 гарантированно будет оцениваться как 1 - person ouah; 09.06.2013
comment
Одного я не понимаю, так как только false требуется иметь конкретное значение, а именно 0, то почему не принято определять false как 0, а true как (!0)? - person Nikos C.; 09.06.2013
comment
@НикосС. это хороший вопрос. Это имеет значение для кода формы if(foo == true), который превратится из просто плохой практики в откровенно глючную. - person djechlin; 09.06.2013
comment
@NikosC.: (!0) является 1 по определению оператора !. - person Keith Thompson; 09.06.2013
comment
+1 за указание на опасности в (x == TRUE) может иметь другое значение истины, чем x. - person Joshua Taylor; 09.06.2013
comment
Я выполнил работу и посмотрел на спецификацию: оператор && должен дать 1, если оба его операнда не равны 0, в противном случае он дает 0. Результат имеет тип int. Тот же язык для всех других условных операторов. Источник: flash-gordon.me.uk/ansi.c.txt. все, что не 0, истинно, но сравнения или логические операторы всегда возвращают 1, а не просто любое ненулевое значение. - person starmole; 10.06.2013

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

Очевидно, что C говорит, что они совместимы, но вы можете запретить преднамеренное использование этой функции, чтобы помочь выявить ошибки — например, когда кто-то мог перепутать & и &&, или они напортачили со своим приоритетом оператора.

person sh1    schedule 09.06.2013
comment
Это хороший момент, и, возможно, некоторые из этих инструментов могут даже поймать глупый код if (boolean_var == TRUE) путем расширения до if (boolean_var == (1 == 1)), который благодаря расширенной информации о типе узла (1 == 1) попадает в шаблон if (<*> == <boolean_expr>). - person Kaz; 09.06.2013

Практической разницы нет. 0 оценивается как false, а 1 оценивается как true. Тот факт, что вы используете логическое выражение (1 == 1) или 1 для определения true, не имеет значения. Они оба оцениваются как int.

Обратите внимание, что стандартная библиотека C предоставляет специальный заголовок для определения логических значений: stdbool.h.

person Shoe    schedule 09.06.2013
comment
конечно, нет... но некоторые люди могут думать иначе, особенно для отрицательных чисел, вот почему :) - person pinkpanther; 09.06.2013
comment
Какие? У тебя наоборот. true оценивается как 1, а false оценивается как 0. C не знает о нативных логических типах, это просто целые числа. - person djechlin; 09.06.2013
comment
В C операторы отношения и равенства дают результаты типа int со значением 0 или 1. C действительно имеет логический тип (_Bool, с макросом bool, определенным в <stdbool.h>, но он был добавлен только в C99, который не изменил семантику операторов для использования нового типа. - person Keith Thompson; 09.06.2013
comment
@djechlin: согласно стандарту 1999 года C действительно имеет собственный логический тип. Он называется _Bool, а <stdbool.h> имеет #define bool _Bool. - person Keith Thompson; 09.06.2013
comment
@KeithThompson, вы правы в том, что 1 == 1 оценивается как int. Отредактировано. - person Shoe; 09.06.2013

Мы не знаем точного значения, которому равно TRUE, и компиляторы могут иметь свои собственные определения. Итак, вы используете внутреннюю компилятора для определения. Это не всегда необходимо, если у вас есть хорошие привычки программирования, но вы можете избежать проблем из-за плохого стиля кодирования, например:

если ((а > б) == ИСТИНА)

Это может быть катастрофой, если вы вручную определите TRUE как 1, в то время как внутреннее значение TRUE будет другим.

person capiggue    schedule 10.06.2013
comment
В C оператор > всегда возвращает 1 для истинного значения и 0 для ложного. Нет никакой возможности, чтобы какой-либо компилятор C ошибся. Сравнения на равенство с TRUE и FALSE неуместны; выше более четко написано как if (a > b). Но идея о том, что разные компиляторы C могут по-разному обрабатывать истину и ложь, просто неверна. - person Keith Thompson; 15.10.2013

  1. Пункт списка

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

#define TRUE 1 
#define FALSE 0

Однако любое число, не равное 0, также будет оцениваться как истинное в условном выражении. Поэтому, используя следующее:

#define TRUE (1==1)
#define FALSE (!TRUE)

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

person Sabashan Ragavan    schedule 09.06.2013
comment
Я бы не назвал это перестраховкой — скорее, вы даете себе ложное чувство безопасности. - person dodgethesteamroller; 10.06.2013