¿El estándar C indica explícitamente el valor de verdad como 0 o 1?

86

Sabemos que cualquier número que no sea igual a 0se considera como trueen C, por lo que podemos escribir:

int a = 16;

while (a--)
    printf("%d\n", a);  // prints numbers from 15 to 0

Sin embargo, me preguntaba si verdadero / falso se define como 1/ 0en C, así que probé el siguiente código:

printf("True = %d, False = %d\n", (0 == 0), (0 != 0));  // prints: True = 1, False = 0

¿El estándar C indica explícitamente los valores de verdad de verdadero y falso como 1y 0respectivamente?

Kevin Dong
fuente
3
Supongo que esta pregunta SO es relevante
Imran Ali
3
Intento ejecutar esto gcccon -std=c89y arroja el mismo resultado.
Kevin Dong
1
@Blackhole, del 15 al 0.
Arturo Torres Sánchez
6
Casi engañado, pero más de una década antes de SO / SE: c-faq.com/bool/bool2.html .
dave_thompson_085
1
Que falso es cero es canon, sin embargo, generalmente se piensa que verdadero es "no cero". Sin embargo, siendo los programadores lo que son, todos hemos utilizado 1 como "no cero" por nuestras diversas razones. Se le anima a no confiar en que un verdadero sea exactamente 1. Si bien (0 == 0) es uno en su ejemplo, algo como (12 == 12) podría tener fácilmente el valor 12; también "verdadero".
Ingeniero

Respuestas:

96

¿El estándar C indica explícitamente los valores de verdad de truey falseas 0y 1respectivamente?

El estándar C define truey falsecomo macros en las stdbool.hque se expanden 1y 0respectivamente.

C11-§7.18:

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

true

que se expande a la constante entera 1,

false

que se expande a la constante entera 0[...]

Para los operadores ==y !=, el estándar dice

C11-§6.5.9 / 3:

Los operadores ==(igual a) y !=(no igual a) son análogos a los operadores relacionales excepto por su menor precedencia. 108) Cada uno de los operadores cede 1si la relación especificada es verdadera y 0si es falsa. El resultado tiene tipo int. Para cualquier par de operandos, exactamente una de las relaciones es verdadera.

haccks
fuente
20
Me parece que la pregunta es sobre 0 == 0y 0 != 0etc., no el valor de las macros.
MM
9
Creo que cuando escribió truequiso decir "el valor de una verdadera comparación" o algo, no la macrotrue
MM
1
@KevinDong; Sí, el borrador C99 tiene un párrafo similar.
haccks
1
@haccks: puedes decir sin dudarlo "idéntico". Acabo de hacer clic en tu cita porque era demasiado vago para buscar por párrafo, ya que me refiero a c99 cuando sea necesario. Y pude encontrarlo con solo buscarlo a través de ctrl+ f;)
dhein
2
@MooingDuck: NaN == NaNes falso y NaN != NaNes verdadero. No hay ningún problema con esa afirmación.
kennytm
51

No se indica explícitamente en C11. Todas las operaciones a nivel de idioma devolverán 1 como verdadero (y aceptarán cualquier valor distinto de cero, incluido NaN, como verdadero).

  • Si le preocupa _Bool, entonces verdadero debe ser 1 porque el estándar solo requiere que tenga 0 y 1. (§6.2.5 / 2).
  • También en <stdbool.h>la macro se trueexpande a 1(§7.18 / 3)
  • ==, !=, <, >, <=Y >=devolver 0 o 1 (§6.5.8 / 6, §6.5.9 / 3).
  • !, &&y ||devuelve 0 o 1 (§6.5.3.3 / 5, §6.5.13 / 3, §6.5.14 / 3)
  • defined se expande a 0 o 1 (§6.10.1 / 1)

Pero todas las funciones de biblioteca estándar, por ejemplo, islowersolo dicen "distinto de cero" para la verdad (por ejemplo, §7.4.1 / 1, §7.17.5.1 / 3, §7.30.2.1 / 1, §7.30.2.2.1 / 4).


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

§6.5.5.3 / 5 : El resultado del operador de negación lógica !es 0 si el valor de su operando no es igual a 0, 1 si el valor de su operando es igual a 0.…

§6.5.8 / 6 : Cada uno de los operadores <(menor que), >(mayor que), <=(menor o igual que) y >=(mayor o igual que) producirá 1 si la relación especificada es verdadera y 0 si Es falso. 107) ...

§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. 108) Cada uno de los operadores produce 1 si la relación especificada es verdadera y 0 si es falso. ...

§6.5.13 / 3 : El &&operador producirá 1 si ambos operandos se comparan no igual a 0; ...

§6.5.14 / 3 : El ||operador producirá 1 si cualquiera de sus operandos es desigual a 0; ...

§6.10.1 / 1 : ... puede contener expresiones de operador unario de la forma - defined identifier- o - defined ( identifier )- que evalúan a 1 si ...

§7.4.1 (Funciones de clasificación de caracteres) / 1 : Las funciones de esta subcláusula devuelven un valor distinto de cero (verdadero) si y solo si ...

§7.18 / 3 : Las tres macros restantes son adecuadas para su uso en #ifdirectivas de preprocesamiento. Son - true- que se expande a la constante entera 1, ...

§7.17.5.1 / 3 : La atomic_is_lock_freefunción genérica devuelve un valor distinto de cero (verdadero) si y solo si las operaciones del objeto están libres de bloqueos. ...

§7.30.2.1 (Funciones de clasificación de caracteres amplios) / 1 : Las funciones de esta subcláusula devuelven un valor distinto de cero (verdadero) si y solo si ...

§7.30.2.2.1 / 4 : La iswctypefunción devuelve un valor distinto de cero (verdadero) si y solo si ...

Kennytm
fuente
23

Hay dos áreas del estándar que debe tener en cuenta al tratar con valores booleanos (con lo que me refiero a valores verdaderos / falsos en lugar del bool/_Booltipo C específico ) en C.

La primera tiene que ver con el resultado de expresiones y se puede encontrar en varias partes de C11 6.5 Expressions(operadores relacionales y de igualdad, por ejemplo). La conclusión es que, siempre que una expresión genera un valor booleano, ...

... da 1 si la relación especificada es verdadera y 0 si es falsa. El resultado tiene el tipo int.

Entonces, sí, el resultado de cualquier expresión generadora de booleanos será uno para verdadero o cero para falso. Esto coincide con lo que encontrará stdbool.hdonde las macros estándar truey falsese definen de la misma manera.

Sin embargo, tenga en cuenta que, siguiendo el principio de robustez de "sea conservador en lo que envíe, liberal en lo que acepte", la interpretación de los números enteros en el contexto booleano es algo más relajada.

Nuevamente, en varias partes de 6.5, verá un lenguaje como:

El ||operador dará 1 si cualquiera de sus operandos se compara con un 0; de lo contrario, da 0. El resultado tiene el tipo int.

Por eso (y otras partes), es obvio que cero se considera falso y cualquier otro valor es verdadero.


Por otro lado, el lenguaje que especifica qué valor se usa para la generación e interpretación booleana también aparece en C99 y C89, por lo que han existido durante bastante tiempo. Incluso K&R (ANSI-C segunda edición y la primera edición) especificó eso, con segmentos de texto como:

Las expresiones relacionales como i > jy las expresiones lógicas conectadas por &&y ||se definen para tener valor 1si son verdaderas y 0si son falsas.

En la parte de la prueba if, while, for, etc, "verdadero" sólo significa "no cero".

El &&operador ... devuelve 1 si ambos operandos no son iguales a cero, 0 en caso contrario.

El ||operador ... devuelve 1 si sus operandos no son iguales a cero y 0 en caso contrario.

Las macros también stdbool.haparecen en C99, pero no en C89 o K&R, ya que ese archivo de encabezado no existía en ese momento.

paxdiablo
fuente
2
en cuenta que ||, ==, !=etc. rendimiento int, no un tipo booleano
MM
2
Voto esta pregunta por la correcta. Para mí, la pregunta también es sobre los operadores relacionales y no sobre las macros.
ckruczek
"En la parte de la prueba if, while, for, etc, 'verdadero' sólo significa 'no cero'." Esta es la parte sobresaliente de la respuesta y es, en mi opinión, una elección desafortunada de Dennis Ritchie desde hace mucho tiempo. Cualquiera que haya escrito funciones que devuelvan códigos de error como el valor devuelto generalmente lo tiene #define noErr 0y cualquier código de error distinto de cero es un error. Y luego el problema es la simplicidad y la belleza de if ( ready_to_do_something() ){do_something();} no funciona. Tiene que ser if ( !not_ready_to_do_something() ){do_something();}"Hay muchas falsedades, pero solo una verdad". VERDADERO debe ser 0.
robert bristow-johnson
Por curiosidad, ¿cómo especificaron los primeros borradores de las reglas de C el comportamiento de "&&" y "||" en el caso de que los operandos tuvieran valores distintos de 0 o 1? El texto que cita dice "expresiones lógicas" conectadas por && y ||, pero ¿qué pasa si esos operadores conectan cosas que no sean expresiones lógicas?
supercat
1
@sdenham, sí. En la primera copia de K&R que tengo (primera edición, tirada 14, una tan temprana que menciona las características de hardware de cuatro máquinas típicas, la PDP-11, Honeywell-6000, IBM-370 e Interdata-8/32), A.7.6/7/10/11(relacional / igualdad / lógico-y / lógico-o) todos especifican que da 0 o 1 como resultado. Tener una respuesta actualizada para incluir eso.
paxdiablo
10

Está mezclando muchas cosas diferentes: declaraciones de control, operadores y tipos booleanos. Cada uno tiene sus propias reglas.

Las declaraciones de control funcionan como, por ejemplo, la ifdeclaración, C11 6.4.8.1:

En ambas formas, la primera subenunciación se ejecuta si la expresión se compara con desigual a 0.

while, foretc. tienen la misma regla. Esto no tiene nada que ver con "verdadero" o "falso".

En cuanto a los operadores que supuestamente están produciendo un resultado booleano, en realidad están produciendo un intvalor de 1 o 0. Por ejemplo, los operadores de igualdad, C11 6.5.9:

Cada uno de los operadores produce 1 si la relación especificada es verdadera y 0 si es falsa

Todo lo anterior se debe a que C no tenía un tipo booleano hasta el año 1999, e incluso cuando obtuvo uno, las reglas anteriores no se cambiaron. Entonces, a diferencia de la mayoría de los otros lenguajes de programación donde las declaraciones y los operadores producen un tipo booleano (como C ++ y Java), solo producen un int, con un valor cero o no cero. Por ejemplo, sizeof(1==1)dará 4 en C pero 1 en C ++.

El tipo booleano real en C se nombra _Booly requiere un compilador moderno. Las cabeceras de las stdbool.hmacros Define bool, truey false, que se expanden para _Bool, 1y 0respectivamente (para la compatibilidad con C ++).


Sin embargo, se considera una buena práctica de programación tratar las declaraciones de control y los operadores como si realmente requirieran / produjeran un tipo booleano. Ciertos estándares de codificación como MISRA-C recomiendan esta práctica. Es decir:

if(ptr == NULL)en lugar de if(ptr).

if((data & mask) != 0)en lugar de if(data & mask).

El objetivo de dicho estilo es aumentar la seguridad de los tipos con la ayuda de herramientas de análisis estático, lo que a su vez reduce los errores. Podría decirse que este estilo solo es significativo si usa analizadores estáticos. Aunque en algunos casos conduce a un código autodocumentado más legible, por ejemplo

if(c == '\0') 

Bien, la intención es clara, el código se autodocumenta.

versus

if(c) 

Malo. Podría significar cualquier cosa, y tenemos que buscar el tipo de cpara entender el código. ¿Es un número entero, un puntero o un carácter?

Lundin
fuente
1
sizeof(bool)es una implementación específica en C ++. Consulte stackoverflow.com/questions/4897844/is-sizeofbool-defined .
David Hammen
@DavidHammen Al igual que sizeof (0 == 0) también se define la implementación. Es solo un ejemplo.
Lundin
Pensé que C cambió las reglas para los tipos booleanos. Otros tipos de tipos de uintN (incluidos los tipos de "bits" de muchos compiladores más antiguos) almacenan los N bits más bajos de un valor e ignoran los bits más altos, mientras que los nuevos tipos booleanos "o" juntos todos los bits.
supercat
1
¿Debería ser así if(ptr != NULL), o quizás if(!ptr)?
Mathieu K.
1
if(c == '\0')se presta al error de codificación particularmente común de los principiantes if(c = '\0'), así que lo evito. De acuerdo, if(c)es malo ... debería ser, por ejemplo,if(valveIsOpen)
aja
4

He programado en muchos idiomas. He visto que true es 1 o -1 dependiendo del idioma. La lógica detrás del verdadero ser 1 era que un bit era un 0 o un 1. La lógica detrás del verdadero ser -1 era que el! El operador era el complemento de uno. Cambió todos los 1 a 0 y todos los 0 a 1 en un int. Entonces, para un int,! 0 = -1 y! (- 1) = 0. Esto me ha hecho tropezar lo suficiente como para no comparar algo para que sea == verdadero, sino que lo comparo para que sea! = Falso. De esa forma, mi estilo de programación funciona en todos los idiomas. Entonces mi respuesta es no preocuparse por eso, sino programar para que su código funcione correctamente de cualquier manera.

Russell Hankins
fuente
como puedo cambie todos los 0 por 1 y aún produzca 0 para! 5?
codeshot
@codeshot No puede. ¡Pero lo que está diciendo es que no todos los lenguajes tratan el operando de! como booleano. ¡Algunas golosinas! como C ~, es decir, un complemento bit a bit. En cuyo caso, determinar el valor resultante requiere conocer el tipo de variable en primer lugar, por lo que! (Uint32_t) 5 sería 4.294.967.290. Pero! 0 sigue siendo 4,294,967,295 y 4,294,967,295 es verdad.
Pegasus Epsilon
1

Esta respuesta debe examinarse un poco más de cerca.

La definición real en C ++ es que todo lo que no sea 0 se trata como verdadero. ¿Por qué es esto relevante? Debido a que C ++ no sabe qué es un número entero por cómo lo pensamos, creamos ese significado, todo lo que contiene es el shell y las reglas para lo que eso significa. Sin embargo, sabe qué son los bits, los que forman un número entero.

1 como un entero se representa libremente en bits, digamos un int de 8 bits con signo como 0000 0001. Muchas veces lo que vemos visualmente es una mentira, -1 es una forma mucho más común de representarlo debido a la naturaleza con signo de 'entero'. Realmente no puedo decir verdadero, ¿por qué? Porque su operación NO es 1111 1110. Ese es un problema realmente importante para un booleano. Cuando hablamos de un booleano, es solo 1 bit; es realmente simple, 0 es falso y 1 es verdadero. Todas las operaciones lógicas son triviales. Es por eso que '-1' debe designarse como 'verdadero' para enteros (con signo). 1111 1111 NOT'ed se convierte en 0000 0000 --- la lógica se mantiene y estamos bien. Las entradas sin firmar son un poco complicadas y se usaban mucho más comúnmente en el pasado, donde 1 significa verdadero porque es fácil implicar la lógica de que '

Ésa es la explicación. Digo que la respuesta aceptada aquí es incorrecta: no hay una definición clara en la definición de C / C ++. Un booleano es un booleano, puede tratar un número entero como un booleano, pero el hecho de que la salida sea un número entero no dice nada sobre la operación que realmente se está realizando es bit a bit.

Tommy
fuente
4
La pregunta era sobre C, no sobre C ++.
glglgl
0

Ocurrió debido a los operadores relacionales en su printfdeclaración.

Operador ==y operador!=

Dado que (0 == 0)es cierto, da un valor1

mientras que, (0 != 0)no es así, da un valor 0.

SKD
fuente