C Operadores de Comparação

Um programa vive fazendo perguntas: "a nota é maior que 6?", "o estoque acabou?", "a senha está certa?". Para perguntar, o C tem seis operadores de comparação. Nesta aula você vai conhecer cada um, descobrir que a resposta do C é um número (1 para sim, 0 para não) e aprender as armadilhas clássicas: trocar == por =, comparar números com vírgula, escrever 0 <= nota <= 10 como na matemática e tentar comparar textos com ==.

Você vai aprender
  • Os seis operadores: ==, !=, <, >, <= e >=.
  • Por que a resposta é um int que vale 1 ou 0 (inclusive no C23, que já tem bool).
  • A diferença entre = (guardar) e == (comparar), e o aviso que o gcc dá quando você troca um pelo outro.
  • Como comparar caracteres e por que 'Z' < 'a' é verdade.
  • Por que 0.1 + 0.2 == 0.3 dá "não" e como comparar double com uma tolerância.
  • A armadilha de comparar int com unsigned, e por que 0 <= nota <= 10 não faz o que parece.
  • Por que textos (strings) não se comparam com ==.

Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa (cada opção é explicada em Compilar com gcc). Você pode repetir cada um no editor do guia, que compila C de verdade no navegador.


A ideia: a balança de dois pratos

Uma comparação é como uma balança antiga, de dois pratos. Você coloca um valor em cada prato e faz uma pergunta: "estão iguais?", "o da esquerda é mais leve?". A balança não muda os pesos; ela só responde, e a resposta é sempre sim ou não.

Em C, "sim" é o número 1 e "não" é o número 0. Toda comparação produz um desses dois números.

OperadorPerguntaExemploResposta
==É igual a?5 == 51 (sim)
!=É diferente de?5 != 50 (não)
<É menor que?3 < 51
>É maior que?3 > 50
<=É menor ou igual a?5 <= 51
>=É maior ou igual a?3 >= 50

Os sinais de dois caracteres são escritos grudados e nesta ordem: <= e >= (o = por último, como se fala: "menor ou igual"); != (o ! quer dizer "não": "não igual"). Com espaço no meio (< =) o gcc não entende. E a igualdade tem dois sinais de igual: um só, =, é atribuição, que muda a variável (veja Operadores de Atribuição e a seção 3 desta aula).


1. Comparar números e ver a resposta

#include <stdio.h>

int main(void) {
    int idade = 17;
    printf("%d\n", idade == 18);
    printf("%d\n", idade != 18);
    printf("%d\n", idade < 18);
    printf("%d\n", idade > 18);
    printf("%d\n", idade <= 17);
    printf("%d\n", idade >= 18);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra compara.c -o compara
$ ./compara
0        « 17 == 18? não
1        « 17 != 18? sim
1        « 17 < 18? sim
0        « 17 > 18? não
1        « 17 <= 17? sim (é igual, e "menor ou igual" aceita igual)
0        « 17 >= 18? não
idade == 18
A comparação. O C olha o valor guardado em idade (17), compara com 18 e produz a resposta: 0.
printf("%d\n", …)
Como a resposta é um número inteiro, ela é mostrada com %d, igual a qualquer int (veja Formatos do printf). Não precisa de parênteses em volta da comparação: a vírgula do printf separa os argumentos.
idade <= 17
"Menor ou igual": basta uma das duas coisas ser verdade. Como 17 é igual a 17, a resposta é 1.
nenhuma variável mudou
Depois das seis perguntas, idade continua valendo 17. Comparar só lê os valores.

Na prática, o lugar mais comum de uma comparação é dentro de um if ("se") ou de um while ("enquanto"): if (idade >= 18) { … }. Você vai usar isso em if e else e while. Aqui o foco é a pergunta em si.

Experimente: mude o valor de estoque (tente 0, 3 e 10) e veja as respostas mudarem. Depois acrescente a pergunta "o estoque é maior que 5?".

#include <stdio.h>

int main(void) {
    int estoque = 3;
    printf("Acabou? %d\n", estoque == 0);
    printf("Tem algum? %d\n", estoque != 0);
    printf("Está baixo (até 3)? %d\n", estoque <= 3);
    return 0;
}

2. A resposta é um int (até no C23)

Muitas linguagens têm um tipo só para "sim ou não", com os valores true (verdadeiro) e false (falso). O C ganhou esse tipo em 1999, com o nome _Bool, e no C23 ele virou bool, sem precisar de #include (aula Booleanos). Mesmo assim, por compatibilidade com programas antigos, as comparações continuam produzindo um int: o padrão C23 ainda diz que o resultado de ==, < e companhia "tem tipo int" e vale 1 ou 0. Dá para conferir com o sizeof, que mede o tamanho de um valor em bytes (aula Tamanho dos Tipos):

#include <stdio.h>

int main(void) {
    int idade = 20;
    int maior = idade >= 18;
    bool pode_votar = idade >= 16;
    printf("maior = %d\n", maior);
    printf("pode_votar = %d\n", pode_votar);
    printf("sizeof(idade >= 18) = %zu\n", sizeof(idade >= 18));
    printf("sizeof(int) = %zu\n", sizeof(int));
    printf("sizeof(bool) = %zu\n", sizeof(bool));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra tipo.c -o tipo
$ ./tipo
maior = 1
pode_votar = 1
sizeof(idade >= 18) = 4     « a resposta ocupa 4 bytes…
sizeof(int) = 4              « …o tamanho de um int
sizeof(bool) = 1             « um bool teria 1 byte
int maior = idade >= 18;
A resposta pode ser guardada numa variável, para usar depois. Primeiro o C calcula a comparação (lado direito), depois guarda o 1 ou o 0 em maior.
bool pode_votar = …
Também pode ir para um bool C23. O 1 vira true, que o printf mostra como 1 (o printf não tem formato que escreva "true").
%zu
O formato do resultado de sizeof (um size_t, um inteiro sem sinal).
Qual tipo usar para guardar a resposta?

No C23 (e no C99 com #include <stdbool.h>), prefira bool: o nome deixa claro que a variável é um "sim ou não". Em código antigo você vai ver muito int no lugar, e funciona do mesmo jeito.


3. = × ==: o erro mais famoso do C

= e == se parecem, mas fazem coisas opostas:

  • estoque = 5 guarda 5 em estoque (atribuição). Leia "estoque recebe 5".
  • estoque == 5 pergunta se estoque vale 5 (comparação). Leia "estoque é igual a 5?".

O problema é que, em C, os dois compilam no mesmo lugar. Veja o que acontece quando a Ana erra a tecla dentro de um if (o if roda o bloco entre chaves quando a pergunta dá "sim"; ele é o assunto de if e else):

#include <stdio.h>

int main(void) {
    int estoque = 0;
    if (estoque = 5) {
        printf("O estoque é 5\n");
    }
    printf("estoque agora vale %d\n", estoque);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra atrib.c -o atrib
atrib.c: In function ‘main’:
atrib.c:5:9: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
    5 |     if (estoque = 5) {
      |         ^~~~~~~
$ ./atrib
O estoque é 5            « mas o estoque era 0!
estoque agora vale 5     « e agora ele mudou
  1. estoque = 5 não pergunta nada: guarda 5 em estoque, apagando o 0.
  2. Em C, uma atribuição também tem um valor: o valor que foi guardado, 5.
  3. O if considera "sim" qualquer número diferente de zero. 5 é "sim", e o bloco roda.
  4. Resultado: a pergunta sempre dá "sim" e ainda estraga a variável.

O aviso diz: "sugiro parênteses em volta de uma atribuição usada como valor de verdade". Em outras palavras: "você usou = onde se espera uma pergunta; se foi de propósito, ponha parênteses extras; se não foi, você queria ==". O aviso vem com o -Wall, um dos motivos para nunca compilar sem ele. No editor do guia, o Clang é ainda mais direto e sugere o conserto:

$ clang -std=c23 -Wall -Wextra atrib.c -o atrib
atrib.c:5:17: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
    5 |     if (estoque = 5) {
      |         ~~~~~~~~^~~
atrib.c:5:17: note: place parentheses around the assignment to silence this warning
    5 |     if (estoque = 5) {
      |                 ^
      |         (          )
atrib.c:5:17: note: use '==' to turn this assignment into an equality comparison
    5 |     if (estoque = 5) {
      |                 ^
      |                 ==

A última nota diz: "use == para transformar esta atribuição numa comparação de igualdade".

Missão: no jogo da Ana, o jogador ainda tem 3 vidas, mas o programa diz "Fim de jogo!" e zera as vidas. Rode, leia o aviso e conserte a comparação. A saída correta é só:

Vidas: 3
#include <stdio.h>

int main(void) {
    int vidas = 3;
    if (vidas = 0) {
        printf("Fim de jogo!\n");
    }
    printf("Vidas: %d\n", vidas);
    return 0;
}
#include <stdio.h>

int main(void) {
    int vidas = 3;
    if (vidas == 0) {
        printf("Fim de jogo!\n");
    }
    printf("Vidas: %d\n", vidas);
    return 0;
}

Repare num detalhe: com vidas = 0, o valor da atribuição é 0, que é "não". O "Fim de jogo!" nem aparece, mas a variável é zerada mesmo assim. O erro pode estragar o programa nas duas direções.


4. Comparar caracteres

Um char é guardado como um número: o código do caractere na tabela ASCII (aula Caracteres). Por isso os seis operadores funcionam com caracteres entre aspas simples:

#include <stdio.h>

int main(void) {
    char letra = 'B';
    printf("%d\n", letra == 'B');
    printf("%d\n", letra == 'b');
    printf("%d\n", 'A' < 'B');
    printf("%d\n", 'Z' < 'a');
    return 0;
}
$ gcc -std=c23 -Wall -Wextra letras.c -o letras
$ ./letras
1        « 'B' == 'B'
0        « 'B' e 'b' são caracteres diferentes
1        « 'A' (65) vem antes de 'B' (66)
1        « 'Z' (90) vem antes de 'a' (97)!

A última linha surpreende: na tabela ASCII, todas as maiúsculas vêm antes de todas as minúsculas. 'A' a 'Z' são 65 a 90; 'a' a 'z' são 97 a 122. Os algarismos '0' a '9' são 48 a 57. Para perguntar "é uma letra?" ou "é maiúscula?" sem decorar números, o C tem funções prontas (Classificar Caracteres).


5. Comparar double: use uma tolerância

Com números inteiros, == é exato. Com double (números com vírgula), não dá para confiar nele. Veja uma conta que qualquer criança acerta:

#include <math.h>
#include <stdio.h>

int main(void) {
    double a = 0.1 + 0.2;
    double b = 0.3;
    printf("%d\n", a == b);
    printf("%.17f\n%.17f\n", a, b);
    printf("%d\n", fabs(a - b) < 1e-9);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra real.c -o real
$ ./real
0                        « 0.1 + 0.2 == 0.3? NÃO!
0.30000000000000004      « o que o 0.1 + 0.2 realmente guardou
0.29999999999999999      « o que o 0.3 realmente guardou
1                        « "estão a menos de 0.000000001 de distância?" sim

O double guarda os números em binário (base 2), e números como 0.1 e 0.3 não têm representação exata em binário, assim como 1/3 não tem em decimal (0,3333…). Cada um é guardado como o valor mais próximo possível, e os pequenos erros não se cancelam. O %.17f pediu 17 casas depois do ponto para mostrar essa diferença, que normalmente fica escondida. A aula float e double explica em detalhe.

A solução é comparar com uma tolerância: em vez de "são iguais?", pergunte "a distância entre eles é menor que um número bem pequeno?".

#include <math.h>
Traz as funções de matemática, entre elas a fabs (aula Matemática).
a - b
A diferença entre os dois. Pode ser negativa.
fabs( … )
floating-point absolute, "valor absoluto de número com vírgula": o número sem o sinal. Transforma −0.00000000000000005 em 0.00000000000000005, para a distância ser sempre positiva. (Não use abs, que é só para int: ela cortaria a parte depois da vírgula.)
< 1e-9
1e-9 é notação científica: 1 × 10−9 = 0.000000001. É a tolerância: se a distância é menor que isso, consideramos iguais.

Neste programa o gcc não pediu a opção -lm (a biblioteca de matemática), porque ele resolve a fabs sozinho. Com outras funções de math.h, como sqrt de uma variável, o Linux pode pedir -lm; a aula Matemática mostra quando.

Qual tolerância usar depende do problema: para dinheiro em reais, 0.001 já basta; para medidas científicas, pode ser bem menor.

Dinheiro sem vírgula

Uma alternativa muito usada em sistemas de lojas e bancos: guardar dinheiro em centavos, num inteiro. R$ 12,34 vira 1234. Aí o == volta a ser exato.


6. Textos (strings) não se comparam com ==

Em C, um texto é guardado num array de char, uma fileira de caracteres terminada por um '\0' (aula Strings). A Ana quis conferir uma senha do jeito "óbvio":

#include <stdio.h>
#include <string.h>

int main(void) {
    char senha[] = "abc123";
    printf("%d\n", senha == "abc123");
    printf("%d\n", strcmp(senha, "abc123") == 0);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra senha.c -o senha
senha.c: In function ‘main’:
senha.c:6:26: warning: comparison with string literal results in unspecified behavior [-Waddress]
    6 |     printf("%d\n", senha == "abc123");
      |                          ^~
$ ./senha
0        « com ==: "a senha está errada"?!
1        « com strcmp: está certa

O texto é o mesmo, mas o == respondeu "não". O motivo, por enquanto, é só este: o == com textos não olha as letras. Ele pergunta se os dois textos estão no mesmo lugar da memória (o mesmo endereço), e senha e "abc123" são duas cópias em lugares diferentes. Você vai entender endereços em Memória e Endereços e Ponteiros.

O aviso diz: "comparar com um texto literal tem comportamento não especificado". O Clang do editor é mais explícito: result of comparison against a string literal is unspecified (use an explicit string comparison function instead), "use uma função de comparação de strings". Com dois arrays (char a[] = "Ana", b[] = "Ana"; a == b), o gcc avisa comparison between two arrays [-Warray-compare] e a resposta também é 0.

A função certa é a strcmp (string compare, "comparar strings"), de <string.h>. Ela compara letra por letra e responde 0 quando os textos são iguais. Por isso a pergunta completa é strcmp(senha, "abc123") == 0. Ela tem os detalhes explicados em Funções de String.


7. A armadilha: números com e sem sinal

O C tem inteiros com sinal (int, que aceita negativos) e sem sinal (unsigned int, só 0 e positivos; veja Inteiros). Um número escrito com u no fim, como 1u, é unsigned. Quando você compara um com o outro, acontece isto:

#include <stdio.h>

int main(void) {
    int saldo = -1;
    unsigned int limite = 1;
    printf("%d\n", saldo < limite);
    printf("%d\n", -1 < 1u);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra sinal.c -o sinal
sinal.c: In function ‘main’:
sinal.c:6:26: warning: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Wsign-compare]
    6 |     printf("%d\n", saldo < limite);
      |                          ^
sinal.c:7:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Wsign-compare]
    7 |     printf("%d\n", -1 < 1u);
      |                       ^
$ ./sinal
0        « -1 < 1 é FALSO?!
0

Menos um é menor que um, qualquer criança sabe. Mas o C respondeu "não" duas vezes. O motivo:

  1. Para comparar, os dois lados precisam ter o mesmo tipo.
  2. Quando um lado é int e o outro é unsigned int, a regra do C converte o int para unsigned.
  3. Um unsigned não tem negativos: o −1 vira 4294967295, o maior unsigned int (veja Conversão de Tipos).
  4. 4294967295 < 1 é falso.

O aviso explica isso: "comparação de expressões inteiras com sinalização diferente: int e unsigned int". Atenção: em C, o -Wsign-compare vem com o -Wextra, e não com o -Wall. Compilando só com -Wall, o gcc fica quieto e o programa responde errado do mesmo jeito. Mais um motivo para usar as duas opções. (No editor do guia, o Clang só avisa na linha 6; na linha 7, com números escritos direto, ele fica quieto. O resultado é o mesmo 0.)

Isso aparece na prática quando você compara um int com um tamanho, como o resultado de sizeof ou de strlen, que são size_t (sem sinal). A solução é usar o mesmo tipo dos dois lados sempre que puder, ou garantir antes que o número com sinal não é negativo.


8. 0 <= nota <= 10 não é o que parece

Na matemática, escrevemos 0 ≤ nota ≤ 10 para dizer "a nota está entre 0 e 10". Em C, isso compila no gcc, mas significa outra coisa:

#include <stdio.h>

int main(void) {
    int nota = 15;
    printf("%d\n", 0 <= nota <= 10);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra faixa.c -o faixa
faixa.c: In function ‘main’:
faixa.c:5:30: warning: comparison of constant ‘10’ with boolean expression is always true [-Wbool-compare]
    5 |     printf("%d\n", 0 <= nota <= 10);
      |                              ^~
faixa.c:5:22: warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
    5 |     printf("%d\n", 0 <= nota <= 10);
      |                    ~~^~~~~~~
$ ./faixa
1        « 15 está entre 0 e 10?!
  1. O C faz uma comparação de cada vez, da esquerda para a direita: (0 <= nota) <= 10.
  2. 0 <= 15 dá 1.
  3. Sobra 1 <= 10, que é verdade. Como a primeira comparação só pode dar 0 ou 1, e os dois são menores que 10, a resposta é sempre 1.

Os dois avisos dizem exatamente isso: "comparar a constante 10 com uma expressão booleana (de sim ou não) dá sempre verdadeiro" e "comparações como X<=Y<=Z não têm o significado da matemática".

O Clang (o do laboratório, versão 21, e o do editor do guia) vai além e trata isso como erro: o programa nem compila.

$ clang -std=c23 -Wall -Wextra faixa.c -o faixa
faixa.c:5:30: error: chained comparison 'X <= Y <= Z' does not behave the same as a mathematical expression [-Wparentheses]
    5 |     printf("%d\n", 0 <= nota <= 10);
      |                              ^
faixa.c:5:30: warning: result of comparison of constant 10 with boolean expression is always true [-Wtautological-constant-out-of-range-compare]
    5 |     printf("%d\n", 0 <= nota <= 10);
      |                    ~~~~~~~~~ ^  ~~
1 warning and 1 error generated.

"Comparação encadeada X <= Y <= Z não se comporta como uma expressão matemática." O jeito certo é fazer duas perguntas e juntá-las com "e": 0 <= nota && nota <= 10. O && é o assunto da próxima aula, Operadores Lógicos.


Formas diferentes de fazer a mesma pergunta

Várias comparações podem ser escritas de mais de um jeito. Todas as linhas de cada grupo dão a mesma resposta:

PerguntaFormasQual usar
"x é pelo menos 6?"x >= 6 · x > 5 (só com inteiros) · 6 <= xx >= 6: fala exatamente o que você quer e funciona com double (com 5.5, x > 5 dá 1 e x >= 6 dá 0).
"x é diferente de 0?"x != 0 · !(x == 0)x != 0, mais curto e direto. O ! ("não") está em Operadores Lógicos.
"x vale 5?"x == 5 · 5 == xx == 5, como se fala.
O "estilo Yoda": 5 == x

Em código antigo você pode ver a constante do lado esquerdo: if (5 == x). O motivo é que, se a pessoa esquecer um =, 5 = x é erro de compilação (não dá para guardar nada no número 5), enquanto x = 5 compila. O apelido vem do mestre Yoda, que fala "invertido". Hoje o aviso -Wparentheses do -Wall pega o erro do = sem precisar inverter, então prefira o jeito natural, x == 5, e compile sempre com -Wall -Wextra.


Quando dá errado

warning: suggest parentheses around assignment used as truth value

O que significa: você escreveu = (guardar) onde deveria haver uma pergunta, quase sempre dentro de if ou while. A variável é alterada e a "pergunta" dá sempre a mesma resposta. No Clang: using the result of an assignment as a condition without parentheses.

Como consertar: troque por ==. Se você queria mesmo guardar e testar ao mesmo tempo (isso existe, em código mais avançado), escreva parênteses extras: if ((x = ler())).

warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning

O que significa: você escreveu uma faixa como na matemática. No gcc o programa roda e responde quase sempre 1; no Clang é error: chained comparison e nem compila.

Como consertar: 0 <= nota && nota <= 10 (aula Operadores Lógicos).

warning: comparison of integer expressions of different signedness

O que significa: você comparou um int com um unsigned (ou com um size_t). Se o int for negativo, a resposta sai errada. Só aparece com -Wextra.

Como consertar: use o mesmo tipo dos dois lados. Não ignore esse aviso.

warning: comparison with string literal results in unspecified behavior

O que significa: você comparou um texto com ==. Isso compara endereços, não letras, e quase sempre dá 0. Com dois arrays, o aviso é comparison between two arrays [-Warray-compare].

Como consertar: strcmp(a, b) == 0, com #include <string.h> (aula Funções de String).

Dois double "iguais" dão 0

O que significa: erros pequenos de arredondamento deixaram os valores diferentes na última casa. Nenhum aviso aparece com -Wall -Wextra.

Como consertar: fabs(a - b) < tolerancia, ou trabalhe com inteiros (centavos).

error: expected expression before ‘=’ token
$ gcc -std=c23 -Wall -Wextra espaco.c -o espaco
espaco.c: In function ‘main’:
espaco.c:5:27: error: expected expression before ‘=’ token
    5 |     printf("%d\n", nota < = 6);
      |                           ^

O que significa: há um espaço no meio do operador. Para o gcc, < = são dois operadores separados: um "menor que" seguido de um = que não faz sentido ali ("esperava uma expressão antes do =").

Como consertar: escreva grudado: <=, >=, ==, !=.


Referência

OperadorPerguntaResultadoCuidado
a == bIguais?int: 1 ou 0Não confunda com =. Não use com double (tolerância) nem com textos (strcmp).
a != bDiferentes?idemMesmos cuidados do ==.
a < b, a > bMenor? Maior?idemint × unsigned: resposta errada com negativos (-Wsign-compare, do -Wextra).
a <= b, a >= bMenor ou igual? Maior ou igual?idemO = vem por último, grudado.
fabs(a - b) < toldouble "iguais"?idem#include <math.h>; escolha a tolerância pelo problema.
strcmp(s, t) == 0Textos iguais?idem#include <string.h>; strcmp devolve 0 quando são iguais.
0 <= x && x <= 10Está na faixa?idemNunca 0 <= x <= 10.

Exercícios

1. Diferente

Complete para perguntar se senha é diferente de 1234:

int errada = senha 1234;

2. Maior ou igual

Complete para perguntar se nota é maior ou igual a 6:

bool aprovado = nota 6;

3. Igual de verdade

Complete o if para perguntar se vidas vale 0 (sem mudar a variável):

if (vidas 0) {

4. O que printf("%d\n", 7 > 3); mostra?

5. Com int x = -5; e unsigned int y = 3;, quanto vale x < y?

6. Qual é o jeito certo de perguntar se o texto nome é "Ana"?

7. Com int nota = 15;, quanto vale 0 <= nota <= 10 no gcc?

Missão final: o boletim

Missão: o programa lê uma nota (a entrada preparada é 7.5). Troque cada 0 marcado por uma comparação: "aprovado" é nota maior ou igual a 6; "nota máxima" é nota igual a 10. Sem avisos. Com 7.5, a saída deve ser:

Aprovado: 1
Nota máxima: 0
#include <stdio.h>

int main(void) {
    double nota = 0;
    if (scanf("%lf", &nota) != 1) {
        return 1;              // a leitura falhou: encerra
    }
    bool aprovado = 0;         // troque 0 pela comparação
    bool maxima = 0;           // troque 0 pela comparação
    printf("Aprovado: %d\n", aprovado);
    printf("Nota máxima: %d\n", maxima);
    return 0;
}
#include <stdio.h>

int main(void) {
    double nota = 0;
    if (scanf("%lf", &nota) != 1) {
        return 1;              // a leitura falhou: encerra
    }
    bool aprovado = nota >= 6;
    bool maxima = nota == 10;
    printf("Aprovado: %d\n", aprovado);
    printf("Nota máxima: %d\n", maxima);
    return 0;
}

Mude a entrada para 10 e para 5.9 e rode de novo. O if (scanf(…) != 1) do começo também é uma comparação: o scanf devolve quantos valores conseguiu ler, e se não leu 1 o programa para (aula Entrada com scanf). Comparar nota == 10 com um double aqui é seguro, porque 10 é guardado exatamente e a nota vem digitada, sem contas no meio.


Resumo

  • Os seis operadores: == (igual), != (diferente), <, >, <=, >=. Sinais duplos vão grudados, com o = por último.
  • A resposta é um int: 1 para sim, 0 para não. Isso continua valendo no C23. Mostre com %d; guarde num bool ou num int.
  • = guarda, == compara. Trocar os dois compila, muda a variável e gera suggest parentheses around assignment used as truth value (do -Wall).
  • char compara pelo código ASCII: maiúsculas (65–90) antes das minúsculas (97–122).
  • double: não use ==; use fabs(a - b) < tolerancia.
  • int × unsigned: o negativo vira um número enorme e -1 < 1u dá 0. O aviso -Wsign-compare vem do -Wextra.
  • 0 <= x <= 10 não funciona como na matemática (aviso no gcc, erro no Clang); use 0 <= x && x <= 10.
  • Textos não se comparam com == (compara endereços): use strcmp(a, b) == 0.