C не має вбудованих булевих типів. Який найкращий спосіб використовувати їх у С?
C не має вбудованих булевих типів. Який найкращий спосіб використовувати їх у С?
Відповіді:
Від кращого до гіршого:
Варіант 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!
<stdbool.h>
bool
компілятора можуть бути більш підходящими за цільовим призначенням булевого значення, ніж використання int
(тобто компілятор може вирішити реалізувати bool
інакше, ніж an int
). Це також може призвести до більш жорсткої перевірки типу під час компіляції, якщо вам пощастить.
int
для bool
? Це марно. Використовуйте unsigned char
. Або використовувати вбудовану команду мови C _Bool
.
Кілька думок про булеви в C:
Я досить старий, що я просто використовую звичайний int
s як мій булевський тип без будь-яких 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
.
a == b
працює?
a
і b
підраховувати від нуля, я б рекомендував a > 0 == b > 0
замість цього. Якщо ви наполягаєте на тому, щоб скористатися правдивістю довільних ненульових значень, ви отримаєте !!var
булеве значення 0/1, еквівалентне тому var
, щоб ви могли написати !!a == !!b
, хоча досить багато читачів вважають це заплутаним.
!a == !b
також достатньо для перевірки рівності, ненулі стають нульовими, а нулі - цілими.
!!a
як "конвертувати небулевий a в його еквівалентне значення істини", тоді як я читав би !a
як "логічно інвертувати булева змінна a". Зокрема, я хотів би шукати якусь конкретну причину, якою хотілося логічної інверсії.
Булеве значення в C - це ціле число: нуль для помилкового і не нульове значення для істинного.
Дивіться також логічний тип даних , розділ C, C ++, Objective-C, AWK .
Ось версія, яку я використав:
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,
true
, false
і bool
виділяються в більшості IDE, тому що вони є значеннями перерахувань і ЬурейеЕ, на відміну від #defines
, які рідко коли - або підсвічування синтаксису.
gcc -ansi -pedantic -Wall
не дає попереджень, тому я довіряю gcc
; Якщо це працює навіть для c89
цього, він повинен працювати c99
також.
!
може повертати лише значення 0
і 1
, таким чином true = !false
, завжди призначатиме значення 1. Цей метод не забезпечує додаткової безпеки typedef enum { false, true } bool;
.
if(!value)
), але цей виняток не застосовується в цьому конкретному випадку.
Якщо ви використовуєте компілятор C99, він має вбудовану підтримку типів bool:
#include <stdbool.h>
int main()
{
bool b = false;
b = true;
}
Насамперед. 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
- Коли будь-яке скалярне значення перетворюється на
_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
як є ; і обережно робіть перетворення домену з будь-яких інших значень до них із подвійним запереченням!!
BOOL
, TRUE
і FALSE
!int
або unsigned int
для проведення перерахунку, але я не знаю нічого у Стандарті, яке б спричиняло поведінку перерахунку як будь-що, крім цілого числа тип.
typedef enum {
false = 0,
true
} t_bool;
!0 = 1
за стандартом C і !a = 0
для будь-якого ненульового значення a
. Проблема полягає в тому, що будь-який ненульовий вважається істинним. Тож якщо a
і b
обидва є "правдивими", це не обов'язково, що `a == b`.
C має булевий тип: bool (принаймні за останні 10 (!) Років)
Включити stdbool.h та true / false буде працювати, як очікувалося.
bool
повинен бути макросом, який розширюється до _Bool
. Різниця має значення тому, що ви можете #undef
макрос (і це дозволено, принаймні як перехідний захід), але ви не untypedef
можете вводити typedef. Однак це не змінює основного погляду вашого першого коментаря.
Будь-яке ненульове значення в булевих операціях оцінюється справжнім, тому ви могли просто
#define TRUE 1
#define FALSE 0
і використовувати константи.
Просто доповнення до інших відповідей та деяким уточненням, якщо вам дозволяється використовувати 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
.bool
true
і , false
але впевнений , що це не дуже гарна ідея. Ця здатність вважається застарілою і надалі буде видалена._Bool
або bool
, якщо скалярне значення дорівнює 0
або порівнюватиметься з 0
ним 0
, в іншому випадку результат 1
: _Bool x = 9;
9
перетворюється на, 1
коли присвоюється x
._Bool
є 1 байт (8 біт), зазвичай програміст спокушається спробувати використати інші біти, але це не рекомендується, оскільки єдине гарантоване, що надається, - це лише один біт для зберігання даних, а не типу, у char
якого є 8 доступні біти.Ви можете використовувати char або інший контейнер з невеликою кількістю.
Псевдокод
#define TRUE 1
#define FALSE 0
char bValue = TRUE;
int
), оскільки в деяких архітектурах ви отримуєте значне враження від продуктивності від розпакування / маскування перевірок цих змінних.
Ви можете використовувати _Bool, але значення повернення повинно бути цілим числом (1 для істинного, 0 для помилкового). Однак, рекомендується включати і використовувати bool, як у C ++, як сказано у цій відповіді з форуму daniweb , а також у цій відповіді з цього іншого запитання про stackoverflow:
_Bool: бульовий тип C99. Використовувати _Bool безпосередньо рекомендується лише в тому випадку, якщо ви підтримуєте застарілий код, який вже визначає макроси для bool, true або false. В іншому випадку ці макроси стандартизовані в заголовку. Включіть цей заголовок, і ви можете використовувати bool так само, як і в C ++.
Умовні вирази вважаються істинними, якщо вони не нульові, але стандарт 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);
що в кінцевому підсумку робить внутрішнє порівняння порівняння, яке в будь-якому випадку може викликати попередження.
Якщо ви використовуєте 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
як стандарт, який ви хочете, щоб ви.
Це те, що я використовую:
enum {false, true};
typedef _Bool bool;
_Bool
це вбудований тип у C. Він призначений для булевих значень.
Ви можете просто використовувати #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);
і так далі
arg
і вираз в цілому: #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE)
. Однак було б краще перевірити на хибність (це дасть правильну відповідь, навіть якщо arg
було 23 замість 0 або 1: #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE)
Але весь вираз можна звести #define NOT(arg) (!(arg))
, звичайно, що дає такий же результат.