Використання булевих значень у С


Відповіді:


1050

Від кращого до гіршого:

Варіант 1 (C99)

#include <stdbool.h>

Варіант 2

typedef enum { false, true } bool;

Варіант 3

typedef int bool;
enum { false, true };

Варіант 4

typedef int bool;
#define true 1
#define false 0

Пояснення

  • Варіант 1 працює лише в тому випадку, якщо ви використовуєте C99 і це "стандартний спосіб" зробити це. Виберіть це, якщо можливо.
  • Варіанти 2, 3 і 4 на практиці матимуть однакову поведінку. # 2 і # 3 не використовують #defines, що, на мій погляд, краще.

Якщо ви не визначилися, ідіть з №1!


1
Чи можете ви детальніше пояснити, чому вони найкращі та гірші?
ендоліт

1
@endolith Вирівнювання, оптимізація та спосіб зберігання вибраного <stdbool.h> boolкомпілятора можуть бути більш підходящими за цільовим призначенням булевого значення, ніж використання int(тобто компілятор може вирішити реалізувати boolінакше, ніж an int). Це також може призвести до більш жорсткої перевірки типу під час компіляції, якщо вам пощастить.
blubberdiblub

1
Навіщо використовувати intдля bool? Це марно. Використовуйте unsigned char. Або використовувати вбудовану команду мови C _Bool.
користувач12211554

@NoBody Використання меншого типу може економити на пам'яті, але це може не зробити його швидшим. Часто швидше використовувати вихідний розмір слова процесора, а не менший розмір, оскільки це може вимагати від компілятора зробити бітові зрушення, щоб правильно їх вирівняти
Тед Кляйн Бергман

241

Кілька думок про булеви в C:

Я досить старий, що я просто використовую звичайний ints як мій булевський тип без будь-яких typedefів або спеціальних визначень чи перерахунків для правдивих / хибних значень. Якщо ви дотримуєтесь моєї пропозиції нижче щодо того, щоб ніколи не порівнювати з булевими константами, то вам потрібно лише використовувати 0/1 для ініціалізації прапорів. Однак такий підхід може вважатися надто реакційним у ці сучасні часи. У цьому випадку, безумовно, слід використовувати, <stdbool.h>оскільки це принаймні має перевагу стандартизації.

Як би не називали булеві константи, використовуйте їх лише для ініціалізації. Ніколи не писати щось подібне

if (ready == TRUE) ...
while (empty == FALSE) ...

Їх завжди можна замінити яснішими

if (ready) ...
while (!empty) ...

Зауважте, що їх можна насправді розумно і зрозуміло читати вголос.

Дайте своїм булевим змінним позитивні назви, тобто fullзамість notfull. Останнє призводить до коду, який важко легко прочитати. Порівняйте

if (full) ...
if (!full) ...

з

if (!notfull) ...
if (notfull) ...

Обидві колишньої пари читають природно, тоді !notfullяк незручно читати, як це є, і стає набагато гірше у складніших булевих виразах.

Булові аргументи, як правило, слід уникати. Розглянемо функцію, визначену так

void foo(bool option) { ... }

У тій частині функції дуже ясно, що означає аргумент, оскільки він має зручне і, сподіваємось, значуще ім'я. Але, схожі сайти викликів

foo(TRUE);
foo(FALSE):

Тут, по суті, неможливо сказати, що означав параметр, не дивлячись завжди на визначення функції або декларацію, і це стає набагато гіршим, як тільки ви додасте ще більш булеві параметри. Я пропоную будь-яке

typedef enum { OPT_ON, OPT_OFF } foo_option;
void foo(foo_option option);

або

#define OPT_ON true
#define OPT_OFF false
void foo(bool option) { ... }

У будь-якому випадку сайт виклику зараз виглядає так

foo(OPT_ON);
foo(OPT_OFF);

що читач має принаймні шанс зрозуміти, не дратуючи визначення foo.


1
І як ви порівнюєте дві змінні для рівності? Ніколи не використовуйте булеві константи чудово, але це не вирішує проблему при порівнянні з непостійною.
Барух

Пробачте, але я не розумію питання. Ви запитуєте, як я порівнюю дві булеві змінні для рівності? Якщо так, не a == bпрацює?
Дейл Хагглунд

5
@Kenji Те, що ви говорите, є правдивим, хоча я вважаю, що використання значень, відмінних від одного, як еквівалента істинного, майже завжди є поганою ідеєю. Таким чином , у вашому прикладі, за умови , що aі bпідраховувати від нуля, я б рекомендував a > 0 == b > 0замість цього. Якщо ви наполягаєте на тому, щоб скористатися правдивістю довільних ненульових значень, ви отримаєте !!varбулеве значення 0/1, еквівалентне тому var, щоб ви могли написати !!a == !!b, хоча досить багато читачів вважають це заплутаним.
Дейл Хагглунд

3
!a == !bтакож достатньо для перевірки рівності, ненулі стають нульовими, а нулі - цілими.
ryanpattison

5
@rpattiso Ви, звичайно, маєте рацію, але, мабуть, я би читав !!aяк "конвертувати небулевий a в його еквівалентне значення істини", тоді як я читав би !aяк "логічно інвертувати булева змінна a". Зокрема, я хотів би шукати якусь конкретну причину, якою хотілося логічної інверсії.
Дейл Хагглунд


74

Ось версія, яку я використав:

typedef enum { false = 0, true = !false } bool;

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

Це вирішує проблему, коли хтось кодує щось, що зводиться до цього:

if (true == !false)

Я думаю, що ми всі погодимось з тим, що це не є хорошою практикою, але за разову вартість виконання "true =! False" ми усуваємо цю проблему.

[EDIT] Зрештою, я використав:

typedef enum { myfalse = 0, mytrue = !myfalse } mybool;

щоб уникнути зіткнення імен з іншими схемами, які визначали trueі false. Але концепція залишається такою ж.

[EDIT] Щоб показати перетворення цілого числа в булеве:

mybool somebool;
int someint = 5;
somebool = !!someint;

Перший (найправильніше)! перетворює ненульове ціле число в 0, потім друге (найбільше ліворуч)! перетворює 0 у myfalseзначення. Я залишу це як вправу для читача для перетворення нульового цілого числа.

[EDIT] У моєму стилі використовувати явне встановлення значення в enum, коли потрібне конкретне значення, навіть якщо значення за замовчуванням було б однаковим. Приклад: Тому що помилковим потрібно нуль, я використовую, false = 0,а неfalse,


5
Крім того, ще одна перевага використання перерахувань є інтеграція IDE - true, falseі boolвиділяються в більшості IDE, тому що вони є значеннями перерахувань і ЬурейеЕ, на відміну від #defines, які рідко коли - або підсвічування синтаксису.

Цікаво: Ігноруючи, чи працює він чи ні, чи дійсно C (99+), щоб дозволити перерахунку посилатися на попереднє значення в тому ж переліку ?

@ tgm1024 gcc -ansi -pedantic -Wallне дає попереджень, тому я довіряю gcc; Якщо це працює навіть для c89цього, він повинен працювати c99також.
yyny

1
"Оскільки false має лише одне значення, але логічна правда може мати багато значень, але техніка встановлює true таким, яким компілятор буде використовувати для протилежного false." Оператор заперечення !може повертати лише значення 0і 1, таким чином true = !false, завжди призначатиме значення 1. Цей метод не забезпечує додаткової безпеки typedef enum { false, true } bool;.
user694733

1
Раніше я знайшов це з C90 (6.3.3.3 Одинарні арифметичні оператори): "Результат оператора логічного заперечення! Дорівнює 0, якщо значення його операнда порівнюється з нерівним рівним 0. 1, якщо значення його операнда порівнюється з 0. Результат має тип int. Вираз! E еквівалентно (O == E). " Це повинно охоплювати будь-який компілятор, який коли-небудь стверджував, що підтримує стандарт C. Компілятори, звичайно, можуть юридично ігнорувати це правило у випадках, коли це не має значення для спостережуваної поведінки (наприклад if(!value)), але цей виняток не застосовується в цьому конкретному випадку.
user694733


30

Насамперед. C, тобто ISO / IEC 9899, ​​має булевий тип вже 19 років . Це набагато довший час, ніж очікувана тривалість кар'єри програмування на С, з поєднанням аматорських / академічних / професійних під час відвідування цього питання . Шахта перевершує це, можливо, лише на 1-2 роки. Це означає, що за час , коли пересічний читач взагалі що-небудь дізнався про C, C насправді мав бульний тип даних .

Для типу даних, #include <stdbool.h>і використання true, falseі bool. Або не включайте його, а використовуйте _Bool, 1а 0замість цього.


В інших відповідях на цю тему пропонуються різні небезпечні практики. Я звернусь до них:

typedef int bool;
#define true 1
#define false 0

Це ні-ні, тому що випадковий читач - який навчився С протягом цих 19 років - очікував, що це boolстосується фактичного bool типу даних і поводитиметься аналогічно, але це не так! Наприклад

double a = ...;
bool b = a;

З C99 bool/ _Bool, bбуде встановлений false тоді і тільки тоді a дорівнював нулю, а в trueіншому випадку. C11 6.3.1.2p1

  1. Коли будь-яке скалярне значення перетворюється на _Bool, результат дорівнює 0, якщо значення порівнюється рівним 0; в іншому випадку результат 1. 59)

Виноски

59) NaNs не порівнюються рівними 0 і, таким чином, перетворюються на 1.

Якщо на typedefмісці, значення doubleбуде примусово до int- якщо значення подвійного не знаходиться в діапазоні для int, поведінка не визначена .

Природно, те саме стосується, якщо trueі falseбули оголошені в enum.

Що ще небезпечніше - це декларування

typedef enum bool {
    false, true
} bool;

оскільки тепер усі значення, окрім 1 та 0, є недійсними, і якщо таке значення буде присвоєно змінній цього типу, поведінка буде повністю визначеною .

Отже, якщо ви не можете використовувати C99 з якихось незрозумілих причин, для булевих змінних слід використовувати:

  • тип intі значення 0та 1 як є ; і обережно робіть перетворення домену з будь-яких інших значень до них із подвійним запереченням!!
  • або якщо ви наполягати ви не пам'ятаєте , що 0 є falsy і ненульова truish, по крайней мере , використання великих літер , так що вони не заплутатися з поняттями C99: BOOL, TRUEі FALSE!

1
Яка частина стандарту C обмежує об'єкти перелічених типів утримуванні значень, явно перерахованих у них? Якщо найбільше значення для перерахованої константи менше UCHAR_MAX або USHRT_MAX, реалізація може використовувати тип, менший за intабо unsigned intдля проведення перерахунку, але я не знаю нічого у Стандарті, яке б спричиняло поведінку перерахунку як будь-що, крім цілого числа тип.
supercat

18
typedef enum {
    false = 0,
    true
} t_bool;

2
2 через MAX_INT слід оцінити істинно також
технозавр

2
@technosaurus Такий підхід не гарантує! false == true, оскільки! false може бути будь-яким ненульовим числом. Простий обхід полягав у тому, щоб явно призначити true для! False.
Андрій

3
@Andrew Це неправда. !0 = 1за стандартом C і !a = 0для будь-якого ненульового значення a. Проблема полягає в тому, що будь-який ненульовий вважається істинним. Тож якщо aі bобидва є "правдивими", це не обов'язково, що `a == b`.
Джеремі Вест

14

C має булевий тип: bool (принаймні за останні 10 (!) Років)

Включити stdbool.h та true / false буде працювати, як очікувалося.


12
10 років у стандарті, але не 10 років у компіляторах! Компіляція C MSVC ++ взагалі не підтримує C99, окрім дозволу // коментарів, і ніколи не може це зробити. Також _Bool визначається в C99 як вбудований тип, тоді як bool є typedef у заголовку <stdbool.h>.
Кліффорд

5
@Clifford 4 роки з моменту вашого коментаря ... нічого не змінилося. MSVC є компілятором C ++, і я вважаю, що MS сказали, що вони не дуже прагнуть підтримувати всі нові функції C (C99 і C11). Але я не можу вважати, що MSVC не підтримує нові функції C як причину (особливо, коли ви говорите це проти 10 років відповіді). 10 років - це дійсно довгий час у світі програмування. Будь-який гідний компілятор повинен мати підтримку для нього набагато менше ніж за 10 років, якщо постачальник має намір його підтримати.
ПП

2
@KingsIndian: Я не впевнений, чому ти направив мені свій коментар чи взагалі відчув потребу коментувати. Я лише констатував ситуацію, яка стояла на момент написання. Я не підтримував цю ситуацію, просто зазначив, що "відповідь" може застосовуватися не за будь-яких обставин.
Кліффорд

@Clifford: Строго, стандарт boolповинен бути макросом, який розширюється до _Bool. Різниця має значення тому, що ви можете #undefмакрос (і це дозволено, принаймні як перехідний захід), але ви не untypedefможете вводити typedef. Однак це не змінює основного погляду вашого першого коментаря.
Джонатан Леффлер

2
VS2015 , а потім (і , можливо , раніше, до точки) не мають ніяких проблем з boolдопомогою <stdbool.h>при C компіляції. Він вирішує до _Bool.

11

Будь-яке ненульове значення в булевих операціях оцінюється справжнім, тому ви могли просто

#define TRUE 1
#define FALSE 0

і використовувати константи.


10
але використовуйте їх обережно: оскільки справжнім результатом може бути будь-яке ненульове значення, тести, якщо (t == ІСТИНА) {...} і якщо (t), які еквівалентні в інших мовах, не еквівалентні C .
Фортега

1
Ви маєте рацію, але це також вірно в C ++, який має тип bool, правда? Під час налагодження я бачив змінні bool зі значеннями 5837834939 ...
ggambett

1
У C ++ тест if (t == true) дорівнює тесту if (t), тому що C ++ робить деяку конверсію (все, що не дорівнює 0, або нульове значення покажчика перетворюється на істинне)
Fortega

6
Все, що ви повинні припустити щодо булевого справжнього значення, - це те, що воно не є нульовим. Так що код, наприклад, якщо (b) є безпечним, тоді як (b == ІСТИНА) ні; остання є поганою практикою (і безглуздою).
Кліффорд

5

Просто доповнення до інших відповідей та деяким уточненням, якщо вам дозволяється використовувати C99.

+-------+----------------+-------------------------+--------------------+
|  Name | Characteristic | Dependence in stdbool.h |        Value       |
+-------+----------------+-------------------------+--------------------+
| _Bool |   Native type  |    Don't need header    |                    |
+-------+----------------+-------------------------+--------------------+
|  bool |      Macro     |           Yes           | Translate to _Bool |
+-------+----------------+-------------------------+--------------------+
|  true |      Macro     |           Yes           |   Translate to 1   |
+-------+----------------+-------------------------+--------------------+
| false |      Macro     |           Yes           |   Translate to 0   |
+-------+----------------+-------------------------+--------------------+

Деякі з моїх переваг:

  • _Boolабо bool? І те, і інше добре, але boolвиглядає краще, ніж ключове слово _Bool.
  • Прийняті значення для boolта _Boolє: falseабо true. Призначення 0або 1замість falseабо trueє дійсним, але важче читати та розуміти логічний потік.

Деякі відомості зі стандарту:

  • _BoolНЕ unsigned int, але є частиною цілочислових непідписаних типів групи . Він досить великий, щоб утримувати значення 0або 1.
  • DO NOT, але так, ви можете перевизначити bool trueі , falseале впевнений , що це не дуже гарна ідея. Ця здатність вважається застарілою і надалі буде видалена.
  • Призначення скалярного типу (арифметичні типи та типи вказівника) _Boolабо bool, якщо скалярне значення дорівнює 0або порівнюватиметься з 0ним 0, в іншому випадку результат 1: _Bool x = 9; 9перетворюється на, 1коли присвоюється x.
  • _Boolє 1 байт (8 біт), зазвичай програміст спокушається спробувати використати інші біти, але це не рекомендується, оскільки єдине гарантоване, що надається, - це лише один біт для зберігання даних, а не типу, у charякого є 8 доступні біти.

2

Саме це:

#define TRUE 1
#define FALSE 0

7
Я поїду з чимось на кшталт #define ПРАВИЛЬНО! ФАЛЬС
Том,

2

Ви можете використовувати char або інший контейнер з невеликою кількістю.

Псевдокод

#define TRUE  1
#define FALSE 0

char bValue = TRUE;

Також у C це, як правило, int, і це може призвести до втрати попереджень про точність за допомогою іншого коду, який використовує int ..
Thomas Bonini

Якщо ви не оптимізуєте руку для простору, завжди краще використовувати звичайний розмір слова апаратури (наприклад, як правило int), оскільки в деяких архітектурах ви отримуєте значне враження від продуктивності від розпакування / маскування перевірок цих змінних.
Кінгслі

2

Ви можете використовувати _Bool, але значення повернення повинно бути цілим числом (1 для істинного, 0 для помилкового). Однак, рекомендується включати і використовувати bool, як у C ++, як сказано у цій відповіді з форуму daniweb , а також у цій відповіді з цього іншого запитання про stackoverflow:

_Bool: бульовий тип C99. Використовувати _Bool безпосередньо рекомендується лише в тому випадку, якщо ви підтримуєте застарілий код, який вже визначає макроси для bool, true або false. В іншому випадку ці макроси стандартизовані в заголовку. Включіть цей заголовок, і ви можете використовувати bool так само, як і в C ++.


2

Умовні вирази вважаються істинними, якщо вони не нульові, але стандарт C вимагає, щоб логічні оператори самі повертали 0 або 1.

@Tom: #define ПРАВДА! БУДЬ погано і абсолютно безглуздо. Якщо файл заголовка пробивається в компільований код C ++, це може призвести до проблем:

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

Деякі компілятори генерують попередження про перетворення int => bool. Іноді люди уникають цього, роблячи:

foo(flag == TRUE);

змусити вираз бути C ++ bool. Але якщо ви #define ПРАВИЛЬНО! ФАЛЬСЕ, ви закінчите:

foo(flag == !0);

що в кінцевому підсумку робить внутрішнє порівняння порівняння, яке в будь-якому випадку може викликати попередження.


1

Якщо ви використовуєте C99, ви можете використовувати _Boolтип. Ні #includeз яких не потрібно. Потрібно ставитися до цього як до цілого числа, хоча там, де 1є trueі 0є false.

Потім можна визначити TRUEі FALSE.

_Bool this_is_a_Boolean_var = 1;


//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;

Або ви можете #include <stdbool.h>і використовувати bool, trueі, falseяк стандарт, який ви хочете, щоб ви.
СС Енн

1

Сьогодні C99 підтримує булеві типи, але вам потрібно #include <stdbool.h>.

Приклад:

#include <stdbool.h>

int main() 
{ 
    bool arr[2] = {true, false}; 

    printf("%d\n", arr[0] && arr[1]);
    printf("%d\n", arr[0] || arr[1]);

    return 0; 
} 

Вихід:

0
1

0

Це те, що я використовую:

enum {false, true};
typedef _Bool bool;

_Bool це вбудований тип у C. Він призначений для булевих значень.


-2

Ви можете просто використовувати #defineдирективу так:

#define TRUE 1
#define FALSE 0
#define NOT(arg) (arg == TRUE)? FALSE : TRUE
typedef int bool;

І використовувати наступним чином:

bool isVisible = FALSE;
bool isWorking = TRUE;
isVisible = NOT(isVisible);

і так далі


5
НЕ макрос повинен бути захищений в дужках argі вираз в цілому: #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE). Однак було б краще перевірити на хибність (це дасть правильну відповідь, навіть якщо argбуло 23 замість 0 або 1: #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE)Але весь вираз можна звести #define NOT(arg) (!(arg)), звичайно, що дає такий же результат.
Джонатан Леффлер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.