Usando valores booleanos en C


Respuestas:


1050

De mejor a peor:

Opción 1 (C99)

#include <stdbool.h>

opcion 2

typedef enum { false, true } bool;

Opción 3

typedef int bool;
enum { false, true };

Opcion 4

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

Explicación

  • La opción 1 solo funcionará si usa C99 y es la "forma estándar" de hacerlo. Elija esto si es posible.
  • Las opciones 2, 3 y 4 tendrán en la práctica el mismo comportamiento idéntico. Sin embargo, # 2 y # 3 no usan #defines, que en mi opinión es mejor.

Si no estás decidido, ¡ve con el # 1!


1
¿Puedes explicar por qué son las mejores a las peores opciones?
endolito

1
@endolith La alineación, las optimizaciones y la forma de almacenar <stdbool.h> boolel que elija el compilador pueden ser más adecuadas para el propósito previsto de un valor booleano que utilizar un int(es decir, el compilador puede optar por implementar un valor booldiferente a an int). También puede resultar en una verificación de tipo más estricta en el momento de la compilación, si tiene suerte.
blubberdiblub

1
¿Por qué usar intpara bool? Eso es un desperdicio. Uso unsigned char. O use el C incorporado _Bool.
user12211554

@NoBody Usar un tipo más pequeño puede ahorrar memoria, pero puede que no sea más rápido. A menudo, es más rápido usar el tamaño de palabra nativo del procesador en lugar de un tamaño más pequeño, ya que podría requerir que el compilador realice pequeños cambios para alinearlo correctamente
Ted Klein Bergman

241

Algunas reflexiones sobre booleanos en C:

Soy lo suficientemente mayor como para usar simplemente ints como mi tipo booleano sin ningún tipo de definición o definición especial o enumeración para valores verdaderos / falsos. Si sigue mi sugerencia a continuación sobre nunca comparar con constantes booleanas, entonces solo necesita usar 0/1 para inicializar las banderas de todos modos. Sin embargo, este enfoque puede considerarse demasiado reaccionario en estos tiempos modernos. En ese caso, uno definitivamente debería usar<stdbool.h> ya que al menos tiene el beneficio de estar estandarizado.

Como se llamen las constantes booleanas, úselas solo para la inicialización. Nunca escribas algo como

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

Estos siempre pueden ser reemplazados por el más claro

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

Tenga en cuenta que estos pueden leerse en voz alta de manera razonable y comprensible.

Dé a sus variables booleanas nombres positivos, es decir, en fulllugar de notfull. Este último conduce a un código que es difícil de leer fácilmente. Comparar

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

con

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

Ambos pares anteriores leen naturalmente, mientras que !notfulles difícil de leer incluso como es, y empeoran mucho en expresiones booleanas más complejas.

Los argumentos booleanos generalmente deben evitarse. Considere una función definida como esta

void foo(bool option) { ... }

Dentro del cuerpo de la función, está muy claro lo que significa el argumento, ya que tiene un nombre conveniente y con suerte significativo. Pero, los sitios de llamadas se ven como

foo(TRUE);
foo(FALSE):

Aquí, es esencialmente imposible saber qué significa el parámetro sin mirar siempre la definición o declaración de la función, y empeora mucho si agrega aún más parámetros booleanos. Sugiero cualquiera

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

o

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

En cualquier caso, el sitio de la llamada ahora se ve así

foo(OPT_ON);
foo(OPT_OFF);

que el lector tiene al menos una oportunidad de entender sin desenterrar la definición de foo.


1
¿Y cómo se comparan dos variables para la igualdad? Nunca usar constantes booleanas funciona muy bien, pero no resuelve el problema cuando se compara con una no constante.
Baruch

Perdóname, pero no entiendo la pregunta. ¿Estás preguntando cómo comparo dos variables booleanas para la igualdad? Si es así, ¿no a == bfunciona?
Dale Hagglund

55
@Kenji Lo que dices es cierto, aunque creo que usar valores distintos de uno como equivalentes para verdadero es casi siempre una mala idea. Entonces, en su ejemplo, suponiendo eso ay bcontando desde cero, recomendaría en su a > 0 == b > 0lugar. Si insiste en aprovechar la veracidad de los valores arbitrarios distintos de cero, !!varproduce el valor booleano 0/1 equivalente a var, por lo que podría escribir !!a == !!b, aunque bastantes lectores lo encontrarán confuso.
Dale Hagglund

3
!a == !btambién es suficiente para probar la igualdad, los no ceros se vuelven cero y los ceros se vuelven uno.
ryanpattison

55
@rpattiso Tienes toda la razón, por supuesto, pero supongo que leería !!acomo "convertir a no booleano a su valor de verdad equivalente", mientras que leería !acomo "invertir lógicamente la variable booleana a". En particular, buscaría alguna razón específica por la que se deseaba la inversión lógica.
Dale Hagglund


74

Aquí está la versión que usé:

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

Porque falso solo tiene un valor, pero un verdadero lógico podría tener muchos valores, pero la técnica establece verdadero como lo que el compilador usará para lo contrario de falso.

Esto soluciona el problema de que alguien codifique algo que se reduciría a esto:

if (true == !false)

Creo que todos estaríamos de acuerdo en que esa no es una buena práctica, pero por el costo único de hacer "verdadero =! Falso" eliminamos ese problema.

[EDITAR] Al final usé:

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

para evitar la colisión de nombres con otros esquemas que estaban definiendo trueyfalse . Pero el concepto sigue siendo el mismo.

[EDITAR] Para mostrar la conversión de entero a booleano:

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

El primero (más correcto)! convierte el entero distinto de cero a 0, luego el segundo (el que queda más a la izquierda). convierte el 0 en un myfalsevalor. Lo dejaré como un ejercicio para que el lector convierta un entero cero.

[EDITAR] Es mi estilo usar la configuración explícita de un valor en una enumeración cuando se requiere el valor específico, incluso si el valor predeterminado sería el mismo. Ejemplo: debido a que falso debe ser cero, lo uso en false = 0,lugar defalse,


55
Otro beneficio del uso de enumeraciones es la integración de IDE true, falsey boolse destacan en la mayoría de los IDE porque son valores de enumeración y un typedef, en oposición a #defines, que rara vez se resaltan en la sintaxis.

Curioso: ignorando si realmente funciona o no, ¿es válido C (99+) para permitir que una enumeración haga referencia a un valor anterior en la misma enumeración ?

@ tgm1024 gcc -ansi -pedantic -Wallno da advertencias, así que confío gcc; Si esto funciona incluso para c89sí, debería funcionar c99también para .
yyny

1
"Porque falso solo tiene un valor, pero un verdadero lógico podría tener muchos valores, pero la técnica establece verdadero como lo que el compilador usará para lo contrario de falso". El operador de negación !solo puede devolver valores 0y 1, por true = !falselo tanto , siempre asignará el valor 1. Este método no proporciona ninguna seguridad adicional typedef enum { false, true } bool;.
user694733

1
Lo primero que encontré es de C90 (6.3.3.3 Operadores aritméticos unarios): "¡El resultado del operador de negación lógica! Es 0 si el valor de su operando es desigual a 0. 1 si el valor de su operando es igual a 0. El result tiene el tipo int. La expresión! E es equivalente a (O == E) ". Eso debería cubrir cualquier compilador que alguna vez haya afirmado que admite el estándar C. Los compiladores pueden, por supuesto, ignorar legalmente esta regla en casos en los que no importa el comportamiento observable (como if(!value)), pero esa excepción no es aplicable en este caso específico.
user694733


30

Lo primero es lo primero. C, es decir, ISO / IEC 9899 ha tenido un tipo booleano durante 19 años . Eso es mucho más tiempo que la duración esperada de la carrera de programación en C con partes amateur / académicas / profesionales combinadas al visitar esta pregunta . La mía supera eso en solo 1-2 años. Significa que durante el tiempo en que un lector promedio ha aprendido algo acerca de C, C realmente ha tenido el tipo de datos booleano .

Para el tipo de datos, #include <stdbool.h>y uso true, falsey bool. O no lo incluya, y use _Bool, 1y en su 0lugar.


Hay varias prácticas peligrosas promovidas en las otras respuestas a este hilo. Me ocuparé de ellos:

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

Esto es no-no, porque un lector casual, que aprendió C dentro de esos 19 años, esperaría que se boolrefiera al tipo de datos real bool y se comportaría de manera similar, ¡pero no es así! Por ejemplo

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

Con C99 bool/ _Bool, bse establecería en false iff a era cero, y de lo truecontrario. C11 6.3.1.2p1

  1. Cuando se convierte cualquier valor escalar _Bool, el resultado es 0 si el valor se compara igual a 0; de lo contrario, el resultado es 1. 59)

Notas al pie

59) Los NaN no se comparan igual a 0 y, por lo tanto, se convierten en 1.

Con el typedefen su lugar, doublese coaccionaría a un int- si el valor del doble no está en el rango int, el comportamiento es indefinido .

Naturalmente, lo mismo se aplica a if truey falsefueron declarados en unenum .

Lo que es aún más peligroso es declarar

typedef enum bool {
    false, true
} bool;

porque ahora todos los valores además de 1 y 0 no son válidos, y si dicho valor se asigna a una variable de ese tipo, el comportamiento sería completamente indefinido .

Por lo tanto, si no puede usar C99 por alguna razón inexplicable, para las variables booleanas debe usar:

  • tipo inty valores 0y 1 tal cual ; y cuidadosamente hacer conversiones de dominio de cualquier otro valor a estos con doble negación!!
  • o si insiste usted no recuerda que el 0 es Falsy y no cero Truish, al menos el uso mayúsculas para que no se confunden con los conceptos C99: BOOL, TRUEy FALSE!

1
¿Qué parte del Estándar C limitaría los objetos de los tipos enumerados a mantener los valores enumerados explícitamente en él? Si el valor más grande para una constante enumerada es menor que UCHAR_MAX o USHRT_MAX, una implementación podría usar un tipo más pequeño que into unsigned intpara mantener una enumeración, pero no sé nada en el Estándar que pueda causar que una enumeración se comporte como algo más que un entero tipo.
supercat

18
typedef enum {
    false = 0,
    true
} t_bool;

2
2 a través de MAX_INT también debería evaluar como verdadero
technosaurus

2
@technosaurus Tomar este enfoque no garantiza! false == true ya que! false puede ser cualquier número distinto de cero. Una solución simple sería asignar explícitamente true a! False.
Andrew

3
@ Andrew Eso no es cierto. !0 = 1por el estándar C, y !a = 0para cualquier valor distinto de cero de a. El problema es que cualquier valor distinto de cero se considera verdadero. Así que si ay bson a la vez "verdadero", que no es necesariamente el caso de que un `== b`.
Jeremy West

14

C tiene un tipo booleano: bool (al menos durante los últimos 10 (!) Años)

Incluir stdbool.h y verdadero / falso funcionará como se esperaba.


12
¡10 años en el estándar, pero no 10 años en compiladores! La compilación C de MSVC ++ no es compatible con C99 de ninguna otra manera que no sea permitir // comentarios, y es probable que nunca lo haga. También _Bool se define en C99 como un tipo incorporado, mientras que bool es un typedef en el encabezado <stdbool.h>.
Clifford

55
@Clifford 4 años después de su comentario ... nada ha cambiado. MSVC es un compilador de C ++ y creo que MS ha dicho que no están realmente interesados ​​en admitir todas las nuevas características de C (C99 y C11). Pero no puedo entender que MSVC no sea compatible con las nuevas características de C como razón (especialmente cuando lo dices en un plazo de 10 años ). 10 años es realmente mucho tiempo en el mundo de la programación. Cualquier compilador decente debería tener soporte en mucho menos de 10 años si el proveedor está destinado a soportarlo.
PP

2
@ KingsIndian: No estoy seguro de por qué me dirigiste tu comentario o incluso sentiste la necesidad de comentarlo. Solo estaba indicando la situación tal como estaba al momento de escribir. No estaba apoyando esa situación, simplemente señalando que la "respuesta" puede no aplicarse en todas las circunstancias.
Clifford

@Clifford: estrictamente, el estándar requiere boolser una macro que se expanda a _Bool. La diferencia es importante porque puede #undefuna macro (y eso está permitido, al menos como una medida de transición), pero no puede untypedefdefinir un tipo. Sin embargo, no altera el objetivo principal de su primer comentario.
Jonathan Leffler

2
VS2015 y más tarde (y posiblemente antes, hasta cierto punto) no tienen ningún problema con el boolmedio <stdbool.h>en C compilación. Se resuelve a _Bool.

11

Todo lo que no sea cero se evalúa como verdadero en operaciones booleanas, por lo que podría simplemente

#define TRUE 1
#define FALSE 0

y usa las constantes.


10
pero úselos con cuidado: dado que un resultado verdadero puede ser cualquier valor distinto de cero, las pruebas if (t == TRUE) {...} y if (t), que son equivalentes en otros idiomas, no son equivalentes en C .
Fortega

1
Tienes razón, pero eso también es cierto en C ++ que tiene un tipo bool, ¿verdad? Durante la depuración que he visto las variables bool con valores de 5837834939 ...
ggambett

1
En C ++, la prueba if (t == verdadero) es igual a la prueba if (t), porque C ++ realiza alguna conversión (todo lo que no es 0 o un valor de puntero nulo se convierte en verdadero)
Fortega

66
Todo lo que debe asumir sobre un valor verdadero booleano es que no es cero. Entonces el código como si (b) es seguro mientras que si (b == VERDADERO) no lo es; esta última es una mala práctica (y no tiene sentido).
Clifford

5

Solo un complemento a otras respuestas y algunas aclaraciones, si se le permite usar 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   |
+-------+----------------+-------------------------+--------------------+

Algunas de mis preferencias:

  • _Boolo bool? Ambos están bien, pero se boolve mejor que la palabra clave _Bool.
  • Los valores aceptados para booly _Boolson: falseo true. Asignar 0o en 1lugar de falseo truees válido, pero es más difícil de leer y comprender el flujo lógico.

Alguna información del estándar:

  • _BoolNO es unsigned int, pero es parte del grupo de tipos enteros sin signo . Es lo suficientemente grande como para contener los valores 0o 1.
  • NO, pero sí, puede redefinir bool truey, falsesin duda, no es una buena idea. Esta capacidad se considera obsoleta y se eliminará en el futuro.
  • Asignando un tipo escalar (tipos aritméticos y tipos de puntero) _Boolao bool, si el valor escalar es igual 0o se compara con 0él 0, de lo contrario el resultado es 1: _Bool x = 9; 9se convierte a 1cuando se le asigna x.
  • _Booles de 1 byte (8 bits), generalmente el programador está tentado a intentar usar los otros bits, pero no se recomienda, porque lo único que se garantiza es que solo se usa un bit para almacenar datos, no como el tipo charque tiene 8 bits disponibles

2

Es esto:

#define TRUE 1
#define FALSE 0

77
Me gustaría ir con algo como #define TRUE! FALSE
Tom

2

Puede usar un char u otro contenedor de números pequeños para ello.

Pseudocódigo

#define TRUE  1
#define FALSE 0

char bValue = TRUE;

También en C por lo general es un int, y puede causar la pérdida de las advertencias de precisión por otro código que utiliza int ..
Thomas Bonini

A menos que esté optimizando a mano el espacio, siempre es mejor usar el tamaño de palabra normal del hardware (por ejemplo, generalmente un int), ya que en algunas arquitecturas se obtiene un impacto significativo en el rendimiento al tener que desempaquetar / enmascarar verificaciones en estas variables.
Kingsley

2

Puede usar _Bool, pero el valor de retorno debe ser un número entero (1 para verdadero, 0 para falso). Sin embargo, se recomienda incluir y usar bool como en C ++, como se dijo en esta respuesta del foro daniweb , así como en esta respuesta , de esta otra pregunta de stackoverflow:

_Bool: tipo booleano de C99. El uso de _Bool directamente solo se recomienda si mantiene un código heredado que ya define macros para bool, true o false. De lo contrario, esas macros están estandarizadas en el encabezado. Incluya ese encabezado y puede usar bool tal como lo haría en C ++.


2

Las expresiones condicionales se consideran verdaderas si no son cero, pero el estándar C requiere que los operadores lógicos devuelvan 0 o 1.

@Tom: #define TRUE! FALSE es malo y no tiene sentido. Si el archivo de encabezado se abre paso en el código C ++ compilado, puede generar problemas:

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

Algunos compiladores generarán una advertencia sobre la conversión int => bool. A veces las personas evitan esto haciendo:

foo(flag == TRUE);

para forzar la expresión a ser un bool de C ++. Pero si #definas VERDADERO! FALSO, terminas con:

foo(flag == !0);

que termina haciendo una comparación int-to-bool que puede activar la advertencia de todos modos.


1

Si está utilizando C99, puede usar el _Booltipo. No #includes son necesarios. Sin embargo, debe tratarlo como un número entero, donde 1está truey 0está false.

Luego puede definir TRUEy FALSE.

_Bool this_is_a_Boolean_var = 1;


//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;

O puede #include <stdbool.h>y usa bool, truey falsecomo el estándar quiere que lo haga.
SS Anne

1

Hoy en día, C99 admite tipos booleanos, pero es necesario #include <stdbool.h>.

Ejemplo:

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

Salida:

0
1

0

Esto es lo que uso:

enum {false, true};
typedef _Bool bool;

_Bool es un tipo incorporado en C. Está destinado a valores booleanos.


-2

Simplemente puede usar la #definedirectiva de la siguiente manera:

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

Y use de la siguiente manera:

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

y así


55
La macro NO deben ser protegidos por los paréntesis alrededor del argy la expresión en su conjunto: #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE). Sin embargo, sería mejor probar la falsedad (dará la respuesta correcta incluso si argfue 23 en lugar de 0 o 1:. #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE)Pero la expresión completa puede reducirse #define NOT(arg) (!(arg)), por supuesto, a lo que produce el mismo resultado.
Jonathan Leffler
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.