Чому #define TRUE (1 == 1) в булевому макросі C, а не просто 1?


160

Я бачив визначення в С

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

Це потрібно? Яка користь у тому, щоб просто визначити TRUE як 1, а FALSE як 0?


35
І більше #define TRUE (’/’/’/’):; #define FALSE (’-’-’-’)(взято з coding-guidelines.com/cbook/cbook1_1.pdf сторінка 871)
osgx

2
Ні, це параноїя від неохайного ^ Wunderinformed, дійсно. У З 1 і 0 роблять за будь-яких обставин однакові.
Єнс

@osgx Що це означає?
mrgloom

Відповіді:


155

Цей підхід використовуватиме фактичний booleanтип (і вирішує trueта false), якщо компілятор підтримує його. (конкретно, C ++)

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

У компіляторі C це еквівалентно 0та 1.
(зауважте, що видалення дужок порушить це через порядок операцій)


7
Це неправильно, булі тут не використовуються. Результатом 1==1є int. (див. stackoverflow.com/questions/7687403/… .)
Мат

4
@Mat: Навіть у C ++, з booleanтипом?
Слакс

9
Питання позначено тегом C, але дійсно в C ++ реляційні оператори повертаються trueабо false.
Мат

5
@Mat: Я здогадуюсь, що такий код написаний у заголовках C, щоб добре грати з C ++
SLaks

20
@SLaks Якщо б він хотів грати добре зі C ++, він би #define TRUE trueі #define FALSE falseколи __cplusplusбуло визначено.
Нікос К.

137

Відповідь - портативність. Чисельні значення TRUEта FALSEне важливі. Що є важливим є те , що заява , якif (1 < 2) має значення if (TRUE)і заяву , як має if (1 > 2)значення if (FALSE).

Зазначено, що в C (1 < 2)оцінюється 1і (1 > 2)оцінюється 0, так як, як вже говорили інші, практичної різниці щодо компілятора немає. Але дозволяючи компілятору визначити TRUEіFALSE відповідно до його власних правил, ви робите їх значення явними програмістам, і ви гарантуєте послідовність у вашій програмі та будь-якій іншій бібліотеці (якщо припустити, що інша бібліотека відповідає стандартам C ... ви б дивуватися).


Деяка історія
Деякі основи визначені FALSEяк 0і TRUEяк -1. Як і багато сучасних мов, вони інтерпретували будь-яке ненульове значення як TRUE, але вони оцінювали булеві вирази, що були істинними як -1. Їх NOTфункціонування було реалізовано додаванням 1 та перевертанням знаку, оскільки це було ефективно зробити так. Так 'НЕ х' став -(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 якbool s. Вони не однакові, але компілятори зазвичай це дозволяють. Так, наприклад, писати цілком законно

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

Це виглядає законним, і компілятор з радістю прийме це, але, ймовірно, не робить те, що ви хотіли. Це тому , що повертається значення strcmp()IS

      0, якщо yourString == myString
    <0, якщоyourString < myString
    > 0, якщоyourString > myString

Отже, рядок вище повертається TRUE лише тоді, коли yourString > myString.

Правильний спосіб зробити це - будь-який

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

або

// 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.

Часто ви знайдете деякі з цих "поганих прикладів" у виробничому коді, і багато досвідчених програмістів присягаються їм: вони працюють, деякі коротші, ніж їх (педантично?) Правильні альтернативи, і ідіоми майже загальновизнані. Але врахуйте: «правильні» версії не менш ефективні, вони гарантовано портативні, вони пройдуть навіть найсуворіші вкладиші, і навіть нові програмісти їх зрозуміють.

Хіба цього не варто?


6
(1==1)не більш портативний, ніж 1. Власні правила компілятора - це мова С, що чітко і однозначно стосується семантики операторів рівності та реляції. Я ніколи не бачив, як компілятор помилявся з цим матеріалом.
Кіт Томпсон

1
Насправді значення, яке повертається, strcmpяк відомо, є меншим, рівним або більшим, ніж 0. Це не гарантовано, що буде -1, 0 або 1, і в дикій природі є платформи, які не повертають ці значення, щоб отримати швидкість реалізації. Отже, якщо strcmp(a, b) == TRUEтоді, a > bале зворотне значення не може мати значення.
Maciej Piechotka

2
@KeithThompson - Можливо, "переносимість" була неправильним терміном. Але факт залишається фактом, що (1 == 1) булева величина; 1 ні.
Адам Лісс

2
@AdamLiss: В C (1==1)і 1обидва є постійними виразами типу intзі значенням 1. Вони семантично однакові. Я думаю, ви можете написати код, який задовольняє читачів, які цього не знають, але де це закінчується?
Кіт Томпсон

2
"не" 5, дійсно, -6, на рівні бітів.
woliveirajr

51

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

У одиницях перекладу С 1 == 1має точно таке значення, як і 1; і 1 == 0має те саме значення, що і 0. Однак у підрозділах перекладу С ++ 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)передує С99.
  • Є все-таки вагомі причини залишатися подалі від 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параметра. Однією з причин написання в Clean C є не тільки те, що наш код є більш портативним (оскільки його розуміють компілятори C ++, не лише компілятори C), але ми можемо отримати користь від діагностичних думок компіляторів C ++.


3
Відмінна і занижена відповідь. Зовсім не очевидно, що два визначення визначення TRUEбудуть відрізнятися під C ++.
user4815162342

4
Як перевантажені функції мають відношення до коду, який компілюється як C, так і C ++?
Кіт Томпсон

@KeithThompson Йдеться не лише про перевантаження, а про правильне введення тексту в цілому, перевантаження - це лише найпрактичніший приклад, коли він починає грати. Звичайно, код C ++ без перевантажень, шаблонів і всього того, що "складні" речі вилучені для "C-сумісності", насправді не дуже хвилюють типи, але це не означає, що не слід скидати концептуальні обмеження типу даною мовою .
Крістіан Рау

1
@ChristianRau: Що ти маєш на увазі "не дуже сильно піклується про типи"? Типи є центральними для мови С; кожен вираз, значення та об'єкт у програмі C мають чітко визначений тип. Якщо ви хочете щось інакше визначити як на C, так і на C ++ (у рідкісних випадках, коли вам потрібно записати код, який компілюється як C, так і C ++), ви можете використовувати #ifdef __cplusplusдля вираження свого наміру набагато чіткіше.
Кіт Томпсон

@KeithThompson Так, я знаю, наскільки важливі типи. Просто без усіх відомих типів матеріалів, таких як перевантаження і шаблони, такі речі, як диференціація між собою boolі intне мають великого значення на практиці, оскільки вони неявно конвертуються один в одного (а в С насправді "однакові" , зверніть увагу на цитати , хоча) і не так багато ситуацій, в яких вам дійсно потрібно роз'єднати між собою. "не так багато", ймовірно, було занадто важким, "набагато менше порівняно з кодом із використанням шаблонів і перевантаження", можливо, було б краще
Крістіан Рау

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

еквівалентно

#define TRUE  1
#define FALSE 0

в С.

Результатом роботи реляційних операторів є 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

1
У мене є сумніви, TRUE замінюється при кожному використанні на 1 == 1, тоді як лише за допомогою 1 замінить 1, чи не перший метод додаткового порівняння накладних витрат ... чи це оптимізований компілятор?
pinkpanther

4
Постійні вирази @pinkpanther зазвичай оцінюються під час компіляції, і тому вони не викликають накладних витрат.
оуа

2
1==1гарантовано буде оцінено до1
1313

3
@NikosC. це якесь гарне питання. Це важливо для коду форми if(foo == true), який буде переходити від просто поганої практики до відвертої баггі.
djechlin

1
+1 для вказівки на небезпеку (x == TRUE)може мати інше значення істини, ніж x.
Джошуа Тейлор

12

Крім C ++ (вже згадуваного), ще одна перевага - це інструменти статичного аналізу. Компілятор позбавить від будь-якої неефективності, але статичний аналізатор може використовувати власні абстрактні типи для розмежування результатів порівняння та інших цілих типів, тому він неявно знає, що TRUE повинен бути результатом порівняння і не слід вважати сумісним з цілим числом.

Очевидно, C говорить, що вони сумісні, але ви можете заборонити навмисне використання цієї функції, щоб допомогти виділити помилки - наприклад, де хтось може заплутатися &і &&чи переплутав їх пріоритет оператора.


1
Це хороший момент, і, можливо, деякі з цих інструментів можуть навіть зловити нерозумний код if (boolean_var == TRUE) за допомогою розширення, до if (boolean_var == (1 == 1))якого завдяки розширеному типу інформації (1 == 1)вузол потрапляє в шаблон if (<*> == <boolean_expr>).
Каз

4

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

Зверніть увагу , що стандартна бібліотека C надає спеціальний заголовок для визначення булевих: stdbool.h.


Звичайно, ви цього не зробили ... але деякі люди можуть подумати інакше, особливо для негативних цифр, тому :)
pinkpanther

Що? У вас це є назад. trueоцінюється 1і falseоцінюється до 0. C не знає про рідні булеві типи, вони просто ints.
djechlin

У C оператори реляції та рівності дають результати типу int, зі значенням 0або 1. C має фактичний булевий тип ( _Boolз макросом, boolвизначеним у <stdbool.h>, але це було додано лише у C99, що не змінило семантику операторів для використання нового типу.
Кіт Томпсон

@djechlin: По стандарту 1999 року, C робить є рідний логічний тип. Це називається _Boolі <stdbool.h>є #define bool _Bool.
Кіт Томпсон

@KeithThompson, ви праві, що вас 1 == 1оцінюють як int. Відредаговано.
Взуття

3

Ми не знаємо точного значення, якому TRUE дорівнює, і компілятори можуть мати свої власні визначення. Отже, те, що ти приводиш, - це використовувати внутрішній компілятор для визначення. Це не завжди потрібно, якщо у вас є хороші звички програмування, але ви можете уникнути проблем із поганим стилем кодування, наприклад:

якщо ((a> b) == ІСТИНА)

Це може бути катастрофою, якщо вручну визначити TRUE як 1, тоді як внутрішнє значення TRUE - інше.


У C >оператор завжди дає 1 для true, 0 для false. Немає можливості жодного компілятора C зрозуміти це неправильно. Порівняння рівності TRUEта FALSEпоганий стиль; вище сказане більш чітко записано як if (a > b). Але думка про те, що різні компілятори C можуть по-різному ставитися до істини та неправди, просто неправильна.
Кіт Томпсон

2
  1. Елемент списку

Зазвичай в мові програмування на C 1 визначається як істинне, а 0 - як хибне. Отже, чому ви бачите таке досить часто:

#define TRUE 1 
#define FALSE 0

Однак будь-яке число, яке не дорівнює 0, буде оцінено істинним, як і в умовному твердженні. Тому, використовуючи наведене нижче:

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

Можна просто явно показати, що ви намагаєтеся захистити його безпечно, зробивши помилкою рівну тому, що не відповідає дійсності.


4
Я б не назвав це "грою в безпеку" - скоріше, ти даєш собі помилкове почуття безпеки.
dodgethesteamroller
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.