C Operador Ternário (? :)

Muitas decisões servem só para escolher um valor: "ano" ou "anos"? O maior de dois números? "Aprovada" ou "Reprovada"? Escrever um if/else de cinco linhas para isso é trabalhoso. O C tem um atalho: o operador ternário, condição ? valor1 : valor2. Nesta aula você vai usá-lo no lugar certo, dentro do printf e de atribuições, e aprender quando ele atrapalha mais do que ajuda.

Você vai aprender
  • A forma condição ? se_sim : se_não e como ler em voz alta.
  • A diferença entre o ternário (escolhe um valor) e o if (escolhe ações).
  • Como usar o ternário dentro do printf, por exemplo para "ano" / "anos".
  • Por que preco + frete ? 15 : 0 não faz o que parece, e quando os parênteses são obrigatórios.
  • Por que os dois valores precisam combinar, e o que acontece quando não combinam.
  • Por que não aninhar ternários demais.

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.


A ideia: a pergunta com duas respostas prontas

Pense na pergunta do garçom: "Está chovendo? Então leve o guarda-chuva; senão, o boné". Antes de ouvir a resposta, as duas opções já estão prontas, e a pergunta só escolhe uma.

O ternário é isso, numa linha:

condição ? valor_se_sim : valor_se_não

Leia em voz alta: "a condição é verdadeira? então este valor, senão aquele". O ? é a pergunta; o : separa as duas respostas. O nome "ternário" quer dizer "de três partes": é o único operador do C com três operandos (o + tem dois, o ! tem um).


1. A forma básica

#include <stdio.h>

int main(void) {
    int a = 7;
    int b = 12;
    int maior = a > b ? a : b;
    printf("O maior é %d\n", maior);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra maior.c -o maior
$ ./maior
O maior é 12
a > b
A condição, igual à de um if: 0 é falso, o resto é verdadeiro. Aqui, 7 > 12 dá 0.
?
"Então…". Separa a condição das respostas.
a
O valor escolhido se a condição for verdadeira.
:
"Senão…".
b
O valor escolhido se a condição for falsa. Como 7 > 12 é falso, o ternário inteiro vale b, 12.
int maior = …;
O resultado do ternário é um valor como outro qualquer: pode ser guardado numa variável, mostrado, usado numa conta.

Só um dos dois valores é calculado: o C calcula a condição primeiro e depois apenas o lado escolhido, como no curto-circuito do && (aula Operadores Lógicos). Então b != 0 ? a / b : 0 nunca divide por zero.

Ternário × if: valor × ação

A mesma escolha com if/else ocupa cinco linhas:

Com ifCom ternário
int maior;
if (a > b) {
    maior = a;
} else {
    maior = b;
}
int maior = a > b ? a : b;

A diferença de fundo: o if escolhe o que fazer (pode ter várias linhas em cada caminho); o ternário escolhe um valor. Quando cada caminho é "guardar um valor na mesma variável" ou "mostrar um texto ou outro", o ternário é mais curto e claro. Quando os caminhos fazem coisas diferentes, use if.


2. Dentro do printf

Lembra do "Faltam 1 anos" da aula if e else? Com o ternário, o plural cabe dentro do printf:

#include <stdio.h>

int main(void) {
    int faltam = 1;
    printf("Faltam %d ano%s.\n", faltam, faltam == 1 ? "" : "s");
    faltam = 3;
    printf("Faltam %d ano%s.\n", faltam, faltam == 1 ? "" : "s");
    int vidas = 0;
    printf("%s\n", vidas > 0 ? "Continue jogando" : "Fim de jogo");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra anos.c -o anos
$ ./anos
Faltam 1 ano.        « faltam == 1: o %s recebeu "" (texto vazio)
Faltam 3 anos.       « senão: o %s recebeu "s"
Fim de jogo
ano%s
O %s (aula Formatos do printf) é um buraco para um texto, colado logo depois de "ano".
faltam == 1 ? "" : "s"
O ternário escolhe o texto que vai no buraco: nada ("") ou um "s". As duas respostas são textos, e é isso que o %s espera.
a vírgula
O ternário inteiro é um argumento do printf. Não precisa de parênteses em volta, porque a vírgula que separa os argumentos não faz parte de nenhuma conta.

Missão: o carrinho da lojinha mostra "1 itens". Use o ternário dentro do printf para escrever "item" quando houver 1 e "itens" nos outros casos. A entrada preparada é 1:

Carrinho: 1 item
#include <stdio.h>

int main(void) {
    int itens = 0;
    if (scanf("%d", &itens) != 1) {
        return 1;
    }
    printf("Carrinho: %d itens\n", itens);
    return 0;
}
#include <stdio.h>

int main(void) {
    int itens = 0;
    if (scanf("%d", &itens) != 1) {
        return 1;
    }
    printf("Carrinho: %d %s\n", itens, itens == 1 ? "item" : "itens");
    return 0;
}

Depois de cumprir a missão, troque a entrada para 0, 2 e 5 e confira o plural.


3. Precedência: o ternário vem quase por último

Na tabela de Precedência, o ?: fica lá embaixo: abaixo de todas as contas, comparações e lógicos, e acima só das atribuições e da vírgula. Isso é bom em maior = a > b ? a : b (a comparação é feita antes, a atribuição depois), mas engana quando o ternário está no meio de uma conta:

#include <stdio.h>

int main(void) {
    int preco = 100;
    int tem_frete = 1;
    int total = preco + tem_frete ? 15 : 0;
    printf("Total: %d\n", total);
    int certo = preco + (tem_frete ? 15 : 0);
    printf("Total: %d\n", certo);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra prec.c -o prec
$ ./prec
Total: 15        « o preço sumiu!
Total: 115
  1. O + tem precedência maior que o ?:. Então o C leu (preco + tem_frete) ? 15 : 0.
  2. preco + tem_frete é 101, diferente de zero, então "verdadeiro".
  3. O ternário inteiro vale 15, e o total ficou 15.

Nem o gcc nem o Clang do editor avisaram nada. A regra: quando o ternário é só uma parte de uma conta, ponha-o entre parênteses: preco + (tem_frete ? 15 : 0).


4. Os dois lados precisam combinar

O ternário inteiro tem um tipo só, decidido na hora de compilar, antes de saber qual lado vai ser escolhido. Por isso as duas respostas precisam ser do mesmo "jeito".

Números de tipos diferentes: o C escolhe o "maior", sem avisar

#include <stdio.h>

int main(void) {
    int n = 3;
    printf("%d\n", n > 0 ? 5 : 2.5);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra tiposter.c -o tiposter
tiposter.c: In function ‘main’:
tiposter.c:5:14: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
    5 |     printf("%d\n", n > 0 ? 5 : 2.5);
      |             ~^     ~~~~~~~~~~~~~~~
      |              |               |
      |              int             double
      |             %f
$ ./tiposter
-869692968       « lixo (muda a cada execução)
$ ./tiposter
-617049208

Um lado é int (5) e o outro é double (2.5). O C converte os dois para o tipo mais "largo", double, e o ternário vale 5.0, mesmo escolhendo o lado do 5. Aí o %d, que espera um int, lê lixo. O aviso explica: "o formato %d espera um int, mas o argumento 2 é double", e até sugere %f. No editor do guia, o mesmo erro mostra 0 em vez de lixo: com o formato errado, cada sistema mostra uma coisa.

Conserto: deixe os dois lados do mesmo tipo (5.0 : 2.5) e use o formato certo (%.1f).

Texto de um lado, número do outro: erro

$ cat tiposter2.c
#include <stdio.h>

int main(void) {
    int n = 3;
    printf("%s\n", n > 0 ? "positivo" : 0);
    printf("%s\n", n > 0 ? "positivo" : 1);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra tiposter2.c -o tiposter2
tiposter2.c: In function ‘main’:
tiposter2.c:6:39: error: pointer/integer type mismatch in conditional expression [-Wint-conversion]
    6 |     printf("%s\n", n > 0 ? "positivo" : 1);
      |                                       ^

"Tipos ponteiro e inteiro não combinam na expressão condicional." Um texto entre aspas é, por baixo, um ponteiro (a posição do texto na memória; aula Ponteiros e Strings), e 1 é um inteiro. Desde o gcc 14, isso é erro; no Clang do editor é só um aviso ([-Wconditional-type-mismatch]), mas o programa está errado do mesmo jeito. Já a linha 5 passou: o 0 escrito assim é um caso especial, o ponteiro nulo (aula Ponteiro Nulo), e mandar um ponteiro nulo para o %s também é um erro, só que o compilador não percebe. Regra simples: texto com texto, número com número.


5. Ternário aninhado: evite

Como o "senão" de um ternário pode ser outro ternário, dá para escolher entre mais de duas opções:

#include <stdio.h>

int main(void) {
    double nota = 7.5;
    char conceito = nota >= 9 ? 'A' : nota >= 7 ? 'B' : nota >= 5 ? 'C' : 'D';
    printf("Conceito %c\n", conceito);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra aninhado.c -o aninhado
$ ./aninhado
Conceito B

Funciona: o ?: se agrupa da direita para a esquerda, então a linha é lida como nota >= 9 ? 'A' : (nota >= 7 ? 'B' : (nota >= 5 ? 'C' : 'D')), que é exatamente a cadeia de else if. Mas é difícil de ler, de conferir e de mudar. Muitos guias de estilo proíbem ternários aninhados. Para mais de duas opções, prefira else if ou switch. Se for mesmo usar, quebre em linhas:

char conceito = nota >= 9 ? 'A'
              : nota >= 7 ? 'B'
              : nota >= 5 ? 'C'
              :              'D';

6. Duas diferenças do C

O resultado não pode receber valor

Em C, o ternário entrega um valor, não uma variável. Então não dá para guardar nada "nele":

$ cat lvalter.c
#include <stdio.h>

int main(void) {
    int a = 7;
    int b = 12;
    (a > b ? a : b) = 0;
    printf("%d %d\n", a, b);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra lvalter.c -o lvalter
lvalter.c: In function ‘main’:
lvalter.c:6:21: error: lvalue required as left operand of assignment
    6 |     (a > b ? a : b) = 0;
      |                     ^

"É preciso um lvalue (algo que possa receber valor) do lado esquerdo do =." (Em C++ essa linha compila e zera o maior dos dois; em C, não.) Use um if: if (a > b) { a = 0; } else { b = 0; }.

O ?: do gcc (não é C padrão)

Você pode encontrar x ?: y, sem o valor do meio, em código feito para o gcc. Quer dizer "x, se x não for zero; senão y". É uma extensão do gcc (o Clang também aceita), não faz parte do padrão C. O gcc só reclama se você pedir o modo estrito, com -pedantic:

$ cat elvis.c
#include <stdio.h>

int main(void) {
    int pedidos = 0;
    printf("%d\n", pedidos ?: 1);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra elvis.c -o elvis
$ ./elvis
1                « pedidos é 0, então vale o 1
$ gcc -std=c23 -Wall -Wextra -pedantic elvis.c -o elvis
elvis.c: In function ‘main’:
elvis.c:5:29: warning: ISO C forbids omitting the middle term of a ‘?:’ expression [-Wpedantic]
    5 |     printf("%d\n", pedidos ?: 1);
      |                             ^

"O C da ISO proíbe omitir o termo do meio de uma expressão ?:." Em código que deve compilar em qualquer compilador, escreva por inteiro: pedidos ? pedidos : 1.


Formas diferentes + qual usar

SituaçãoMelhor formaExemplo
Escolher um entre dois valores para guardarTernárioint maior = a > b ? a : b;
Escolher um texto para mostrarTernário dentro do printfprintf("%s\n", ok ? "Sim" : "Não");
Ternário no meio de uma contaTernário entre parêntesestotal = preco + (frete ? 15 : 0);
Cada caminho faz coisas diferentes, ou mais de uma coisaif / elseif (ok) { salvar(); avisar(); } else { … }
Três ou mais opçõeselse if ou switchConceitos A, B, C, D.
Ternário só para "fazer coisas"Eviteok ? printf("sim") : printf("não"); compila, mas é um if disfarçado.

Quando dá errado

O resultado da conta ignora parte dela (sem aviso)

O que significa: você escreveu o ternário no meio de uma conta, como preco + frete ? 15 : 0. Tudo à esquerda do ? virou a condição.

Como consertar: parênteses em volta do ternário: preco + (frete ? 15 : 0).

warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’

O que significa: um lado do ternário é double, então o ternário inteiro é double, mesmo quando escolhe o lado inteiro.

Como consertar: deixe os dois lados do mesmo tipo e use o formato certo.

error: pointer/integer type mismatch in conditional expression

O que significa: um lado é texto e o outro é número. No Clang: warning: pointer/integer type mismatch in conditional expression ('char *' and 'int').

Como consertar: texto com texto (ok ? "1" : "0") ou número com número (ok ? 1 : 0).

error: lvalue required as left operand of assignment

O que significa: você tentou guardar um valor no resultado de um ternário. Em C isso não existe. No Clang: expression is not assignable.

Como consertar: use if/else.

error: expected ‘:’ before …
$ cat semdois.c
#include <stdio.h>

int main(void) {
    int a = 7, b = 12;
    int maior = a > b ? a;
    printf("%d\n", maior);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra semdois.c -o semdois
semdois.c: In function ‘main’:
semdois.c:5:26: error: expected ‘:’ before ‘;’ token
    5 |     int maior = a > b ? a;
      |                          ^
      |                          :

O que significa: o ternário precisa das duas respostas. Faltou o : valor_se_não.

Como consertar: complete: a > b ? a : b. Se não existe "senão", você quer um if, não um ternário.


Referência rápida

ExpressãoVale
c ? x : yx se c for diferente de 0; senão y. Só o lado escolhido é calculado.
a > b ? a : bO maior dos dois.
n == 1 ? "" : "s"Plural num %s.
b != 0 ? a / b : 0Divisão protegida.
p + (c ? 15 : 0)Ternário dentro de conta: com parênteses.
c ? 5 : 2.5Tipo double (5.0 ou 2.5).
x ?: yExtensão do gcc; no C padrão: x ? x : y.

Exercícios

1. Complete o ternário

Complete para menor receber o menor dos dois números:

int menor = a < b a b;

2. Aprovada ou não

Complete a condição para mostrar "Aprovada" quando a nota for 6 ou mais:

printf("%s\n", ? "Aprovada" : "Reprovada");

3. Quanto vale x depois de int n = 0; int x = n ? 10 : 20;?

4. Com int preco = 50, frete = 1;, quanto vale preco + frete ? 10 : 0?

5. Quando é melhor usar if/else em vez do ternário?

Missão final: o maior de três

Missão: o programa lê três números (a entrada preparada é 4 9 2) e deve mostrar o maior. Use o operador ternário (dica: primeiro o maior entre a e b; depois compare esse resultado com c). Sem avisos:

Maior: 9
#include <stdio.h>

int main(void) {
    int a = 0, b = 0, c = 0;
    if (scanf("%d %d %d", &a, &b, &c) != 3) {
        return 1;
    }
    int maior = a;    // use o ternário

    printf("Maior: %d\n", maior);
    return 0;
}
#include <stdio.h>

int main(void) {
    int a = 0, b = 0, c = 0;
    if (scanf("%d %d %d", &a, &b, &c) != 3) {
        return 1;
    }
    int maior = a > b ? a : b;
    maior = maior > c ? maior : c;

    printf("Maior: %d\n", maior);
    return 0;
}

Teste com 9 4 2, 2 4 9 e 5 5 5: o maior tem de aparecer em qualquer posição.


Resumo

  • condição ? x : y vale x se a condição for verdadeira, senão y. Só o lado escolhido é calculado.
  • Use para escolher um valor (guardar, mostrar). Para escolher ações, use if.
  • Dentro do printf, resolve plurais e textos alternativos: n == 1 ? "" : "s".
  • O ?: tem precedência baixa: dentro de uma conta, ponha parênteses (preco + (frete ? 15 : 0)). Nenhum compilador avisa.
  • Os dois lados precisam combinar: int com double vira double; texto com número é erro no gcc.
  • Evite ternários aninhados; para três ou mais opções, else if ou switch.
  • Em C, o resultado não recebe valor (lvalue required). x ?: y é extensão do gcc.