stdbool - ¿Por qué#define TRUE(1== 1) en una macro booleana C en lugar de simplemente como 1?
php string to boolean (8)
He visto definiciones en C
#define TRUE (1==1)
#define FALSE (!TRUE)
¿Es esto necesario? ¿Cuál es el beneficio de simplemente definir TRUE como 1 y FALSE como 0?
- Artículo de lista
Normalmente, en el lenguaje de programación C, 1 se define como verdadero y 0 se define como falso. De ahí que veas lo siguiente con bastante frecuencia:
#define TRUE 1
#define FALSE 0
Sin embargo, cualquier número no igual a 0 se evaluaría como verdadero también en una declaración condicional. Por lo tanto, usando el siguiente:
#define TRUE (1==1)
#define FALSE (!TRUE)
Puedes simplemente mostrar explícitamente que estás tratando de ir a lo seguro haciendo que lo falso sea lo que no sea verdadero.
Además de C ++ (ya mencionado), otro beneficio es para las herramientas de análisis estático. El compilador eliminará cualquier ineficiencia, pero un analizador estático puede usar sus propios tipos abstractos para distinguir entre los resultados de comparación y otros tipos de enteros, por lo que sabe implícitamente que TRUE debe ser el resultado de una comparación y no debe suponerse que es compatible con un entero.
Obviamente, C dice que son compatibles, pero puede optar por prohibir el uso deliberado de esa función para ayudar a resaltar los errores, por ejemplo, donde alguien podría haber confundido &
y &&
, o han fallado en la precedencia de su operador.
El truco (1 == 1)
es útil para definir TRUE
de una manera transparente para C, pero proporciona una mejor tipa en C ++. El mismo código se puede interpretar como C o C ++ si está escribiendo en un dialecto llamado "Clean C" (que compila como C o C ++) o si está escribiendo archivos de cabecera API que pueden ser utilizados por programadores C o C ++.
En las unidades de traducción C, 1 == 1
tiene exactamente el mismo significado que 1
; y 1 == 0
tiene el mismo significado que 0
. Sin embargo, en las unidades de traducción C ++, 1 == 1
tiene tipo bool
. Entonces, la macro TRUE
definida de esa manera se integra mejor en C ++.
Un ejemplo de cómo se integra mejor es que, por ejemplo, si la función foo
tiene sobrecargas para int
y para bool
, entonces foo(TRUE)
elegirá la sobrecarga de bool
. Si TRUE
solo se define como 1
, entonces no funcionará bien en C ++. foo(TRUE)
querrá la sobrecarga int
.
Por supuesto, C99 introdujo bool
, true
y false
y estos pueden usarse en archivos de encabezado que funcionan con C99 y con C.
Sin embargo:
- esta práctica de definir
TRUE
yFALSE
como(0==0)
y(1==0)
es anterior a C99. - todavía hay buenas razones para mantenerse alejado de C99 y trabajar con C90.
Si está trabajando en un proyecto mixto de C y C ++, y no quiere C99, defina en su lugar el minúsculas true
, false
y bool
.
#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif
Dicho esto, el truco 0==0
fue (¿es?) Utilizado por algunos programadores incluso en código que nunca tuvo la intención de interoperar con C ++ de ninguna manera. Eso no compra nada y sugiere que el programador tiene un malentendido sobre cómo funcionan los booleanos en C.
En caso de que la explicación de C ++ no esté clara, aquí hay un programa de prueba:
#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;
}
La salida:
bool
int
En cuanto a la pregunta de los comentarios de cómo son las funciones sobrecargadas de C ++ relevantes para la programación mixta de C y C ++. Estos solo ilustran una diferencia de tipo. Una razón válida para querer que una constante true
sea bool
cuando se compila como C ++ es para un diagnóstico limpio. En sus niveles de advertencia más altos, un compilador de C ++ podría advertirnos acerca de una conversión si pasamos un entero como un parámetro bool
. Una razón para escribir en Clean C no es solo que nuestro código es más portátil (ya que los compiladores de C ++ lo entienden, no solo los compiladores de C), sino que también podemos beneficiarnos de las opiniones diagnósticas de los compiladores de C ++.
Este enfoque usará el tipo boolean
real (y resuelva en true
y false
) si el compilador lo admite. (específicamente, C ++)
Sin embargo, sería mejor verificar si C ++ está en uso (a través de la macro __cplusplus
) y realmente usar true
y false
.
En un compilador de C, esto es equivalente a 0
y 1
.
(tenga en cuenta que al eliminar los paréntesis se romperá debido al orden de las operaciones)
La diferencia práctica es ninguna. 0
se evalúa como false
y 1
se evalúa como true
. El hecho de que use una expresión booleana ( 1 == 1
) o 1
, para definir true
, no hace ninguna diferencia. Ambos son evaluados a int
.
Observe que la biblioteca estándar de C proporciona un encabezado específico para definir booleanos: stdbool.h
.
La respuesta es la portabilidad. Los valores numéricos de TRUE
y FALSE
no son importantes. Lo que es importante es que una afirmación como if (1 < 2)
evalúe a if (TRUE)
y una instrucción como if (1 > 2)
evalúe a if (FALSE)
.
De acuerdo, en C, (1 < 2)
evalúa a 1
y (1 > 2)
evalúa a 0
, por lo que otros han dicho, no hay diferencia práctica en lo que respecta al compilador. Pero al permitir que el compilador defina TRUE
y FALSE
acuerdo con sus propias reglas, está haciendo que sus significados sean explícitos para los programadores, y garantiza consistencia dentro de su programa y cualquier otra biblioteca (suponiendo que la otra biblioteca cumpla con los estándares C ... se sorprenderá).
Algo de historia
Algunos BASIC definieron FALSE
como 0
y TRUE
como -1
. Al igual que muchos lenguajes modernos, interpretaron cualquier valor distinto de cero como TRUE
, pero evaluaron las expresiones booleanas que eran verdaderas como -1
. Su operación NOT
se implementó agregando 1 y cambiando el signo, porque era eficiente hacerlo de esa manera. Entonces ''NO x'' se convirtió en -(x+1)
. Un efecto secundario de esto es que un valor como 5
evalúa como TRUE
, pero NOT 5
evalúa a -6
, ¡que también es TRUE
! Encontrar este tipo de error no es divertido.
Mejores prácticas
Dadas las reglas de facto de que cero se interpreta como FALSE
y cualquier valor distinto de cero se interpreta como TRUE
, nunca se deben comparar las expresiones de aspecto booleano con TRUE
o FALSE
. Ejemplos:
if (thisValue == FALSE) // Don''t do this!
if (thatValue == TRUE) // Or this!
if (otherValue != TRUE) // Whatever you do, don''t do this!
¿Por qué? Debido a que muchos programadores usan el atajo para tratar int
s como bool
s. No son lo mismo, pero los compiladores generalmente lo permiten. Entonces, por ejemplo, es perfectamente legal escribir
if (strcmp(yourString, myString) == TRUE) // Wrong!!!
Parece legítimo, y el compilador lo aceptará felizmente, pero probablemente no haga lo que usted querría. Eso es porque el valor de retorno de strcmp()
es
0 si yourString == myString
<0 si yourString < myString
> 0 si yourString > myString
Por lo tanto, la línea anterior solo devuelve TRUE
cuando yourString > myString
.
La forma correcta de hacerlo es cualquiera
// Valid, but still treats int as bool.
if (strcmp(yourString, myString))
o
// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)
Similar:
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.
A menudo encontrará algunos de estos "malos ejemplos" en el código de producción, y muchos programadores experimentados los juran: funcionan, algunos son más cortos que sus alternativas (pedantemente) correctas, y los modismos son casi universalmente reconocidos. Pero considere: las versiones "correctas" no son menos eficientes, tienen la garantía de ser portátiles, pasarán incluso los más borrosos, e incluso los nuevos programadores las comprenderán.
¿No vale la pena?
No sabemos el valor exacto al que TRUE es igual y los compiladores pueden tener sus propias definiciones. Entonces, ¿qué es privode es usar el interno del compilador para definición? Esto no siempre es necesario si tiene buenos hábitos de programación, pero puede evitar problemas para un estilo de codificación incorrecto, por ejemplo:
if ((a> b) == TRUE)
Esto podría ser un desastre si manualmente define TRUE como 1, mientras que el valor interno de TRUE es otro.
#define TRUE (1==1)
#define FALSE (!TRUE)
es equivalente a
#define TRUE 1
#define FALSE 0
Cª.
El resultado de los operadores relacionales es 0
o 1
. 1==1
se garantiza que se evaluará a 1
y !(1==1)
se garantiza que se evaluará a 0
.
No hay absolutamente ninguna razón para usar la primera forma. Tenga en cuenta que la primera forma, sin embargo, no es menos eficiente ya que en casi todos los compiladores una expresión constante se evalúa en tiempo de compilación en lugar de en tiempo de ejecución. Esto está permitido según esta regla:
(C99, 6.6p2) "Una expresión constante se puede evaluar durante la traducción en lugar de en tiempo de ejecución, y en consecuencia se puede usar en cualquier lugar que pueda ser una constante".
PC-Lint incluso emitirá un mensaje (506, valor constante booleano) si no utiliza un literal para las macros TRUE
y FALSE
:
Para C,
TRUE
debe definirse como1
. Sin embargo, otros lenguajes usan cantidades distintas de 1, por lo que algunos programadores creen que!0
es ir a lo seguro.
También en C99, las definiciones stdbool.h
para macros booleanas true
y false
usan directamente literales:
#define true 1
#define false 0