2011-07-08 26 views
5

Acabo de tropezar con una afirmación, que falló, ya que comparó falso con el tipo devuelto de una función, ya que la función devolvió un bool y la afirmación verificó no solo el valor, sino también el tipo de valor de retorno para coincidir con el de falso, para garantizar, que se devuelve un bool. Ahora el problema es que C99 define bool como _Bool y _Bool incluso no es necesariamente del mismo tamaño que int (de hecho, en mi experiencia, en la mayoría de las plataformas en la actualidad es a menudo del mismo tamaño que char sin signo), no para hablar de ser mismo tipo (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 ningún tipo de definición de sintonía y preprocesador sin un tipo de transmisión predeterminado a int. Si C99 en cambio definiera falso y verdadero como ((bool) 0) y ((bool) 1), siempre serían de tipo bool, sin importar cómo se define _Bool. Entonces, ¿hay alguna buena razón para tenerlas siempre definidas como enteros, incluso cuando bool no es una int en esa plataforma o es solo un error en el lenguaje, que debería corregirse con C1x?C99 - ¿por qué son falsos y verdaderos definidos como 0 y 1 y no como ((bool) 0) y ((bool) 1)?

Respuesta

8

false y true se definen como el número entero constantes de 0 y 1 respectivamente, porque eso es exactamente lo que el estándar C99 especifica en la sección 7,16:

< SNIP>

Los tres macros restantes son adecuado para su uso en #if directivas de preprocesamiento. Ellos son

cierto

que se expande a la constante entera 1,

falsa

que se expande al número entero constante 0, y

< SNIP>

EDIT: como indican los comentarios a continuación, parece que malinterpreté un poco la pregunta, y debería haber proporcionado la razón por la que el estándar así lo especifica. Una razón por la que puedo pensar es que se supone que true y false se pueden usar en las directivas de preprocesamiento #if (como la cita de las menciones estándar).

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

La expresión que controla inclusión condicional será un número entero expresión constante excepto que: no deberá contener un molde;

+0

Buena respuesta, pero proporcionar una cita real la convertiría en una gran respuesta. – Nemo

+0

-1 para mendigar la pregunta. –

+0

@Nemo: el bloque de código contiene una cita directa del estándar C99, pero podría haberlo indicado explícitamente, y haberlo hecho como una cita (corregida ahora). –

2

primer lugar, aunque _Bool puede no ser int, se requiere que un _Bool puede aceptar los valores 0 y 1, por lo tanto la expansión de true y false a 1 y 0 son bien.

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

También, para la compatibilidad con versiones anteriores, true y false son razonables ser int s, porque todos los operadores lógicos devuelven int.

C99 §6.5.3.3/5: El resultado del operador negación lógica ! es 0 si el valor de su operando compara desigual a 0, 1 si el valor de su operando compara igual a 0. El resultado tiene 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 a), y >= (mayor que o igual a) deberá dar 1 si la relación especificada es verdadera y 0 si es falsa. 90) El resultado tiene el tipo int.

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

C99 §6.5.13/3: El operador && obtendrá 1 si sus dos operandos son comparables a 0; de lo contrario, arroja 0. El resultado tiene el tipo int.

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

Y finalmente, como @Sander De Dycker mencionado, la norma define true y false expandirse como que (C99 §7.16/3).

+0

Pero normalmente verificará los operadores lógicos sin usar verdadero o falso y escribirá el código como "if (foo) "y" if (! foo) "(porque la especificación de implementación TRUE-macro solo se garantiza que sea! = FALSE, pero no para ser 1, y si escribe código como" if (foo == true) ", entonces podrías accidentalmente adquirir ese hábito para VERDADERO). Además, si evitar un rechazo implícito al comparar los resultados del operador lógico con verdadero/falso sería un problema, entonces guardar estos resultados en bools o verificar un bool contra verdadero/falso tendría el mismo problema y sería mejor para el estándar definir bool como int. – Kaiserludi

4

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

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

... Las conversiones aritméticas habituales se realizan sobre los operandos ...

" conversiones aritméticas habituales "tiene un significado muy específico en el estándar C. Se define en §6.3.1.8, e incluye los siguientes:

... las promociones enteros se realizan en ambos operandos ...

"promociones enteros" es también un término definido, desde §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 a unsigned int. Estas se llaman promociones enteras.48) Los demás tipos no se modifican por las promociones enteras.

Aunque existen tipos más estrechos que int en la norma C, que se ensanchan de forma automática 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 opción natural para el tipo de estos literales.

+0

¿por qué usaría un AND binario en dos booleanos? – dascandy

+1

@dascandy: totalmente al lado del punto. Además, ¿por qué no? –

+1

@dascandy: ¿Qué pasaría si los dos operandos fueran llamadas de función que devolvían '_Bool', y qué pasa si la función llama tiene efectos secundarios que no quiere perder por un cortocircuito? – jamesdlin

1

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

Para hacerlo, sería posible, pero más complicado lo necesario. Fue mucho más fácil para los escritores de estándares y los escritores de compiladores hacer solo los cambios mínimos necesarios para agregar un nuevo tipo booleano al lenguaje. Dado que todas las operaciones booleanas todavía se evalúan como int, todos los compiladores anteriores a C99 se pueden actualizar para admitir C99 sin tener que cambiar su código de evaluación de tipo para todos los operadores básicos, y los redactores estándar pueden confiar más en que el nuevo La función booleana no ha introducido accidentalmente inconsistencias en partes del estándar que anteriormente estaban bien. Todo lo que necesitaban hacer era asegurarse de que las "conversiones aritméticas habituales" se aplicaran al _Bool, y entonces todo lo demás está garantizado para funcionar.

No es un motivo técnico. Es práctico.

Cuestiones relacionadas