O padrão C indica explicitamente o valor verdade como 0 ou 1?

86

Sabemos que quaisquer números que não sejam iguais a 0são vistos como trueem C, então podemos escrever:

int a = 16;

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

No entanto, gostaria de saber se verdadeiro / falso são definidos como 1/ 0em C, então tentei o código abaixo:

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

O padrão C indica explicitamente os valores verdadeiros de verdadeiro e falso como 1e 0respectivamente?

Kevin dong
fonte
3
Acho que esta pergunta SO é relevante
Imran Ali
3
Tento executar isso gcccom -std=c89e produz o mesmo resultado.
Kevin Dong de
1
@Blackhole, de 15 a 0.
Arturo Torres Sánchez
6
Quase tolo, mas mais de uma década antes do SO / SE: c-faq.com/bool/bool2.html .
dave_thompson_085
1
Que falso é zero é canônico, porém verdadeiro é geralmente considerado como "diferente de zero". Sendo os programadores o que são, todos nós usamos 1 como "não zero" por nossos vários motivos. Você é encorajado a não confiar que um verdadeiro seja exatamente 1. Embora (0 == 0) seja um em seu exemplo, algo como (12 == 12) poderia facilmente ter o valor 12; também verdade".
Engenheiro de

Respostas:

96

O padrão C indica explicitamente os valores verdade de truee falseas 0e 1respectivamente?

O padrão C define truee falsecomo macros nas stdbool.hquais se expandem para 1e0 respectivamente.

C11-§7.18:

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

Para os operadores ==e!= , o padrão diz

C11-§6.5.9 / 3:

Os operadores ==(igual a) e !=(não igual a) são análogos aos operadores relacionais, exceto por sua precedência inferior. 108) Cada um dos operadores retorna 1se a relação especificada for verdadeira e 0se for falsa. O resultado tem tipo int. Para qualquer par de operandos, exatamente uma das relações é verdadeira.

haccks
fonte
20
Parece-me que a questão é sobre 0 == 0e 0 != 0etc., não sobre o valor das macros.
MM de
9
Eu acho que quando ele escreveu, trueele quis dizer "o valor de uma comparação verdadeira" ou algo assim, não a macrotrue
MM de
1
@KevinDong; Sim, o rascunho do C99 tem um parágrafo semelhante.
haccks de
1
@haccks: você pode dizer sobre isso sem hesitar "idêntico". Acabei de ler sua citação porque estava com preguiça de procurar por parágrafo, já que me refiro a c99 quando necessário. E consegui encontrá-lo apenas pesquisando via ctrl+ f;)
dhein
2
@MooingDuck: NaN == NaNé falso e NaN != NaNé verdadeiro. Não há problema com essa afirmação.
kennytm
51

Não é indicado explicitamente em C11. Todas as operações em nível de linguagem retornarão 1 como verdadeiro (e aceitará qualquer diferente de zero incluindo NaN como verdadeiro).

  • Se você se preocupa com _Bool, então true deve ser 1 porque o padrão exige que ele contenha apenas 0 e 1. (§6.2.5 / 2).
  • Também na <stdbool.h>macro se trueexpande para 1(§7.18 / 3)
  • ==, !=, <, >, <=E >=retornar 0 ou 1 (§6.5.8 / 6, §6.5.9 / 3).
  • !, &&E ||retornar 0 ou 1 (§6.5.3.3 / 5, §6.5.13 / 3, §6.5.14 / 3)
  • defined expande para 0 ou 1 (§6.10.1 / 1)

Mas todas as funções de biblioteca padrão, por exemplo, islowerapenas diga "diferente de zero" para verdade (por exemplo, §7.4.1 / 1, §7.17.5.1 / 3, §7.30.2.1 / 1, §7.30.2.2.1 / 4).


§6.2.5 / 2 : Um objeto declarado como tipo _Boolé grande o suficiente para armazenar os valores 0 e 1.

§6.5.5.3 / 5 : O resultado do operador lógico de negação !é 0 se o valor de seu operando for diferente de 0, 1 se o valor de seu operando for igual a 0. ...

§6.5.8 / 6 : Cada um dos operadores <(menor que), >(maior que), <=(menor ou igual a) e >=(maior ou igual a) deve render 1 se a relação especificada for verdadeira e 0 se for é falso. 107) ...

§6.5.9 / 3 : Os operadores ==(igual a) e !=(não igual a) são análogos aos operadores relacionais, exceto por sua precedência inferior. 108) Cada um dos operadores produz 1 se a relação especificada for verdadeira e 0 se for falso. …

§6.5.13 / 3 : O &&operador deve produzir 1 se ambos os operandos forem diferentes de 0; …

§6.5.14 / 3 : O ||operador deve produzir 1 se qualquer um de seus operandos for diferente de 0; …

§6.10.1 / 1 : ... pode conter expressões de operadores unários da forma - defined identifier- ou - defined ( identifier )- que avaliam como 1 se ...

§7.4.1 (funções de classificação de caracteres) / 1 : As funções nesta subseção retornam diferente de zero (verdadeiro) se e somente se ...

§7.18 / 3 : 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, ...

§7.17.5.1 / 3 : A atomic_is_lock_freefunção genérica retorna diferente de zero (verdadeiro) se e somente se as operações do objeto estão livres de bloqueio. …

§7.30.2.1 (Funções de classificação de caracteres amplos) / 1 : As funções nesta subseção retornam diferente de zero (verdadeiro) se e somente se ...

§7.30.2.2.1 / 4 : A iswctypefunção retorna diferente de zero (verdadeiro) se e somente se ...

Kennytm
fonte
23

Existem duas áreas do padrão que você precisa estar ciente ao lidar com valores booleanos (pelo qual quero dizer valores verdadeiro / falso em vez do C específico bool/_Bool tipo ) em C.

O primeiro tem a ver com o resultado de expressões e pode ser encontrado em várias partes de C11 6.5 Expressions(operadores relacionais e de igualdade, por exemplo). O ponto principal é que, sempre que um valor booleano é gerado por uma expressão, ele ...

... retorna 1 se a relação especificada for verdadeira e 0 se for falsa. O resultado tem o tipo int.

Portanto, sim, o resultado de qualquer expressão geradora de booleanos será um para verdadeiro ou zero para falso. Isso corresponde ao que você encontrará stdbool.honde as macros padrão truee falsesão definidas da mesma maneira.

Tenha em mente, entretanto, que, seguindo o princípio de robustez de "ser conservador no que você envia, liberal no que você aceita", a interpretação de inteiros no contexto booleano é um pouco mais relaxada.

Novamente, em várias partes do 6.5, você verá uma linguagem como:

O ||operador deve gerar 1 se qualquer um de seus operandos for diferente de 0; caso contrário, resulta em 0. O resultado tem o tipo int.

A partir disso (e de outras partes), é óbvio que zero é considerado falso e qualquer outro valor é verdadeiro.


Como um aparte, a linguagem que especifica qual valor é usado para a geração e interpretação booleana também aparece em C99 e C89, então eles já existem há algum tempo. Mesmo K&R (ANSI-C segunda edição e a primeira edição) especificou que, com segmentos de texto como:

Expressões relacionais como expressões i > jlógicas conectadas por &&e ||são definidas para ter valor 1se verdadeiro e 0falso.

Na parte de teste de if, while, for, etc, "verdadeiro" significa apenas "não-zero".

O &&operador ... retorna 1 se ambos os operandos forem comparados de forma diferente a zero, 0 caso contrário.

O ||operador ... retorna 1 se qualquer um dos seus operandos for diferente de zero e 0 caso contrário.

As macros stdbool.haparecem no C99 também, mas não no C89 ou K&R, pois o arquivo de cabeçalho não existia naquele ponto.

paxdiablo
fonte
2
notar que ||, ==, !=etc rendimento int, não um tipo booleano
MM
2
Eu voto esta questão para a correta. Para mim, a questão é também sobre os operadores relacionais e não sobre as macros.
ckruczek
"Na parte de teste de if, while, for, etc, 'verdadeiro' significa apenas 'não-zero'." Esta é a parte saliente da resposta e é, em minha opinião, uma escolha infeliz de Dennis Ritchie de muito tempo atrás. Qualquer pessoa que escreveu funções que retornam códigos de erro como o valor de retorno geralmente tem #define noErr 0e qualquer código de erro diferente de zero é um erro. E então o problema é a simplicidade e a beleza do if ( ready_to_do_something() ){do_something();} não funciona. Tem que ser if ( !not_ready_to_do_something() ){do_something();}"Existem muitas falsidades, mas apenas uma verdade." TRUE deve ser 0.
robert bristow-johnson
Por curiosidade, como os primeiros rascunhos das regras C especificaram o comportamento de "&&" e "||" no caso em que os operandos tinham valores diferentes de 0 ou 1? O texto que você cita diz "expressões lógicas" conectadas por && e ||, mas e se esses operadores conectarem outras coisas além de expressões lógicas?
supercat de
1
@sdenham, sim. Na primeira cópia de K&R que tenho (primeira edição, tiragem 14, uma tão inicial que menciona as características de hardware de quatro máquinas típicas, o PDP-11, Honeywell-6000, IBM-370 e Interdata-8/32), A.7.6/7/10/11(relacional / igualdade / lógico-e / lógico-ou) todos especificam que dá 0 ou 1 como resultado. Atualizei a resposta para incluir isso.
paxdiablo
10

Você está misturando muitas coisas diferentes: instruções de controle, operadores e tipos booleanos. Cada um tem suas próprias regras.

As declarações de controle funcionam como, por exemplo, a ifdeclaração C11 6.4.8.1:

Em ambas as formas, a primeira subinstrução é executada se a expressão for diferente de 0.

while, for etc. têm a mesma regra. Isso não tem nada a ver com "verdadeiro" ou "falso".

Quanto aos operadores que supostamente estão gerando um resultado booleano, eles estão gerando um intcom valor 1 ou 0. Por exemplo, os operadores de igualdade, C11 6.5.9:

Cada um dos operadores resulta em 1 se a relação especificada for verdadeira e 0 se for falsa

Tudo isso ocorre porque C não tinha um tipo booleano até o ano de 1999 e, mesmo quando tinha um, as regras acima não foram alteradas. Portanto, ao contrário da maioria das outras linguagens de programação em que instruções e operadores geram um tipo booleano (como C ++ e Java), eles apenas geram um int, com valor zero ou diferente de zero. Por exemplo,sizeof(1==1) fornecerá 4 em C, mas 1 em C ++.

O tipo booleano real em C é nomeado _Boole requer um compilador moderno. O cabeçalho stdbool.hmacros define bool, truee false, que se expandem para _Bool, 1e 0respectivamente (para compatibilidade com C ++).


No entanto, é considerada uma boa prática de programação tratar instruções de controle e operadores como se eles realmente exigissem / gerassem um tipo booleano. Certos padrões de codificação, como MISRA-C, recomendam essa prática. Isso é:

if(ptr == NULL)em vez de if(ptr).

if((data & mask) != 0)em vez de if(data & mask).

O objetivo desse estilo é aumentar a segurança de tipo com o auxílio de ferramentas de análise estática, que por sua vez reduzem os bugs. Indiscutivelmente, esse estilo só é significativo se você usar analisadores estáticos. Embora em alguns casos isso leve a um código mais legível e autodocumentado, por exemplo

if(c == '\0') 

Ótimo, a intenção é clara, o código é autodocumentado.

versus

if(c) 

Mau. Pode significar qualquer coisa, e temos que procurar o tipo de cpara entender o código. É um número inteiro, um ponteiro ou um caractere?

Lundin
fonte
1
sizeof(bool)é a implementação específica em C ++. Consulte stackoverflow.com/questions/4897844/is-sizeofbool-defined .
David Hammen
@DavidHammen Assim como sizeof (0 == 0) também é definido pela implementação. É apenas um exemplo.
Lundin
Achei que C realmente mudou as regras para os tipos booleanos. Outros tipos de tipos uintN (incluindo muitos tipos de "bit" de compiladores mais antigos) armazenam os bits N inferiores de um valor e ignoram quaisquer bits superiores, enquanto os novos tipos booleanos efetivamente "ou" juntos todos os bits.
supercat de
1
Deveria ser if(ptr != NULL), ou talvez if(!ptr)?
Mathieu K.
1
if(c == '\0')se presta ao erro de codificação particularmente comum para iniciantes if(c = '\0'), então eu o evito. Concordo, if(c)é ruim ... deveria ser, por exemplo,if(valveIsOpen)
aja
4

Eu programei em várias línguas. Eu vi true ser 1 ou -1 dependendo do idioma. A lógica por trás do verdadeiro ser 1 era que um bit era 0 ou 1. A lógica por trás do verdadeiro ser -1 era que o! operador era um complemento de um. Ele mudou todos os 1's para 0's e todos os 0's para 1's em um int. Portanto, para um int,! 0 = -1 e! (- 1) = 0. Isso me enganou o suficiente para que eu não comparasse algo como == verdadeiro, mas em vez disso, comparando-o com! = Falso. Dessa forma, meu estilo de programação funciona em todas as linguagens. Portanto, minha resposta é não se preocupar com isso, mas programe para que seu código funcione corretamente de qualquer maneira.

Russell Hankins
fonte
como pode ! mude todos os 0s para 1s e ainda produza 0 para! 5?
codeshot
@codeshot Não pode. Mas o que ele está enfatizando é que nem todas as linguagens tratam o operando de! como um booleano. Alguns mimos! como C's ~ - isto é, um complemento bit a bit. Nesse caso, determinar o valor resultante requer o conhecimento do tipo da variável em primeiro lugar, então! (Uint32_t) 5 seria 4.294.967.290. Mas! 0 ainda é 4.294.967.295 e 4.294.967.295 é verdadeiro.
Pegasus Epsilon
1

Essa resposta precisa ser examinada um pouco mais de perto.

A definição real em C ++ é que qualquer coisa diferente de 0 é tratada como verdadeira. Por que isso é relevante? Porque C ++ não sabe o que é um inteiro pela forma como pensamos sobre ele - nós criamos esse significado, tudo o que ele contém é o shell e as regras para o que isso significa. Ele sabe o que são os bits, o que constitui um número inteiro.

1 como um inteiro é representado vagamente em bits, digamos um int assinado de 8 bits como 0000 0001. Muitas vezes o que vemos visualmente é uma mentira, -1 é uma forma muito mais comum de representá-lo devido à natureza do sinal de 'inteiro'. Eu realmente não posso significar verdadeiro, por quê? Porque NÃO é a operação 1111 1110. Esse é um problema realmente importante para um booleano. Quando falamos sobre um booleano, ele é apenas 1 bit - é realmente simples, 0 é falso e 1 é verdadeiro. Todas as operações lógicas são triviais. É por isso que '-1' deve ser designado como 'verdadeiro' para inteiros (com sinal). 1111 1111 NOT'ed torna-se 0000 0000 --- a lógica se mantém e estamos bem. Ints não assinados são um pouco complicados e eram muito mais comumente usados ​​no passado - onde 1 significa verdadeiro porque é fácil sugerir a lógica de que '

Essa é a explicação. Eu digo que a resposta aceita aqui está errada - não há uma definição clara na definição C / C ++. Um booleano é um booleano, você pode tratar um número inteiro como um booleano, mas o fato de a saída ser um número inteiro não diz nada sobre a operação que está realmente sendo feita é bit a bit.

Tommy
fonte
4
A pergunta era sobre C, não C ++.
glglgl
0

Aconteceu por causa dos Operadores Relacionais em seu printf declaração.

Operador ==e operador!=

Uma vez que (0 == 0)é verdade, dá um valor1

ao passo que, (0 != 0)não é verdade, dá um valor 0.

SKD
fonte