Использование логических значений в C

Ответы:

1050

От лучшего к худшему:

Вариант 1 (С99)

#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отличную от int). Это также может привести к более строгой проверке типов во время компиляции, если вам повезет.
blubberdiblub
1
Зачем использовать intдля bool? Это расточительно. Использование unsigned char. Или используйте встроенный Си _Bool.
user12211554
@ NoBody Использование меньшего типа может сэкономить память, но это может не сделать это быстрее. Часто быстрее использовать собственный размер слова процессора, а не меньший размер, так как это может потребовать, чтобы компилятор выполнил битовые сдвиги, чтобы выровнять его должным образом
Тед Кляйн Бергман
241

Несколько мыслей о логических значениях в C:

Я достаточно intвзрослый , чтобы просто использовать простой s в качестве моего логического типа без каких-либо определений типов, специальных определений или перечислений для значений true / false. Если вы последуете моему предложению никогда не сравнивать с булевыми константами, тогда вам все равно нужно будет использовать 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
@ Кендзи То, что вы говорите, правда, хотя я считаю, что использование значений, отличных от единицы, в качестве эквивалента для истины - почти всегда плохая идея. Так что в вашем примере, предполагая это 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;

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

Это решает проблему того, что кто-то кодирует что-то, что сводится к следующему:

if (true == !false)

Я думаю, что мы все согласны с тем, что это не очень хорошая практика, но за единовременную стоимость выполнения операции «true =! False» мы устраняем эту проблему.

[РЕДАКТИРОВАТЬ] В конце концов я использовал:

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

чтобы избежать конфликта имен с другими определяющими схемами trueи false. Но концепция остается прежней.

[EDIT] Чтобы показать преобразование целого числа в логическое значение:

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

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

[РЕДАКТИРОВАТЬ] Мой стиль заключается в использовании явной установки значения в перечислении, когда требуется конкретное значение, даже если значение по умолчанию будет таким же. Пример: поскольку false должно быть равно нулю, я использую, false = 0,а неfalse,

Майкл Поттер
источник
5
Кроме того, еще одно преимущество использования перечислений является интеграция IDE - true, falseи boolвыделяются в большинстве IDE, потому что они являются значениями перечислений и ЬурейеЕ, в отличие от #defines, которые редко когда - либо подсветки синтаксиса.
Любопытно: не обращая внимания на то, действительно ли это работает, действительно ли это C (99+), чтобы позволить перечислению ссылаться на предыдущее значение в том же перечислении ?
@ tgm1024 не gcc -ansi -pedantic -Wallвыдает предупреждений, поэтому я верю gcc; Если это работает даже для c89этого, должно работать на c99aswell.
yyny
1
«Потому что ложь имеет только одно значение, но логическое истина может иметь много значений, но метод устанавливает истину так, чтобы компилятор использовал ее в противоположность ложному». Оператор отрицания !может только возвращать значения 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

Это нет-нет, потому что случайный читатель - который изучил C в течение этих 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) NaN не сравниваются равными 0 и, следовательно, преобразуются в 1.

При наличии на typedefместе значение doubleбудет приведено к int- если значение double не находится в диапазоне для 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для хранения перечисления, но я не знаю ничего в Стандарте, которое могло бы привести к тому, что перечисление будет вести себя как что-то отличное от целого числа тип.
Суперкат
18
typedef enum {
    false = 0,
    true
} t_bool;
mouviciel
источник
2
От 2 до MAX_INT также следует оценить как истинное
технозавр
2
@technosaurus Такой подход не гарантирует! false == true, поскольку! false может быть любым ненулевым числом. Простой обходной путь - явно назначить true для! False.
Андрей
3
@ Андрей Это не правда. !0 = 1по стандарту C, и !a = 0для любого ненулевого значения a. Проблема в том, что любое ненулевое значение считается истинным. Таким образом, если aи bоба являются «истиной», это не обязательно тот случай, когда `a == b`.
Джереми Уэст
14

C имеет логический тип: bool (по крайней мере, за последние 10 (!) Лет)

Включите stdbool.h, и true / false будет работать как положено.

dmeister
источник
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 лет, если поставщик намерен поддерживать его.
PP
2
@KingsIndian: Я не уверен, почему вы направили свой комментарий мне или даже почувствовали необходимость комментировать вообще. Я только констатировал ситуацию на момент написания статьи. Я не поддерживал эту ситуацию, просто указывал, что «ответ» может применяться не при всех обстоятельствах.
Клиффорд
@Clifford: Строго говоря, стандарт boolдолжен быть макросом, который расширяется до _Bool. Разница имеет значение, потому что вы можете #undefмакрос (и это разрешено, по крайней мере, в качестве переходной меры), но вы не можете untypedeftypedef. Это не меняет основную направленность вашего первого комментария.
Джонатан Леффлер
2
VS2015 и более поздние версии (и, возможно, ранее, до определенного момента) не имеют проблем со boolсквозной <stdbool.h>компиляцией под Си. Это решает до _Bool.
11

Все, что не равно нулю, оценивается как истинное в логических операциях, так что вы можете просто

#define TRUE 1
#define FALSE 0

и использовать константы.

ggambett
источник
10
но используйте их с осторожностью: поскольку истинным результатом может быть любое ненулевое значение, тесты if (t == TRUE) {...} и if (t), которые эквивалентны в других языках, не эквивалентны в C .
Fortega
1
Вы правы, но это также верно для C ++, который имеет тип bool, верно? Во время отладки я видел переменные bool со значениями 5837834939 ...
ggambett
1
В C ++ тест if (t == true) равен тесту if (t), потому что C ++ выполняет некоторое преобразование (все, что не равно 0 или значение нулевого указателя преобразуется в true)
Fortega
6
Все, что вы должны предположить о булевом истинном значении, это то, что оно ненулевое. Так что код вроде if (b) безопасен, а if (b == TRUE) - нет; последнее - плохая практика (и бессмысленная).
Клиффорд
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.
  • НЕ, но да, вы можете переопределить bool trueи, falseконечно, не очень хорошая идея. Эта способность считается устаревшей и будет удалена в будущем.
  • Назначение скалярного типа (арифметические типы и типы указателей) к _Boolили bool, если скалярное значение равно 0или сравнивает 0это будет 0, в противном случае результат 1: _Bool x = 9; 9преобразуется в , 1когда назначен x.
  • _Bool1 байт (8 бит), обычно у программиста возникает соблазн попытаться использовать другие биты, но это не рекомендуется, поскольку гарантируется только то, что для хранения данных используется только один бит, а не тип char, имеющий 8 биты доступны.
Неопределенное поведение
источник
2

Вот это:

#define TRUE 1
#define FALSE 0
RngTng
источник
7
Я пошел бы с чем-то вроде #define TRUE! FALSE
Том
2

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

Псевдо-код

#define TRUE  1
#define FALSE 0

char bValue = TRUE;
Филип Экберг
источник
Также в C это обычно int, и это может вызвать потерю точности предупреждений другим кодом, который использует int.
Thomas Bonini
Если вы не оптимизируете пространство вручную, всегда лучше использовать аппаратный нормальный размер слова (например, обычно an int), поскольку на некоторых архитектурах вы получаете значительное снижение производительности от необходимости распаковывать / маскировать проверки этих переменных.
Кингсли
2

Вы можете использовать _Bool, но возвращаемое значение должно быть целым числом (1 для true, 0 для false). Тем не менее, рекомендуется включать и использовать BOOL как в C ++, как сказано в этом ответе от daniweb форума , а также этот ответ , от этого другого StackOverflow вопрос:

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

Локян
источник
2

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

@Tom: #define TRUE! FALSE - это плохо и совершенно бессмысленно. Если заголовочный файл попадает в скомпилированный код C ++, то это может привести к проблемам:

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

Некоторые компиляторы генерируют предупреждение о преобразовании int => bool. Иногда люди избегают этого, делая:

foo(flag == TRUE);

заставить выражение быть C ++ bool. Но если вы #define TRUE! FALSE, вы получите:

foo(flag == !0);

который заканчивает тем, что делает сравнение типа int-to-bool, которое в любом случае может вызвать предупреждение.

jamesdlin
источник
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;
That_Linux_Guy
источник
Или вы можете #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
Kalana
источник
0

Это то, что я использую:

enum {false, true};
typedef _Bool bool;

_Bool является встроенным типом в C. Он предназначен для логических значений.

user12211554
источник
-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)), конечно, что дает тот же результат.
Джонатан Леффлер