¿Por qué "sizeof (a? True: false)" da una salida de cuatro bytes?

133

Tengo un pequeño fragmento de código sobre el sizeofoperador con el operador ternario:

#include <stdio.h>
#include <stdbool.h>

int main()
{
    bool a = true;
    printf("%zu\n", sizeof(bool));  // Ok
    printf("%zu\n", sizeof(a));     // Ok
    printf("%zu\n", sizeof(a ? true : false)); // Why 4?
    return 0;
}

Salida ( GCC ):

1
1
4 // Why 4?

Pero aquí,

printf("%zu\n", sizeof(a ? true : false)); // Why 4?

el operador ternario devuelve el booleantipo y el tamaño del booltipo es 1byte en C.

Entonces, ¿por qué sizeof(a ? true : false)da una salida de cuatro bytes?

msc
fuente
39
sizeof(true)y sizeof(false)también es 4: ide.geeksforgeeks.org/O5jvuN
tkausl
77
La pregunta más interesante aquí sería por qué esta implementación es "inconsistente", ya que obviamente define _Booltener el tamaño 1, pero no truey false. Pero el estándar no tiene nada que decir al respecto por lo que puedo decir.
12
@FelixPalmen mismo motivo por el que se da char a; sizeof(a) == 1y sizeof('a') == sizeof(int)(en C). No se trata de la implementación, se trata del lenguaje.
n. 'pronombres' m.
10
¿Has intentado imprimir sizeof(true)? tal vez aclarará un poco más las cosas (en particular, será obvio que el operador ternario es un arenque rojo).
n. 'pronombres' m.
44
@FelixPalmen truees #defined ser 1 por stdbool.hlo que sí, esta es la definición literal.
n. 'pronombres' m.

Respuestas:

223

Es porque tienes #include <stdbool.h>. Ese encabezado define macros true y falseser 1y 0, por lo que su declaración se ve así:

printf("%zu\n", sizeof(a ? 1 : 0)); // Why 4?

sizeof(int) es 4 en tu plataforma.

Justin
fuente
21
"Es porque tienes #include <stdbool.h>" No, no lo es. sizeof(a ? (uint8_t)1 : (uint8_t)0);también daría un resultado de 4. La promoción entera de los ?:operandos es la parte importante aquí, no el tamaño de truey false.
Lundin
9
@Lundin: Ambos son importantes. Tal como está escrito, el tipo ya intno tiene promoción. La razón por la que no puede "arreglarlo" son las promociones predeterminadas.
R .. GitHub DEJA DE AYUDAR AL HIELO
55
@PeterSchneider Esto no es C ++. Esto es C. En C ++, truey nofalse son macros; Son palabras clave. No están definidos para ser y , sino para ser los valores verdaderos y falsos del tipo. 10bool
Justin
55
@PeterSchneider No, hoy aprendiste algo sobre C. No confundas los dos idiomas. En C ++, sizeof(true)es 1. demo .
Rakete1111
1
Es cierto, lo mezcló. No había leído con cuidado y estaba mal escrito por cppreference-link. Mi culpa, gracias. Pero tengo este sentimiento sobre c ++ de todos modos.
Peter Schneider
66

Aquí, booleantipo de retorno del operador ternario ,

OK, hay más que eso!

En C, el resultado de esta operación ternaria es de tipo int. [notas a continuación (1,2)]

Por lo tanto, el resultado es el mismo que la expresión sizeof(int), en su plataforma.


Nota 1: Cita C11, capítulo §7.18,Boolean type and values <stdbool.h>

[....] 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, [....]

Nota 2: Para operador condicional, capítulo §6.5.15, ( énfasis mío )

Se evalúa el primer operando; hay un punto de secuencia entre su evaluación y la evaluación del segundo o tercer operando (lo que se evalúe). El segundo operando se evalúa solo si el primero se compara desigual a 0; el tercer operando se evalúa solo si el primero se compara igual a 0; el resultado es el valor del segundo o tercer operando (lo que se evalúe), [...]

y

Si tanto el segundo como el tercer operando tienen un tipo aritmético, el tipo de resultado que se determinaría mediante las conversiones aritméticas habituales, si se aplicaran a esos dos operandos, es el tipo del resultado. [....]

por lo tanto, el resultado será de tipo entero y, debido al rango de valores, las constantes son precisamente de tipo int.

Dicho esto, un consejo genérico, int main()debería ser mejor cumplir int main (void)con los estándares.

Sourav Ghosh
fuente
@ user694733 umm ... ¿por qué no? <stdbool.h>define que los MACROS son de tipo int... ¿está mal?
Sourav Ghosh
@BasileStarynkevitch OK, veo que ahora, esto parece realmente incorrecto, actualizado ahora.
Sourav Ghosh
58

El operador ternario es un arenque rojo.

    printf("%zu\n", sizeof(true));

imprime 4 (o lo sizeof(int)que sea ​​que esté en su plataforma).

Lo siguiente supone que booles un sinónimo charo un tipo similar de tamaño 1, y intes mayor que char.

La razón por la cual sizeof(true) != sizeof(bool)y sizeof(true) == sizeof(int)es simplemente porque notrue es una expresión de tipo . Es una expresión de tipo . Es d como en .boolint#define1stdbool.h

No hay valores de tipo boolen C en absoluto. Todos estos valores se promueven de inmediato int, incluso cuando se usan como argumento para sizeof. Editar: este párrafo no es cierto, argumentos para sizeofno ser promovido int. Sin embargo, esto no afecta a ninguna de las conclusiones.

norte. 'pronombres' m.
fuente
Buena respuesta. Después de leer la respuesta más votada actualmente, estaba pensando que todas las declaraciones deberían evaluar a 4. Esto aclaró las cosas. +1
Pedro A
55
¿No es (bool)1un valor de tipo bool?
Ben Voigt
printf("%u\n", sizeof((char) 1));imprime 1en mi plataforma mientras printf("%u\n", sizeof(1));imprime 4. ¿No significa esto que su afirmación "Cada uno de esos valores se promueve inmediatamente a int, incluso cuando se usa como argumento para sizeof" es falsa?
JonatanE
Esto realmente no responde la pregunta. El tamaño y el tipo de trueetc. realmente no importan en el caso de ?:que, de inttodos modos, se promueve un número entero . Es decir, la respuesta debe abordar por qué ?: es un arenque rojo.
Lundin
66
Creo que la respuesta aborda el problema de la mejor manera posible. Eres bienvenido a denunciarlo o mejorarlo.
n. 'pronombres' m.
31

En cuanto al tipo booleano en C

Un tipo booleano se introdujo bastante tarde en el lenguaje C, en el año 1999. Antes de eso, C no tenía un tipo booleano, sino que se usaba intpara todas las expresiones booleanas. Por lo tanto, todos los operadores lógicos como > == !etc. devuelven un intvalor 1o 0.

Fue personalizado para las aplicaciones utilizar tipos caseros como typedef enum { FALSE, TRUE } BOOL;, que también se reduce a inttipos de tamaño.

C ++ tenía un tipo booleano mucho mejor y explícito bool, que no era mayor de 1 byte. Mientras que los tipos booleanos o expresiones en C terminarían como 4 bytes en el peor de los casos. Se introdujo algún tipo de compatibilidad con C ++ en C con el estándar C99. C luego obtuvo un tipo booleano _Booly también el encabezado stdbool.h.

stdbool.hProporciona cierta compatibilidad con C ++. Este encabezado define la macro bool(la misma ortografía que la palabra clave C ++) que se expande a _Bool, un tipo que es un tipo entero pequeño, probablemente 1 byte grande. Del mismo modo, la cabecera proporciona dos macros truey false, misma ortografía como palabras clave C ++, pero con compatibilidad con versiones anteriores de los programas en C de más edad . Por lo tanto truey falseampliar a 1y 0en C y su tipo es int. Estas macros no son en realidad del tipo booleano como lo serían las palabras clave correspondientes de C ++.

Del mismo modo, para fines de compatibilidad con versiones anteriores, los operadores lógicos en C aún devuelven un inthasta el día de hoy, a pesar de que C actualmente tiene un tipo booleano. Mientras que en C ++, los operadores lógicos devuelven a bool. Por lo tanto, una expresión tal como sizeof(a == b)dará el tamaño de an inten C, pero el tamaño de a boolen C ++.

Sobre el operador condicional ?:

El operador condicional ?:es un operador extraño con un par de peculiaridades. Es un error común creer que es 100% equivalente a if() { } else {}. No exactamente.

Hay un punto de secuencia entre la evaluación del primer y segundo o tercer operando. Se ?:garantiza que el operador solo evaluará el segundo o el tercer operando, por lo que no puede ejecutar ningún efecto secundario del operando que no se evalúe. Código como true? func1() : func2()no se ejecutará func2(). Hasta aquí todo bien.

Sin embargo , hay una regla especial que establece que los operandos segundo y tercero deben obtener implícitamente el tipo promocionado y equilibrado entre sí con las conversiones aritméticas habituales . ( Las reglas de promoción de tipo implícito en C se explican aquí ). Esto significa que el segundo o tercer operando siempre será al menos tan grande como un int.

Por lo tanto, no importa eso truey falseresulta ser de tipo intC porque la expresión siempre daría al menos el tamaño de un intno importa.

¡Incluso si reescribiera la expresión , aún devolvería el tamaño de un !sizeof(a ? (bool)true : (bool)false) int

Esto se debe a la promoción de tipo implícito a través de las conversiones aritméticas habituales.

Lundin
fuente
1
C ++ no garantiza realmente sizeof(bool)==1.
aschepler
1
@aschepler No, pero el mundo real fuera del estándar C ++ lo garantiza. Nombra un compilador donde no es 1.
Lundin
Hola. Creo que esta respuesta sería mejor sin su primera parte. La segunda parte responde a la pregunta. El resto, aunque interesante, es solo ruido.
YSC
@YSC Esto fue originalmente etiquetado como C y C ++, por lo que fue necesaria una comparación entre sus diferentes tipos de bool y la historia detrás de ellos. Dudo que hubiera escrito la primera parte si no fuera por la etiqueta C ++. Sin embargo, uno tiene que entender por qué sizeof (bool) es 1 pero sizeof (false) es 4 en C.
Lundin
21

Respuesta rápida:

  • sizeof(a ? true : false)se evalúa como 4porque truey falsese definen <stdbool.h>como 1y 0respectivamente, por lo que la expresión se expande a sizeof(a ? 1 : 0)una expresión entera con tipo int, que ocupa 4 bytes en su plataforma. Por la misma razón, sizeof(true)también evaluaría 4en su sistema.

Sin embargo, tenga en cuenta que:

  • sizeof(a ? a : a)también evalúa 4porque el operador ternario realiza las promociones enteras en su segundo y tercer operandos si son expresiones enteras. Lo mismo sucede, por supuesto, para sizeof(a ? true : false)y sizeof(a ? (bool)true : (bool)false), pero echando toda la expresión como boolse comporta como se esperaba: sizeof((bool)(a ? true : false)) -> 1.

  • También señalan que los operadores de comparación albergar valores booleanos 1o 0, mas tenga intTipo: sizeof(a == a) -> 4.

Los únicos operadores que mantienen la naturaleza booleana de aserían:

  • el operador de coma: ambos sizeof(a, a)y sizeof(true, a)evaluar 1en tiempo de compilación.

  • los operadores de asignación: ambos sizeof(a = a)y sizeof(a = true)tienen un valor de 1.

  • los operadores de incremento: sizeof(a++) -> 1

Finalmente, todo lo anterior se aplica solo a C: C ++ tiene una semántica diferente con respecto al booltipo, los valores booleanos truey los falseoperadores de comparación y el operador ternario: todas estas sizeof()expresiones se evalúan 1en C ++.

chqrlie
fuente
2
Buena respuesta que en realidad se las arregla para señalar que realmente no importa de qué tipo truey qué falseson, porque los ?:operandos serían promovidos a enteros de inttodos modos. Por sizeof(a ? (uint8_t)true : (uint8_t)false)lo tanto , también dará 4 como resultado.
Lundin
Esta respuesta cubre el punto principal importante, valor promovido aint
Chinni
1

Aquí hay un fragmento del cual es lo que se incluye en la fuente

#ifndef __cplusplus

#define bool    _Bool
#define true    1
#define false   0

#else /* __cplusplus */

Hay macros truey falsese declaran como 1 y 0 respectivamente.

sin embargo, en este caso, el tipo es el tipo de las constantes literales. Tanto 0 como 1 son constantes enteras que caben en un int, por lo que su tipo es int.

y el sizeof(int)en tu caso es 4.

u__
fuente
-3

No hay ningún tipo de datos booleanos en C, en su lugar, las expresiones lógicas se evalúan a valores enteros 1cuando es verdadero de lo contrario 0.

Las expresiones condicionales como if, for, while, o c ? a : besperar un entero, si el número no es cero se considera truea excepción de algunos casos especiales, he aquí una función suma recursiva en la que el operador ternario-evaluará truehasta que nalcance 0.

int sum (int n) { return n ? n+sum(n-1) : n ;

También se puede usar para NULLverificar un puntero, aquí hay una función recursiva que imprime el contenido de una Lista enlazada individualmente.

void print(sll * n){ printf("%d -> ",n->val); if(n->next)print(n->next); }
Khaled.K
fuente