Usando valores booleanos em C

Respostas:

1049

Do melhor ao pior:

Opção 1 (C99)

#include <stdbool.h>

opção 2

typedef enum { false, true } bool;

Opção 3

typedef int bool;
enum { false, true };

Opção 4

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

Explicação

  • A opção 1 funcionará apenas se você usar o C99 e é a "maneira padrão" de fazê-lo. Escolha isso, se possível.
  • As opções 2, 3 e 4 terão na prática o mesmo comportamento idêntico. Os # 2 e # 3 não usam # define, o que, na minha opinião, é melhor.

Se você estiver indeciso, vá com o número 1!

Thomas Bonini
fonte
1
Você pode explicar por que elas são as melhores para as piores escolhas?
endolith 29/01
1
@endolith O alinhamento, otimizações e a maneira de armazenar um que <stdbool.h> boolo compilador escolhe pode ser mais adequado para a finalidade pretendida de um valor booleano do que utilizar um int(isto é, o compilador pode optar por implementar um booldiferente do que um int). Também pode resultar em uma verificação mais rigorosa do tipo no momento da compilação, se você tiver sorte.
blubberdiblub 6/03
1
Por que usar intpara bool? Isso é um desperdício. Use unsigned char. Ou use o C embutido _Bool.
user12211554
@NoBody O uso de um tipo menor pode economizar memória, mas pode não torná-lo mais rápido. Muitas vezes, é mais rápido usar tamanho da palavra nativa do processador em vez de um tamanho menor, uma vez que poderia exigir o compilador para fazer mudanças bit para alinhá-lo corretamente
Ted Klein Bergman
241

Algumas reflexões sobre os booleanos em C:

Tenho idade suficiente para usar apenas plain ints como meu tipo booleano, sem nenhum typedefs ou definições ou enumerações especiais para valores true / false. Se você seguir minha sugestão abaixo, nunca comparando com constantes booleanas, precisará usar 0/1 para inicializar os sinalizadores de qualquer maneira. No entanto, essa abordagem pode ser considerada muito reacionária nos tempos modernos. Nesse caso, deve-se definitivamente usar, <stdbool.h>pois pelo menos tem o benefício de ser padronizado.

Quaisquer que sejam as constantes booleanas chamadas, use-as apenas para inicialização. Nunca escreva algo como

if (ready == TRUE) ...
while (empty == FALSE) ...

Estes sempre podem ser substituídos pelos mais claros

if (ready) ...
while (!empty) ...

Observe que eles podem ser lidos em voz alta e razoável.

Atribua nomes positivos às suas variáveis ​​booleanas, ou seja, em fullvez de notfull. O último leva a um código difícil de ler facilmente. Comparar

if (full) ...
if (!full) ...

com

if (!notfull) ...
if (notfull) ...

Ambos os pares anteriores lêem naturalmente, embora !notfullseja difícil ler o que é, e se tornam muito piores em expressões booleanas mais complexas.

Argumentos booleanos geralmente devem ser evitados. Considere uma função definida como esta

void foo(bool option) { ... }

Dentro do corpo da função, é muito claro o que o argumento significa, uma vez que possui um nome conveniente e, espero, significativo. Mas, os sites de chamada parecem

foo(TRUE);
foo(FALSE):

Aqui, é essencialmente impossível dizer o que o parâmetro significou sem sempre olhar para a definição ou declaração da função, e fica muito pior assim se você adicionar ainda mais parâmetros booleanos. Eu sugiro

typedef enum { OPT_ON, OPT_OFF } foo_option;
void foo(foo_option option);

ou

#define OPT_ON true
#define OPT_OFF false
void foo(bool option) { ... }

Em ambos os casos, o site de chamada agora parece

foo(OPT_ON);
foo(OPT_OFF);

que o leitor tem pelo menos uma chance de entender sem aprofundar a definição de foo.

Dale Hagglund
fonte
1
E como você compara duas variáveis ​​para igualdade? Nunca usar constantes booleanas funciona muito bem, mas não resolve o problema ao comparar com uma não constante.
Baruch
Perdoe-me, mas não entendo a pergunta. Você está perguntando como eu comparo duas variáveis ​​booleanas para igualdade? Se sim, não a == bfunciona?
Dale Hagglund
5
@ Kenji O que você diz é verdade, embora eu acredite que usar valores diferentes de um como equivalente para true seja quase sempre uma má idéia. Portanto, no seu exemplo, supondo que ae bconte a partir de zero, eu recomendo a > 0 == b > 0. Se você insistir em tirar proveito da veracidade de valores arbitrários diferentes de zero, !!varproduz o valor booleano 0/1 equivalente a var, para que você possa escrever !!a == !!b, embora alguns leitores o achem confuso.
Dale Hagglund
3
!a == !btambém é suficiente para testar a igualdade, não-zeros se tornam zero e zeros se tornam um.
ryanpattison
5
@rpattiso Você está certo, é claro, mas acho que eu leria !!acomo "converter não booleano a em seu valor de verdade equivalente", enquanto eu leria !acomo "inverter logicamente a variável booleana a". Em particular, eu procuraria por algum motivo específico que a inversão lógica fosse desejada.
Dale Hagglund
74

Aqui está a versão que eu usei:

typedef enum { false = 0, true = !false } bool;

Como false possui apenas um valor, mas um true lógico pode ter muitos valores, mas a técnica define true para ser o que o compilador usará para o oposto de false.

Isso resolve o problema de alguém codificar algo que se resume a isso:

if (true == !false)

Acho que todos concordamos que essa não é uma boa prática, mas pelo custo único de se fazer "verdadeiro =! Falso", eliminamos esse problema.

[EDIT] No final, eu usei:

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

para evitar colisão de nomes com outros esquemas que estavam definindo truee false. Mas o conceito permanece o mesmo.

[EDIT] Para mostrar a conversão de número inteiro em booleano:

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

O primeiro (mais à direita)! converte o número inteiro diferente de zero em 0, depois o segundo (mais à esquerda)! converte o 0 em um myfalsevalor. Vou deixar como um exercício para o leitor converter um número inteiro zero.

[EDIT] É meu estilo usar a configuração explícita de um valor em uma enumeração quando o valor específico é necessário, mesmo que o valor padrão seja o mesmo. Exemplo: como false precisa ser zero, eu uso em false = 0,vez defalse,

Michael Potter
fonte
5
Também um outro benefício de usar enums é a integração IDE - true, falsee boolsão destacadas em, porque eles são valores enum e um typedef, ao contrário da maioria IDE #defines, que são raramente sintaxe destacada.
Curioso: Ignorando se ele realmente funciona ou não, é válido C (99+) para permitir que uma enum faça referência a um valor anterior na mesma enumeração ?
@ tgm1024 gcc -ansi -pedantic -Wallnão dá avisos, então eu confio gcc; Se isso funcionar, mesmo assim, c89também deve funcionar c99.
yyny
1
"Como false possui apenas um valor, mas um true lógico pode ter muitos valores, mas a técnica define true para ser o que o compilador usará para o oposto de false". O operador de negação !só pode retornar valores 0e 1, portanto true = !false, sempre atribuirá o valor 1. Este método não oferece segurança extra typedef enum { false, true } bool;.
user694733
1
Anteriormente, encontrei a partir de C90 (6.3.3.3 Operadores aritméticos unários): "O resultado do operador de negação lógica! É 0 se o valor de seu operando for diferente de 0. 1 se o valor de seu operando for igual a 0. O resultado tem o tipo int. A expressão! E é equivalente a (O == E). " Isso deve abranger qualquer compilador que já tenha alegado oferecer suporte ao padrão C. É claro que os compiladores podem ignorar legalmente essa regra nos casos em que não importa o comportamento observável (como if(!value)), mas essa exceção não é aplicável neste caso específico.
user694733
30

Primeiras coisas primeiro. C, ou seja, a ISO / IEC 9899 possui um tipo booleano há 19 anos . Isso é muito mais tempo do que a duração esperada da carreira de programação C, com partes amadoras / acadêmicas / profissionais combinadas ao visitar esta pergunta . A minha supera isso em meros talvez 1-2 anos. Isso significa que, durante o tempo em que um leitor comum aprendeu alguma coisa sobre C, C realmente teve o tipo de dados booleanos .

Para o tipo de dados,, #include <stdbool.h>e use true, falsee bool. Ou não o inclua, use e _Bool, em vez disso.10


Existem várias práticas perigosas promovidas nas outras respostas a este segmento. Vou abordá-los:

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

Isso é não-não, porque um leitor casual - que aprendeu C nesses 19 anos - esperaria que isso boolse refere ao tipo de dados real bool e se comportaria de maneira semelhante, mas não! Por exemplo

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

Com C99 bool/ _Bool, bseria definido como false iff igual a a zero e, truecaso contrário. C11 6.3.1.2p1

  1. Quando qualquer valor escalar é convertido em _Bool, o resultado é 0 se o valor for comparado a 0; caso contrário, o resultado é 1. 59)

Notas de rodapé

59) NaNs não comparam igual a 0 e, portanto, convertem para 1.

Com o typedeflocal, o doubleseria coagido a um int- se o valor do dobro não estiver no intervalo int, o comportamento será indefinido .

Naturalmente, o mesmo se aplica a se truee falsefoi declarado em um enum.

O que é ainda mais perigoso é declarar

typedef enum bool {
    false, true
} bool;

porque agora todos os valores além de 1 e 0 são inválidos e, se esse valor for atribuído a uma variável desse tipo, o comportamento será totalmente indefinido .

Portanto, se você não pode usar C99 por algum motivo inexplicável, para variáveis ​​booleanas você deve usar:

  • tipo inte valores 0e 1 como está ; e faça com cuidado conversões de domínio de outros valores para esses com dupla negação!!
  • ou se você insistir que não se lembra de que 0 é Falsas e diferente de zero Truish, pelo menos uso maiúsculas para que eles não se confundem com os conceitos C99: BOOL, TRUEe FALSE!
Antti Haapala
fonte
1
Que parte do Padrão C limitaria os objetos de tipos enumerados a manter os valores listados explicitamente? Se o maior valor para uma constante enumerada for menor que UCHAR_MAX ou USHRT_MAX, uma implementação poderá usar um tipo menor que intou unsigned intpara armazenar uma enumeração, mas não conheço nada no Padrão que faça com que uma enumeração se comporte como algo diferente de um número inteiro. tipo.
Supercat
18
typedef enum {
    false = 0,
    true
} t_bool;
mouviciel
fonte
2
2 a MAX_INT também devem avaliar como verdadeiro
technosaurus
2
@technosaurus A adoção dessa abordagem não garante! false == true, pois! false pode ser qualquer número diferente de zero. Uma solução simples seria atribuir explicitamente true a! False.
Andrew
3
@ Andrew Isso não é verdade. !0 = 1pelo padrão C e !a = 0para qualquer valor diferente de zero de a. O problema é que qualquer diferente de zero é considerado verdadeiro. Portanto, se ae bsão "verdadeiros", não é necessariamente o caso que `a == b`.
Jeremy West
14

C tem um tipo booleano: bool (pelo menos nos últimos 10 (!) Anos)

Inclua stdbool.he true / false funcionará conforme o esperado.

dmeister
fonte
12
10 anos no padrão, mas não 10 anos em compiladores! A compilação C do MSVC ++ não oferece suporte ao C99, exceto a permissão de // comentários, e provavelmente nunca o fará. Também _Bool é definido no C99 como um tipo interno, enquanto bool é um typedef no cabeçalho <stdbool.h>.
Clifford
5
@Clifford 4 anos depois de seu comentário ... nada mudou. O MSVC é um compilador C ++ e acredito que a MS disse que não está realmente interessada em oferecer suporte a todos os novos recursos do C (C99 e C11). Mas não posso supor que o MSVC não suporte novos recursos C como motivo (especialmente quando você diz isso contra uma resposta de 10 anos ). 10 anos é realmente muito tempo no mundo da programação. Qualquer compilador decente deve ter suporte para ele em menos de 10 anos se o fornecedor pretender suportá-lo.
PP
2
@ KingsIndian: Não sei por que você direcionou seu comentário para mim ou até sentiu a necessidade de comentar. Eu estava apenas declarando a situação como estava no momento em que escrevi. Eu não estava apoiando essa situação, apenas apontando que a "resposta" pode não se aplicar em todas as circunstâncias.
Clifford
@ Clifford: Estritamente, o padrão exige booluma macro que se expanda para _Bool. A diferença importa porque você pode #undefuma macro (e isso é permitido, pelo menos como uma medida de transição), mas você não pode untypedefdigitar um. No entanto, isso não altera o impulso principal do seu primeiro comentário.
precisa
2
O VS2015 e posterior (e possivelmente anterior, até certo ponto) não têm problemas com boola <stdbool.h>compilação em C. Resolve para _Bool.
11

Qualquer coisa diferente de zero é avaliada como verdadeira em operações booleanas, para que você possa apenas

#define TRUE 1
#define FALSE 0

e use as constantes.

ggambett
fonte
10
mas use-os com cuidado: como um resultado verdadeiro pode ser qualquer valor diferente de zero, os testes if (t == TRUE) {...} e if (t), que são equivalentes em outros idiomas, não são equivalentes em C .
Fortega
1
Você está certo, mas isso também é verdade em C ++, que tem um tipo bool, certo? Durante a depuração eu vi variáveis bool com valores de 5837834939 ...
ggambett
1
Em C ++, o teste if (t == true) é igual ao teste if (t), porque o C ++ faz alguma conversão (tudo o que não é 0 ou um valor de ponteiro nulo é convertido em true)
Fortega
6
Tudo o que você deve assumir sobre um valor verdadeiro booleano é que ele é diferente de zero. Portanto, código como se (b) é seguro enquanto se (b == VERDADEIRO) não é; o último é uma má prática (e inútil).
Clifford
5

Apenas um complemento para outras respostas e alguns esclarecimentos, se você puder usar o 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   |
+-------+----------------+-------------------------+--------------------+

Algumas das minhas preferências:

  • _Boolou bool? Ambos estão bem, mas boolparecem melhores que a palavra-chave _Bool.
  • Valores aceitos para boole _Boolsão: falseou true. A atribuição 0ou em 1vez de falseou trueé válida, mas é mais difícil de ler e entender o fluxo lógico.

Algumas informações do padrão:

  • _BoolNÃO unsigned int, mas faz parte dos tipos de números inteiros não assinados do grupo . É grande o suficiente para armazenar os valores 0ou 1.
  • NÃO, mas sim, você pode redefinir bool truee, com falsecerteza, não é uma boa ideia. Essa habilidade é considerada obsoleta e será removida no futuro.
  • Atribuir um tipo escalar (tipos aritméticos e tipos de ponteiro) a _Boolou bool, se o valor escalar for igual 0ou comparável a 0ele 0, caso contrário, o resultado é 1: _Bool x = 9; 9é convertido em 1quando atribuído a x.
  • _Boolé de 1 byte (8 bits), geralmente o programador é tentado a tentar usar os outros bits, mas não é recomendado, porque a única garantia garantida é que apenas um bit seja usado para armazenar dados, não como o tipo charque possui 8 bits . bits disponíveis.
Comportamento indefinido
fonte
2

É isto:

#define TRUE 1
#define FALSE 0
RngTng
fonte
7
Id ir com algo parecido com # define VERDADEIRO FALSO!
Tom
2

Você pode usar um caractere ou outro contêiner de número pequeno.

Pseudo-código

#define TRUE  1
#define FALSE 0

char bValue = TRUE;
Filip Ekberg
fonte
Também em C, geralmente é um int, e pode causar perda de avisos de precisão por outro código que usa int. #
Thomas Bonini
A menos que você esteja otimizando manualmente o espaço, é sempre melhor usar o tamanho normal das palavras do hardware (por exemplo: geralmente um int), já que em algumas arquiteturas você obtém um desempenho significativo ao ter que descompactar / mascarar as verificações dessas variáveis.
22418 Kingsley
2

Você pode usar _Bool, mas o valor de retorno deve ser um número inteiro (1 para true, 0 para false). No entanto, recomenda-se incluir e usar bool como em C ++, como foi dito nesta resposta do fórum daniweb , bem como nesta resposta , desta outra pergunta sobre o stackoverflow:

_Bool: tipo booleano do C99. Usar o _Bool diretamente é recomendado apenas se você estiver mantendo um código legado que já define macros para bool, true ou false. Caso contrário, essas macros são padronizadas no cabeçalho. Inclua esse cabeçalho e você pode usar bool como faria em C ++.

Lokian
fonte
2

Expressões condicionais são consideradas verdadeiras se forem diferentes de zero, mas o padrão C exige que os próprios operadores lógicos retornem 0 ou 1.

@ Tom: #definir VERDADEIRO! FALSO é ruim e é completamente inútil. Se o arquivo de cabeçalho chegar ao código C ++ compilado, poderá causar problemas:

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

Alguns compiladores irão gerar um aviso sobre a conversão int => bool. Às vezes, as pessoas evitam isso fazendo:

foo(flag == TRUE);

para forçar a expressão a ser um booleano C ++. Mas se você definir # VERDADEIRO! FALSO, terá:

foo(flag == !0);

que acaba fazendo uma comparação int-bool que pode disparar o aviso de qualquer maneira.

jamesdlin
fonte
1

Se você estiver usando o C99, poderá usar o _Booltipo Não #includesão necessários. Você precisa tratá-lo como um número inteiro, onde 1está truee 0é false.

Você pode então definir TRUEe 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
fonte
Ou você pode #include <stdbool.h>e usar bool,, truee falsecomo o padrão deseja.
SS Anne
1

Atualmente, o C99 suporta tipos booleanos, mas você precisa #include <stdbool.h>.

Exemplo:

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

Resultado:

0
1
Kalana
fonte
0

Isto é o que eu uso:

enum {false, true};
typedef _Bool bool;

_Bool é um tipo incorporado em C. Destina-se a valores booleanos.

user12211554
fonte
-2

Você pode simplesmente usar a #definediretiva da seguinte maneira:

#define TRUE 1
#define FALSE 0
#define NOT(arg) (arg == TRUE)? FALSE : TRUE
typedef int bool;

E use da seguinte maneira:

bool isVisible = FALSE;
bool isWorking = TRUE;
isVisible = NOT(isVisible);

e assim por diante

Prakash Bhattarai
fonte
5
A NÃO macro deve ser protegido por parênteses em torno da argea expressão como um todo: #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE). No entanto, seria melhor testar a falsidade (ela fornecerá a resposta correta mesmo se argfor 23 em vez de 0 ou 1 : #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE). Mas toda a expressão pode ser reduzida a #define NOT(arg) (!(arg)), é claro, que produz o mesmo resultado.
Jonathan Leffler