Por que #define TRUE (1 == 1) em uma macro booleana C em vez de simplesmente como 1?


160

Eu já vi definições em C

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

Isso é necessário? Qual é o benefício de simplesmente definir TRUE como 1 e FALSE como 0?


35
E mais #define TRUE (’/’/’/’):; #define FALSE (’-’-’-’)(extraído de coding-guidelines.com/cbook/cbook1_1.pdf página 871)
osgx

2
Não, é paranóia dos ignorantes. Em C, 1 e 0 fazem em todas as circunstâncias o mesmo.
Jens

@osgx O que isso significa?
Mrgloom 9/07/19

Respostas:


155

Essa abordagem usará o booleantipo real (e resolverá para truee false) se o compilador suportar. (especificamente, C ++)

No entanto, seria melhor verificar se C ++ está em uso (via __cplusplusmacro) e realmente usar truee false.

Em um compilador C, isso é equivalente a 0e 1.
(observe que remover os parênteses quebrará isso devido à ordem das operações)


7
Isso está incorreto, bools não são usados ​​aqui. O resultado de 1==1é um int. (veja stackoverflow.com/questions/7687403/... .)
Mat

4
@ Mat: Mesmo em C ++, com o booleantipo?
SLaks

9
A pergunta está marcada como C, mas, de fato, em C ++, os operadores relacionais retornam trueou false.
Mat

5
@Mat: Eu estou supondo que tal código é escrito em cabeçalhos C, a fim de jogar bem com C ++
SLaks

20
@ Slaks Se ele quisesse jogar bem com C ++, faria #define TRUE truee #define FALSE falsesempre que __cplusplusfor definido.
precisa

137

A resposta é portabilidade. Os valores numéricos de TRUEe FALSEnão são importantes. O que é importante é que uma declaração como if (1 < 2)avalia como if (TRUE)e uma declaração como if (1 > 2)avaliada como if (FALSE).

Concedido, em C, (1 < 2)avalia 1e (1 > 2)avalia 0, portanto, como outros já disseram, não há diferença prática no que diz respeito ao compilador. Mas, ao permitir que o compilador defina TRUEe de FALSEacordo com suas próprias regras, você está explicitando seus significados aos programadores e garantindo consistência dentro do programa e de qualquer outra biblioteca (assumindo que a outra biblioteca siga os padrões C ... ser surpreendido).


Alguma história
Alguns BASICs definidos FALSEcomo 0e TRUEcomo -1. Como muitas linguagens modernas, eles interpretaram qualquer valor diferente de zero como TRUE, mas avaliaram expressões booleanas que eram verdadeiras como -1. Sua NOToperação foi implementada adicionando 1 e invertendo o sinal, porque era eficiente fazê-lo dessa maneira. Então 'NOT x' se tornou -(x+1). Um efeito colateral disso é que um valor como 5avalia para TRUE, mas NOT 5avalia para -6, o que também é TRUE! Encontrar esse tipo de bug não é divertido.

Práticas recomendadas
Dadas as regras de fato em que zero é interpretado como FALSEe qualquer valor diferente de zero é interpretado como TRUE, você nuncaTRUEFALSE deve comparar expressões de aparência booleana com ou . Exemplos:

if (thisValue == FALSE)  // Don't do this!
if (thatValue == TRUE)   // Or this!
if (otherValue != TRUE)  // Whatever you do, don't do this!

Por quê? Porque muitos programadores usam o atalho para tratar ints como bools. Eles não são os mesmos, mas os compiladores geralmente permitem. Por exemplo, é perfeitamente legal escrever

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

Que aparência legítimos, e o compilador terá todo o prazer aceitá-lo, mas ele provavelmente não faz o que você gostaria. Isso porque o valor de retorno de strcmp()é

      0 se yourString == myString
    <0 se yourString < myString
    > 0 seyourString > myString

Portanto, a linha acima retorna TRUEapenas quando yourString > myString.

A maneira correta de fazer isso é

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))

ou

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)

Similarmente:

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.

Você encontrará frequentemente alguns desses "exemplos ruins" no código de produção, e muitos programadores experientes juram por eles: eles funcionam, alguns são mais curtos que suas alternativas corretas (pedanticamente?) E os idiomas são quase universalmente reconhecidos. Mas considere: as versões "certas" não são menos eficientes, são garantidas para serem portáteis, elas passam pelos linters mais rígidos e até mesmo os novos programadores as entendem.

Isso não vale a pena?


6
(1==1)não é mais portátil do que 1. As regras do próprio compilador são as da linguagem C, que é clara e inequívoca sobre a semântica da igualdade e dos operadores relacionais. Eu nunca vi um compilador errar essas coisas.
amigos estão dizendo sobre keith thompson

1
Na verdade, strcmpsabe-se que o valor retornado por é menor que, igual ou maior que 0. Não é garantido que seja -1, 0 ou 1 e existem plataformas em estado selvagem que não retornam esses valores para ganhar velocidade de implementação. Então, se strcmp(a, b) == TRUE, em seguida, a > bmas inversa implicação não pôde prender.
Maciej Piechotka

2
@ KeithThompson - Talvez "portabilidade" tenha sido o termo errado. Mas o fato é que (1 == 1) é um valor booleano; 1 não é.
Adam Liss

2
@AdamLiss: em C, (1==1)e 1são expressões constantes do tipo intcom o valor 1. Elas são semanticamente idênticas. Suponho que você possa escrever um código que atenda a leitores que não sabem disso, mas onde termina?
Keith Thompson

2
'not' 5 é, de fato, -6, no nível de bits.
Woliveirajr

51

O (1 == 1)truque é útil para definir TRUEde uma maneira que seja transparente para C, mas que ofereça melhor digitação em C ++. O mesmo código pode ser interpretado como C ou C ++ se você estiver escrevendo em um dialeto chamado "Clean C" (que é compilado como C ou C ++) ou se estiver gravando arquivos de cabeçalho de API que possam ser usados ​​por programadores de C ou C ++.

Nas unidades de tradução C, 1 == 1tem exatamente o mesmo significado que 1; e 1 == 0tem o mesmo significado que 0. No entanto, no C ++ unidades de tradução, 1 == 1tem tipo bool. Portanto, a TRUEmacro definida dessa maneira se integra melhor ao C ++.

Um exemplo de como ele se integra melhor é que, por exemplo, se a função footiver sobrecargas para inte para bool, então foo(TRUE)ela escolherá a boolsobrecarga. Se TRUEfor apenas definido como 1, não funcionará bem no C ++. foo(TRUE)vai querer a intsobrecarga.

Claro, C99 introduzida bool, truee falsee estes podem ser usados em arquivos de cabeçalho que o trabalho com C99 e com C.

Contudo:

  • essa prática de definir TRUEe FALSEcomo (0==0)e (1==0)antecede C99.
  • ainda existem boas razões para ficar longe do C99 e trabalhar com o C90.

Se você estiver trabalhando em um projeto misto de C e C ++ e não quiser o C99, defina as letras minúsculas e true, em vez disso.falsebool

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

Dito isto, o 0==0truque foi (é?) Usado por alguns programadores, mesmo em código que nunca teve a intenção de interoperar com o C ++ de forma alguma. Isso não compra nada e sugere que o programador tem um mal-entendido de como os booleanos funcionam em C.


Caso a explicação do C ++ não esteja clara, aqui está um programa de teste:

#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;
}

A saída:

bool
int

Quanto à pergunta dos comentários de como as funções C ++ sobrecarregadas são relevantes para a programação mista de C e C ++. Isso apenas ilustra uma diferença de tipo. Um motivo válido para desejar que uma trueconstante seja boolcompilada como C ++ é o diagnóstico limpo. Nos níveis mais altos de aviso, um compilador C ++ pode nos alertar sobre uma conversão se passarmos um número inteiro como boolparâmetro. Uma razão para escrever no Clean C não é apenas o fato de nosso código ser mais portátil (já que é entendido pelos compiladores C ++, não apenas pelos compiladores C), mas também podemos nos beneficiar das opiniões de diagnóstico dos compiladores C ++.


3
Resposta excelente e subestimada. Não é de todo óbvio que as duas definições de TRUEserão diferentes em C ++.
precisa saber é o seguinte

4
Como as funções sobrecarregadas são relevantes para o código que é compilado como C e C ++?
Keith Thompson

@KeithThompson Não se trata apenas de sobrecarga, mas de digitação adequada em geral, a sobrecarga é apenas o exemplo mais prático quando se entra em jogo. É claro que o código C ++ sem sobrecargas, modelos e todo esse material "complicado" removido para "compatibilidade C" não se importa muito com tipos, mas isso não significa que se deva derrubar as limitações de tipo conceitual em uma determinada linguagem .
Christian Rau

1
@ChristianRau: O que você quer dizer com "realmente não se importa muito com tipos"? Os tipos são centrais para a linguagem C; toda expressão, valor e objeto em um programa C tem um tipo bem definido. Se você deseja definir algo diferente em C e em C ++ (no diretório raros casos em que você realmente precisa escrever código que seja compilado como C e C ++), você pode usar #ifdef __cpluspluspara expressar sua intenção com muito mais clareza.
Keith Thompson

@KeithThompson Sim, eu sei como os tipos são importantes. É que, sem todas as coisas sensíveis ao tipo, como sobrecarga e modelos, coisas como a diferenciação entre boole intnão importam muito na prática, pois são implicitamente conversíveis entre si (e em C, na verdade, "o mesmo" , observe as aspas , no entanto) e não há muitas situações nas quais você realmente precisa desambiguar as duas. "não muito" provavelmente foi muito pesado " , muito menos se comparado ao código que usa modelos e sobrecarga" teria sido melhor talvez.
Christian Rau

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

é equivalente a

#define TRUE  1
#define FALSE 0

em C.

O resultado dos operadores relacionais é 0ou 1. 1==1é garantido para ser avaliado 1e !(1==1)garantido para ser avaliado 0.

Não há absolutamente nenhuma razão para usar a primeira forma. Observe que a primeira forma não é menos eficiente, pois em quase todos os compiladores uma expressão constante é avaliada no tempo de compilação e não no tempo de execução. Isso é permitido de acordo com esta regra:

(C99, 6.6p2) "Uma expressão constante pode ser avaliada durante a tradução, em vez do tempo de execução, e, portanto, pode ser usada em qualquer lugar em que uma constante possa estar."

O PC-Lint emitirá uma mensagem (506, valor constante booleano) se você não usar um literal para TRUEe FALSEmacros:

Para C, TRUEdeve ser definido para ser 1. No entanto, outros idiomas usam quantidades diferentes de 1, então alguns programadores acham que !0estão jogando com segurança.

Também em C99, as stdbool.hdefinições para macros booleanas truee false usam diretamente literais:

#define true   1
#define false  0

1
Eu tenho uma dúvida, TRUE é substituído a cada uso por 1 == 1, enquanto apenas usar 1 substitui 1, não é o primeiro método extra de comparação extra ... ou é compilador otimizado?
pinkpanther

4
As expressões constantes @pinkpanther são geralmente avaliadas em tempo de compilação e, portanto, não causam sobrecarga.
OuJun

2
1==1é garantido para ser avaliado para1
ouah

3
@NikosC. essa é uma boa pergunta. Isso é importante para o código do formulário if(foo == true), que passará de meramente má prática a um buggy simples.
precisa saber é o seguinte

1
+1 para apontar os perigos (x == TRUE)pode ter um valor de verdade diferente de x.
21413 Joshua Taylor #

12

Além do C ++ (já mencionado), outro benefício é para ferramentas de análise estática. O compilador eliminará quaisquer ineficiências, mas um analisador estático pode usar seus próprios tipos abstratos para distinguir entre resultados de comparação e outros tipos inteiros; portanto, sabe implicitamente que TRUE deve ser o resultado de uma comparação e não deve ser considerado compatível. com um número inteiro.

Obviamente, C diz que eles são compatíveis, mas você pode optar por proibir o uso deliberado desse recurso para ajudar a destacar bugs - por exemplo, onde alguém pode ter se confundido &e &&/ ou eles confundiram sua precedência de operador.


1
Esse é um bom ponto, e talvez algumas dessas ferramentas possam até pegar código bobo if (boolean_var == TRUE) por meio de expansão para a if (boolean_var == (1 == 1))qual, graças às informações aprimoradas de tipo do (1 == 1)nó, caiam no padrão if (<*> == <boolean_expr>).
Kaz

4

A diferença prática é nenhuma. 0é avaliado falsee 1avaliado como true. O fato de você usar uma expressão booleana ( 1 == 1) ou 1, para definir true, não faz diferença. Ambos são avaliados para int.

Note-se que a biblioteca padrão C proporciona um cabeçalho específico para definir booleanos: stdbool.h.


é claro que você não tem ... mas algumas pessoas possam pensar de outra forma, especialmente para números negativos por isso :)
pinkpanther

O que? Você tem isso ao contrário. trueé avaliado 1e falseavaliado como 0. C não sabe sobre tipos booleanos nativos, são apenas ints.
precisa saber é o seguinte

Em C, operadores relacionais e de igualdade produzem resultados do tipo int, com valor 0ou 1. C tem um tipo booleano real ( _Boolcom um macro booldefinido no <stdbool.h>, mas que só foi adicionada no C99, que se não mudar a semântica dos operadores para usar o novo tipo.
Keith Thompson

@djechlin: A partir do padrão de 1999 C não ter um tipo booleano nativa. É chamado _Bool, e <stdbool.h>tem #define bool _Bool.
Keith Thompson

@KeithThompson, você está certo sobre 1 == 1ser avaliado como int. Editado.
Shoe

3

Não sabemos o valor exato que TRUE é igual e os compiladores podem ter suas próprias definições. Então, o que você privode é usar o interno do compilador para definição. Isso nem sempre é necessário se você tiver bons hábitos de programação, mas puder evitar problemas por algum estilo de codificação ruim, por exemplo:

if ((a> b) == VERDADEIRO)

Isso pode ser um desastre se você definir manualmente TRUE como 1, enquanto o valor interno de TRUE for outro.


Em C, o >operador sempre produz 1 para verdadeiro, 0 para falso. Não há possibilidade de nenhum compilador C estar errado. Comparações de igualdade TRUEe FALSEestilo pobre; o acima é mais claramente escrito como if (a > b). Mas a idéia de que diferentes compiladores C podem tratar a verdade e o falso de maneira diferente é incorreta.
Keith Thompson

2
  1. Item da lista

Normalmente, na linguagem de programação C, 1 é definido como verdadeiro e 0 é definido como falso. Por isso, você vê o seguinte com bastante frequência:

#define TRUE 1 
#define FALSE 0

No entanto, qualquer número diferente de 0 seria avaliado como verdadeiro também em uma instrução condicional. Portanto, usando o abaixo:

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

Você pode apenas mostrar explicitamente que está tentando jogar pelo seguro, tornando falso igual ao que não é verdadeiro.


4
Eu não chamaria isso de "jogar pelo seguro" - em vez disso, você está se dando uma falsa sensação de segurança.
dodgethesteamroller
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.