Por que "sizeof (a? True: false)" fornece uma saída de quatro bytes?

133

Eu tenho um pequeno pedaço de código sobre o sizeofoperador com o operador ternário:

#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;
}

Saída ( GCC ):

1
1
4 // Why 4?

Mas aqui,

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

o operador ternário retorna booleantipo e tamanho do booltipo é 1byte em C.

Então, por que sizeof(a ? true : false)fornecer uma saída de quatro bytes?

msc
fonte
39
sizeof(true)e sizeof(false)também é 4: ide.geeksforgeeks.org/O5jvuN
tkausl
7
A questão mais interessante aqui seria por que essa implementação é "inconsistente", pois ela obviamente define o _Booltamanho 1, mas não truee false. Mas o padrão não tem nada a dizer sobre isso, tanto quanto eu posso dizer.
12
@FelixPalmen mesma razão pela qual é dado char a; sizeof(a) == 1e sizeof('a') == sizeof(int)(em C). Não é sobre a implementação, é sobre a linguagem.
n. 'pronomes' m.
10
Você já tentou imprimir sizeof(true)? talvez isso torne as coisas um pouco mais claras (em particular, ficará óbvio que o operador ternário é um arenque vermelho).
n. 'pronomes' m.
4
@FelixPalmen trueé #defined 1 stdbool.hcomo sim, esta é a definição literal.
n. 'pronomes' m.

Respostas:

223

É porque você tem #include <stdbool.h>. Esse cabeçalho define macros true e falsebe , 1e 0, portanto, sua declaração é assim:

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

sizeof(int) é 4 na sua plataforma.

Justin
fonte
21
"É porque você tem #include <stdbool.h>" Não, não é. sizeof(a ? (uint8_t)1 : (uint8_t)0);também daria um resultado de 4. A promoção inteira dos ?:operandos é a parte importante aqui, não o tamanho de truee false.
Lundin
9
@ Lundin: Ambos são importantes. Conforme escrito, o tipo já está intsem promoção. O motivo pelo qual você não pode "consertar" são as promoções padrão.
R .. GitHub Pare de ajudar o gelo
5
@ PeterSchneider Este não é C ++. Isso é C. Em C ++, truee nãofalse são macros; são palavras-chave. Eles não estão definidos para ser e , mas para ser os valores verdadeiros e falsos do tipo. 10bool
31717 Justin
5
@ PeterSchneider Não, você aprendeu algo sobre C hoje. Não confunda os dois idiomas. Em C ++, sizeof(true)é 1. demo .
Rakete1111
1
É verdade, misturei tudo. Não tinha lido com atenção e foi enganado pelo cppreference-link. A culpa é minha, obrigada. Mas eu tenho esse sentimento sobre c ++ de qualquer maneira.
Peter Schneider
66

Aqui, booleantipo de retorno do operador ternário ,

OK, há mais do que isso!

Em C, o resultado dessa operação ternária é do tipo int. [notas abaixo (1,2)]

Portanto, o resultado é o mesmo que a expressão sizeof(int)na sua plataforma.


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

[....] As três macros restantes são adequadas para uso em #ifdiretivas de pré-processamento. Eles são

true

que se expande para a constante inteira 1,

false

que se expande para a constante inteira 0, [....]

Nota 2: Para operador condicional, capítulo §6.5.15, ( ênfase minha )

O primeiro operando é avaliado; existe um ponto de sequência entre sua avaliação e a avaliação do segundo ou terceiro operando (o que for avaliado). O segundo operando é avaliado apenas se o primeiro comparar com 0; o terceiro operando é avaliado apenas se o primeiro for comparado a 0; o resultado é o valor do segundo ou terceiro operando (o que for avaliado), [...]

e

Se o segundo e o terceiro operandos tiverem um tipo aritmético, o tipo de resultado que seria determinado pelas conversões aritméticas usuais, caso fossem aplicadas a esses dois operandos, é o tipo do resultado. [....]

portanto, o resultado será do tipo inteiro e, devido ao intervalo de valores, as constantes são precisamente do tipo int.

Dito isto, um conselho genérico int main()deve ser melhor int main (void)para ser verdadeiramente compatível com os padrões.

Sourav Ghosh
fonte
@ user694733 umm..why não? <stdbool.h>define o MACROS para ser do tipo int..é errado?
Sourav Ghosh 30/10
@BasileStarynkevitch OK, vejo que agora, isso parece realmente errado, atualizado agora.
Sourav Ghosh 30/10
58

O operador ternário é um arenque vermelho.

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

imprime 4 (ou o que sizeof(int)estiver na sua plataforma).

O seguinte pressupõe que boolé um sinônimo charou um tipo semelhante de tamanho 1 e inté maior que char.

A razão pela qual sizeof(true) != sizeof(bool)e sizeof(true) == sizeof(int)é simplesmente porque nãotrue é uma expressão do tipo . É uma expressão do tipo . É d como em .boolint#define1stdbool.h

Não há valores de tipo boolem C. Todo esse valor é imediatamente promovido para int, mesmo quando usado como argumento para sizeof. Editar: este parágrafo não é verdadeiro, argumentos para sizeofos quais não é promovido int. Isso não afeta nenhuma das conclusões.

n. 'pronomes' m.
fonte
Boa resposta. Depois de ler a resposta mais votada atualmente, pensei que todas as declarações deveriam ser avaliadas em 4. Isso esclareceu as coisas. +1
Pedro A
5
Não é (bool)1um rvalor do tipo bool?
Ben Voigt
printf("%u\n", sizeof((char) 1));imprime 1na minha plataforma enquanto printf("%u\n", sizeof(1));imprime 4. Isso não significa que sua declaração "Todo valor desse valor é imediatamente promovido para int, mesmo quando usado como argumento para sizeof" é falsa?
JonatanE
Isso realmente não responde à pergunta. O tamanho e tipo de trueetc realmente não importa no caso de, ?:uma vez que é promovido inteiro para intqualquer forma. Ou seja, a resposta deve abordar o porquê de ?: um arenque vermelho.
Lundin
6
Penso que a resposta aborda a questão da melhor maneira possível. Você pode votar ou melhorá-lo.
n. 'pronomes' m.
31

Em relação ao tipo booleano em C

Um tipo booleano foi introduzido bastante tarde na linguagem C, no ano de 1999. Antes disso, C não tinha um tipo booleano, mas era usado intpara todas as expressões booleanas. Portanto, todos os operadores lógicos, como > == !etc, retornam um intvalor 1ou 0.

Era costume que os aplicativos usassem tipos caseiros como typedef enum { FALSE, TRUE } BOOL;, que também se resume a inttipos de tamanho.

O C ++ tinha um tipo booleano muito melhor e explícito bool, que não era maior que 1 byte. Enquanto os tipos ou expressões booleanos em C terminariam em 4 bytes no pior caso. Alguma maneira de compatibilidade com C ++ foi introduzida em C com o padrão C99. C então obteve um tipo booleano _Boole também o cabeçalho stdbool.h.

stdbool.hfornece alguma compatibilidade com C ++. Esse cabeçalho define a macro bool(a mesma ortografia que a palavra-chave C ++) que se expande para _Bool, um tipo que é um tipo inteiro pequeno, provavelmente com 1 byte de largura. Da mesma forma, o cabeçalho fornece dois macros truee false, mesmo ortografia como palavras-chave C ++, mas com compatibilidade com versões anteriores para programas em C mais velhos . Por isso truee falseexpandir-se para 1e 0em C e seu tipo é int. Essas macros não são realmente do tipo booleano, como seriam as palavras-chave C ++ correspondentes.

Da mesma forma, para fins de compatibilidade com versões anteriores, os operadores lógicos em C ainda retornam um intaté hoje, mesmo que C atualmente tenha um tipo booleano. Enquanto em C ++, operadores lógicos retornam a bool. Assim, uma expressão como sizeof(a == b)dará o tamanho de um intem C, mas o tamanho de um boolem C ++.

Em relação ao operador condicional ?:

O operador condicional ?:é um operador estranho com algumas peculiaridades. É um erro comum acreditar que é 100% equivalente a if() { } else {}. Não é bem assim.

Há um ponto de sequência entre a avaliação do 1º e do 2º ou 3º operando. O ?:operador é garantido apenas para avaliar tanto o 2º ou o 3º operando, por isso não pode executar quaisquer efeitos colaterais do operando que não é avaliado. Código como true? func1() : func2()não será executado func2(). Por enquanto, tudo bem.

No entanto , existe uma regra especial que afirma que o segundo e o terceiro operando devem ser implicitamente promovidos e equilibrados entre si com as conversões aritméticas usuais . ( Regras implícitas de promoção de tipo em C explicadas aqui ). Isso significa que o segundo ou terceiro operando sempre será pelo menos tão grande quanto um int.

Portanto, não importa isso truee falseseja do tipo intC, porque a expressão sempre daria pelo menos o tamanho de um intnão.

Mesmo se você reescrevesse a expressão, ela retornaria o tamanho de um !sizeof(a ? (bool)true : (bool)false) int

Isso ocorre devido à promoção implícita de tipo por meio das conversões aritméticas usuais.

Lundin
fonte
1
C ++ não garante realmente sizeof(bool)==1.
Aschepler #
1
@aschepler Não, mas o mundo real fora do padrão C ++ garante isso. Cite um compilador onde não é 1.
Lundin
Oi. Eu acho que essa resposta seria melhor sem a primeira parte. A segunda parte responde à pergunta. O resto, embora interessante, é apenas barulho.
LCA
@YSC Isso foi originalmente marcado com C e C ++, portanto era necessária uma comparação entre os diferentes tipos de bools e o histórico por trás deles. Duvido que eu teria escrito a primeira parte se não fosse a tag C ++. No entanto, deve-se entender por que sizeof (bool) é 1, mas sizeof (false) é 4 em C.
Lundin
21

Resposta rápida:

  • sizeof(a ? true : false)é avaliado como 4porque truee falseé definido <stdbool.h>como 1e 0respectivamente, para que a expressão se expanda para sizeof(a ? 1 : 0)uma expressão inteira com o tipo int, que ocupa 4 bytes na sua plataforma. Pelo mesmo motivo, você sizeof(true)também avaliaria 4em seu sistema.

Observe no entanto que:

  • sizeof(a ? a : a)também avalia 4porque o operador ternário executa as promoções de números inteiros em seu segundo e terceiro operandos, se forem expressões de número inteiro. O mesmo, naturalmente, acontece por sizeof(a ? true : false)e sizeof(a ? (bool)true : (bool)false), mas lançando a expressão inteira como boolse comporta como esperado: sizeof((bool)(a ? true : false)) -> 1.

  • Também note que os operadores de comparação avaliar a valores booleanos 1ou 0, mas tem inttipo: sizeof(a == a) -> 4.

Os únicos operadores que mantêm a natureza booleana aseriam:

  • o operador de vírgula: ambos sizeof(a, a)e sizeof(true, a)avaliar 1em tempo de compilação.

  • os operadores de atribuição: ambos sizeof(a = a)e sizeof(a = true)têm um valor de 1.

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

Finalmente, todas as opções acima se aplicam apenas a C: C ++ possui semânticas diferentes em relação ao booltipo, valores booleanos truee falseoperadores de comparação e operador ternário: todas essas sizeof()expressões são avaliadas 1em C ++.

chqrlie
fonte
2
Boa resposta que realmente indica que realmente não importa que tipo truee que falsesão, porque os ?:operandos seriam promovidos a números inteiros de intqualquer maneira. Assim sizeof(a ? (uint8_t)true : (uint8_t)false), também produzirá 4 como resultado.
Lundin
Esta resposta cobre o principal ponto importante, o valor que é promovido paraint
Chinni
1

Aqui está um trecho do qual é incluído na fonte

#ifndef __cplusplus

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

#else /* __cplusplus */

Existem macros truee falsesão declaradas como 1 e 0, respectivamente.

no entanto, nesse caso, o tipo é o tipo das constantes literais. 0 e 1 são constantes inteiras que se encaixam em um int, portanto, seu tipo é int.

e sizeof(int)no seu caso é 4.

você__
fonte
-3

Não há tipo de dados booleano em C; expressões lógicas são avaliadas para valores inteiros 1quando true, caso contrário 0.

Expressões condicionais como if, for, while, ou c ? a : besperar um inteiro, se o número for diferente de zero é considerado truecom exceção de alguns casos especiais, aqui está uma função de soma recursivo em que o ternário-operador irá avaliar trueaté que nalcance 0.

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

Também pode ser usado para NULLverificar um ponteiro, aqui está uma função recursiva que imprime o conteúdo de uma lista vinculada vinculada.

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