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

550

C не имеет встроенных булевых типов. Какой лучший способ использовать их в C?

Теги:
boolean

13 ответов

939
Лучший ответ

Вариант 1

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

Вариант 2

typedef int bool;
enum { false, true };

Вариант 3

typedef enum { false, true } bool;

Вариант 4 (C99)

#include <stdbool.h>

Объяснение

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

Если вы еще не определились, перейдите к # 3!

197

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

Я достаточно взрослый, чтобы просто использовать в качестве логического типа plain int без каких-либо определений типов, специальных определений или перечислений для значений 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
    А как вы сравниваете две переменные на равенство? Никогда не используйте булевы константы, это прекрасно работает, но это не решает проблему при сравнении с непостоянной.
  • 0
    Прости меня, но я не понимаю вопроса. Вы спрашиваете, как я сравниваю две булевы переменные на равенство? Если так, то не работает a == b ?
Показать ещё 5 комментариев
76

Логическое значение в C - целое число: ноль для false и ненулевое значение для true.

См. также Тип данных Boolean, раздел C, С++, Objective-C, AWK.

  • 0
    Он также хорошо работает с логическими операторами (&& и ||).
58

Вот версия, которую я использовал:

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] Чтобы показать преобразование целочисленного значения в boolean:

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

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

  • 4
    Еще одним преимуществом использования перечислений является интеграция с IDE - true , false и bool выделяются в большинстве IDE, поскольку они являются значениями перечисления и typedef, в отличие от #defines , которые редко когда-либо выделяются синтаксисом.
  • 0
    Любопытно: не обращая внимания на то, действительно ли это работает, действительно ли это C (99+), чтобы позволить перечислению ссылаться на предыдущее значение в том же перечислении ?
Показать ещё 1 комментарий
42

Если вы используете компилятор C99, у него есть встроенная поддержка типов bool:

#include <stdbool.h>
int main()
{
  bool b = false;
  b = true;
}

http://en.wikipedia.org/wiki/Boolean_data_type

17
typedef enum {
    false = 0,
    true
} t_bool;
  • 2
    От 2 до MAX_INT также должно оцениваться как true
  • 2
    @technosaurus Такой подход не гарантирует! false == true, поскольку! false может быть любым ненулевым числом. Простой обходной путь - явно назначить true для! False.
Показать ещё 1 комментарий
14

Обо всем по порядку. 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 данных bool и будет вести себя аналогично, но это не так! Например

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

С C99 bool/_Bool b будет установлено в false если a равно нулю, и true противном случае. При наличии typedef double будет приведен к int - если значение double не находится в диапазоне для int, поведение не определено.

Естественно, то же самое относится, если true и false были объявлены в enum.

Что еще опаснее, так это декларировать

typedef enum bool {
    false, true
} bool;

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

Поэтому, если вы не можете использовать C99 по какой-то необъяснимой причине, для логических переменных вы должны использовать:

  • введите int и значения 0 и 1 как есть; и тщательно делайте доменные преобразования из любых других значений в них с двойным отрицанием !!
  • или если вы настаиваете, что не помните, что 0 - это ложь и ненулевое значение, по крайней мере используйте верхний регистр, чтобы их не спутали с понятиями C99: BOOL, TRUE и FALSE !
  • 0
    Какая часть Стандарта C ограничит объекты перечислимых типов хранением значений, явно перечисленных в них? Если наибольшее значение для перечисляемой константы меньше, чем UCHAR_MAX или USHRT_MAX, реализация может использовать тип, меньший чем int или unsigned int для хранения перечисления, но я ничего не знаю в Стандарте, который мог бы заставить перечисление вести себя как что-либо другое чем целочисленный тип.
10

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

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

  • 11
    10 лет в стандарте, но не 10 лет в компиляторах! Компиляция C MSVC ++ вообще не поддерживает C99, кроме разрешения // комментариев, и вряд ли когда-либо сделает это. Также _Bool определен в C99 как встроенный тип, в то время как bool является typedef в заголовке <stdbool.h>.
  • 4
    @ Clifford 4 года с момента вашего комментария ... ничего не изменилось. MSVC - это компилятор C ++, и я полагаю, MS заявляет, что на самом деле они не заинтересованы в поддержке всех новых функций C (C99 и C11). Но я не могу принять тот факт, что MSVC не поддерживает новые функции C в качестве причины (особенно если вы говорите это против 10 лет ответа). 10 лет - это действительно много времени в мире программирования. Любой достойный компилятор должен иметь поддержку для него гораздо менее, чем через 10 лет, если поставщик намерен поддерживать его.
Показать ещё 3 комментария
8

Любое ненулевое значение вычисляется как true в булевых операциях, поэтому вы можете просто

#define TRUE 1
#define FALSE 0

и используйте константы.

  • 10
    но используйте их с осторожностью: поскольку истинным результатом может быть любое ненулевое значение, тесты if (t == TRUE) {...} и if (t), которые эквивалентны в других языках, не эквивалентны в C ,
  • 1
    Вы правы, но это также верно для C ++, который имеет тип bool, верно? Во время отладки я видел переменные bool со значениями 5837834939 ...
Показать ещё 2 комментария
1

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

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

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

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

foo(flag == TRUE);

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

foo(flag == !0);

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

0

Это:

#define TRUE 1
#define FALSE 0
  • 6
    Я бы пошел с чем-то вроде #define TRUE! FALSE
  • 0
    @Tom: Какое значение v в этом коде: char v = !0 ? Это результат вашего предложения.
0

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

Псевдо-код

#define TRUE  1
#define FALSE 0

char bValue = TRUE;
  • 0
    Также в C это обычно int, и это может вызвать потерю точности предупреждений другим кодом, который использует int ..
  • 0
    Если вы не оптимизируете пространство вручную, всегда лучше использовать аппаратный нормальный размер слова (например, обычно int ), поскольку на некоторых архитектурах вы получаете значительное снижение производительности от необходимости распаковывать / маскировать проверки этих переменных.
-1

Вы можете просто использовать директиву #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);

и так.. в

  • 2
    Макрос NOT должен быть защищен круглыми скобками вокруг arg и выражения в целом: #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE) . Однако было бы лучше проверить на ложность (это даст правильный ответ, даже если arg было 23 вместо 0 или 1: #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE) . Но Конечно, все выражение можно уменьшить до #define NOT(arg) (!(arg)) , что дает тот же результат.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню