C99: ¿por qué se define falso y verdadero como 0 y 1 y no como ((bool) 0) y ((bool) 1)?

Simplemente encontré una afirmación, que falló, ya que comparó falso con el tipo de retorno de una función, ya que la función misma devolvió un bool y la afirmación verificó no solo el valor, sino también el tipo de valor de retorno para que coincida con el de falso, Para garantizar, que se devuelve un bool. Ahora el problema es que C99 define bool como _Bool y _Bool no es necesariamente del mismo tamaño que int (de hecho, en mi experiencia, en la mayoría de las plataformas en la actualidad a menudo es del mismo tamaño que el personaje sin signo), por no hablar de ser el mismo type (lo que en realidad es imposible, ya que _Bool es un tipo incorporado del lenguaje en C99), pero define falso y verdadero como 0 y 1 sin ninguna definición de cadena de caracteres y las definiciones del preprocesador sin una cadena de caracteres predeterminada se establecerán como int. Si C99 en su lugar definiera falso y verdadero como ((bool) 0) y ((bool) 1), siempre serían de tipo bool, no importa, cómo se define _Bool. Entonces, ¿hay alguna buena razón para tenerlos siempre definidos como ints, incluso cuando bool no es un int en esa plataforma o es solo un error en el lenguaje que debería solucionarse con C1x?

false y true se definen como las constantes enteras 0 y 1 respectivamente, porque eso es exactamente lo que especifica el estándar C99 en la sección 7.16 :

Las tres macros restantes son adecuadas para su uso en directivas de preprocesamiento #if . Son

cierto

que se expande a la constante entera 1,

falso

que se expande a la constante entera 0, y

EDITAR : como lo indican los comentarios a continuación, parece que malinterpreté ligeramente la pregunta, y debería haber proporcionado la razón por la cual la norma lo especifica así. Una razón por la que puedo pensar es que se supone que lo true y lo false se pueden utilizar en las directivas de preprocesamiento de #if (como se menciona en la cita de la norma).

La razón ((bool) 0) o ((bool) 1) no funciona en las directivas de preprocesamiento de #if , es porque el estándar no lo permite. En la sección 6.10.1 dice:

La expresión que controla la inclusión condicional será una expresión constante de enteros, excepto que: no contendrá una conversión;

Más allá de las otras razones ya mencionadas, porque un _Bool convierte en un int todos modos tan pronto como haces casi cualquier cosa con él.

Por ejemplo, ¿cuál es el tipo de (_Bool)0 & (_Bool)1 ? Podría pensar que la expresión tiene el tipo _Bool , pero en realidad §6.5.10 define la semántica de & :

… Las conversiones aritméticas habituales se realizan en los operandos …

Las “conversiones aritméticas habituales” tienen un significado muy específico en el estándar C. Se define en §6.3.1.8 e incluye lo siguiente:

… las promociones enteras se realizan en ambos operandos …

“promociones enteras” también es un término definido, de §6.3.1.1:

Si un int puede representar todos los valores del tipo original, el valor se convierte en un int; de lo contrario, se convierte en un int sin signo. Estos se denominan promociones enteras.48) Todos los demás tipos no se modifican por las promociones enteras.

Aunque existen tipos más estrechos que int en el estándar C, se amplían automáticamente a int en casi cualquier expresión. Junto con el hecho de que el resultado de las operaciones booleanas tiene el tipo int , esto hace que int sea ​​una elección natural para el tipo de estos literales.

En primer lugar, aunque _Bool puede no ser int , se requiere que un _Bool pueda aceptar los valores 0 y 1, por lo tanto, expandir true y false a 1 y 0 está bien.

C99 §6.2.5 / 2 : Un objeto declarado como tipo _Bool es lo suficientemente grande como para almacenar los valores 0 y 1.

Además, para compatibilidad con versiones anteriores, true y false son razonables para ser int s, porque todos los operadores lógicos devuelven int .

C99 §6.5.3.3 / 5 : ¡El resultado del operador de negación lógica ! es 0 si el valor de su operando se compara con 0, 1 si el valor de su operando se compara con 0. El resultado tiene el tipo int . La expresión !E es equivalente a (0==E) .

C99 §6.5.8 / 6 : Cada uno de los operadores < (menor que), > (mayor que), <= (menor o igual que), y >= (mayor o igual que) dará 1 si el valor especificado La relación es verdadera y 0 si es falsa. 90) El resultado tiene tipo int .

C99 §6.5.9 / 3 : Los operadores == (igual a) y != (No igual a) son análogos a los operadores relacionales, excepto por su precedencia más baja. 91) Cada uno de los operadores produce 1 si la relación especificada es verdadera y 0 si es falsa. El resultado tiene tipo int . Para cualquier par de operandos, exactamente una de las relaciones es verdadera.

C99 §6.5.13 / 3 : El operador && deberá producir 1 si sus dos operandos se comparan de manera desigual con 0; de lo contrario, arroja 0. El resultado tiene tipo int .

C99 §6.5.14 / 3 : El || el operador producirá 1 si cualquiera de sus operandos se compara desigual con 0; de lo contrario, arroja 0. El resultado tiene tipo int .

Y finalmente, como lo mencionó @Sander De Dycker , el estándar definido true y false se expandirá de esa manera (C99 §7.16 / 3).

Todas las demás respuestas están tratando de usar el estándar para justificar por qué el estándar define las cosas de una manera determinada, lo cual me parece insatisfactorio. El estándar define no solo los tipos, sino también los operadores y el preprocesador, por lo que si C99 introdujera un tipo booleano, ¿por qué no cambiar todos los operadores booleanos para evaluar un valor de ese tipo y extender el preprocesador para admitir los tipos booleanos?

Hacerlo sería posible, pero más complicado lo necesario. Fue mucho más fácil para los escritores estándar y los comstackdores hacer los cambios mínimos necesarios para agregar un nuevo tipo booleano al lenguaje. Dado que todas las operaciones booleanas aún se evalúan al tipo int , todos los comstackdores anteriores a C99 pueden actualizarse para admitir C99 sin tener que cambiar su código de evaluación de tipo para todos los operadores básicos, y los escritores estándar pueden tener más confianza de que el nuevo La característica booleana no ha introducido inconsistencias de forma accidental en partes del estándar que anteriormente habían estado bien. Todo lo que tenían que hacer era asegurarse de que las “conversiones aritméticas habituales” se aplicaran a _Bool , y luego se garantizaría que todo lo demás funcionaría.

No es una razón técnica. Es una práctica.