Почему #define TRUE (1 == 1) в булевом макросе C вместо простого как 1?

Я видел определения в C

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

Это необходимо? Какая польза от простого определения TRUE как 1 и FALSE как 0?

Ответ 1

Этот подход будет использовать фактический тип boolean (и разрешить true и false), если компилятор его поддерживает. (в частности, С++)

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

В компиляторе C это эквивалентно 0 и 1.
(обратите внимание, что удаление скобок будет нарушено, что из-за порядка операций)

Ответ 2

Ответ - переносимость. Числовые значения TRUE и FALSE не важны. Важно то, что выражение типа if (1 < 2) оценивается как if (TRUE), а выражение типа if (1 > 2) оценивается как if (FALSE).

Предположим, что в C, (1 < 2) оценивается как 1 и (1 > 2) оценивается как 0, так как другие сказали, нет никакой практической разницы в отношении компилятора. Но давая компилятору определить TRUE и FALSE в соответствии со своими собственными правилами, вы делаете их значения явными для программистов, и вы гарантируете согласованность в своей программе и любой другой библиотеке (если другая библиотека соответствует стандартам C... вы были бы поражены).


Некоторая история
Некоторые BASIC определяли FALSE как 0 и TRUE как -1. Как и многие современные языки, они интерпретировали любое ненулевое значение как TRUE, но они оценивали булевы выражения, которые были истинными как -1. Их операция NOT была реализована добавлением 1 и переворачиванием знака, потому что это было эффективно для этого. Итак, "NOT x" стал -(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!

Почему? Поскольку многие программисты используют ярлык обработки int как bool s. Они не совпадают, но компиляторы обычно допускают это. Так, например, совершенно законно писать

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

Это выглядит законным, и компилятор с радостью примет его, но он, вероятно, не делает того, что вы хотите. Это потому, что возвращаемое значение strcmp() равно

    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.

Вы часто найдете некоторые из этих "плохих примеров" в производственном коде, и многие опытные программисты клянутся ими: они работают, некоторые из них короче, чем их (педантично?) правильные альтернативы, и идиомы почти повсеместно признаны. Но подумайте: "правильные" версии не менее эффективны, они гарантированно будут переносимыми, они пройдут даже самые строгие линтеры, и даже новые программисты поймут их.

Разве это не стоит?

Ответ 3

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

В единицах C-трансляции 1 == 1 имеет то же значение, что и 1; и 1 == 0 имеет то же значение, что и 0. Однако в единицах перевода С++ 1 == 1 имеет тип bool. Таким образом, макрос TRUE, определенный таким образом, лучше интегрируется в С++.

Примером того, как он лучше интегрируется, является то, что если функция foo имеет перегрузки для int и для bool, то foo(TRUE) выберет перегрузку bool. Если TRUE определяется как 1, то он не будет хорошо работать на С++. foo(TRUE) потребуется перегрузка int.

Конечно, C99 представил bool, TRUE и false, и они могут использоваться в файлах заголовков, которые работают с C99 и с C.

Однако:

  • эта практика определения TRUE и false как (0==0) и (1==0) предшествует C99.
  • Есть еще веские причины, чтобы держаться подальше от C99 и работать с C90.

Если вы работаете в смешанном проекте C и С++ и не хотите C99, определите нижний регистр TRUE, false и bool.

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif

Как говорится, трюк 0==0 был (есть?), используемый некоторыми программистами даже в коде, который никогда не был предназначен для взаимодействия с С++. Это ничего не покупает и предполагает, что у программиста есть непонимание того, как булевы работают на 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 и С++. Это просто иллюстрирует разницу типов. Действительной причиной желания константы TRUE быть bool при компиляции как С++ для чистой диагностики. На своих самых высоких уровнях предупреждения компилятор С++ может предупредить нас о преобразовании, если мы передадим целое число как параметр bool. Одна из причин написания в Clean C заключается не только в том, что наш код более переносимый (поскольку он понимается компиляторами С++, а не только компиляторами C), но мы можем извлечь пользу из диагностических мнений компиляторов С++.

Ответ 4

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

эквивалентно

#define TRUE  1
#define FALSE 0

в C.

Результатом реляционных операторов является 0 или 1. 1==1 гарантированно оценивается до 1, а !(1==1) гарантированно оценивается до 0.

Нет абсолютно никакой причины использовать первую форму. Обратите внимание, что первая форма не менее эффективна, так как почти для всех компиляторов постоянное выражение оценивается во время компиляции, а не во время выполнения. Это разрешено в соответствии с этим правилом:

(C99, 6.6p2) "Постоянное выражение может быть оценено во время трансляции, а не во время выполнения, и, соответственно, может использоваться в любом месте, где может быть константа".

PC-Lint даже выдает сообщение (506, постоянное значение boolean), если вы не используете литерал для макросов TRUE и FALSE:

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

Также в C99 определения stdbool.h для булевых макросов TRUE и FALSE напрямую используют литералы:

#define true   1
#define false  0

Ответ 5

Помимо С++ (уже упоминалось), еще одно преимущество для инструментов статического анализа. Компилятор избавится от какой-либо неэффективности, но статический анализатор может использовать свои собственные абстрактные типы для различения результатов сравнения и других целочисленных типов, поэтому он неявно знает, что ИСТИНА должен быть результатом сравнения и не должен считаться совместимым с целым числом.

Очевидно, что C говорит, что они совместимы, но вы можете запретить преднамеренное использование этой функции, чтобы помочь выделить ошибки - например, когда кто-то может запутать & и &&, или они испортили свои приоритет оператора.

Ответ 6

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

Обратите внимание, что стандартная библиотека C предоставляет определенный заголовок для определения булевых: stdbool.h.

Ответ 7

Мы не знаем точное значение, равное TRUE, и компиляторы могут иметь свои собственные определения. Итак, вы соглашаетесь использовать внутренний компилятор для определения. Это не всегда необходимо, если у вас хорошие привычки к программированию, но вы можете избежать проблем для плохого стиля кодирования, например:

if ((a > b) == TRUE)

Это может быть катастрофой, если вы изначально определяете TRUE как 1, а внутреннее значение TRUE - это другое.

Ответ 8

  • Элемент списка

Как правило, на языке программирования C 1 определяется как истинный, а 0 - как false. Поэтому вы часто видите следующее:

#define TRUE 1 
#define FALSE 0

Однако любое число, не равное 0, будет оцениваться как истинное, так и в условном выражении. Поэтому, используя приведенное ниже:

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

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