C Precedência
Quanto é 2 + 3 * 4? Se você respondeu 14, já conhece a ideia desta aula: alguns operadores são calculados antes de outros. Na escola isso se chama "ordem das operações"; em programação, precedência. O C tem mais de 40 operadores, e todos têm um lugar nessa fila. Nesta aula você vai ver a tabela completa, aprender a usar parênteses para mandar na ordem e conhecer as armadilhas em que até programadores experientes caem.
- O que é precedência (quem vai primeiro) e associatividade (empate: da esquerda ou da direita?).
- A tabela completa dos operadores do C, do mais "forte" ao mais "fraco".
- Como os parênteses resolvem tudo, e quando usá-los mesmo sem precisar.
- Os erros clássicos: a média
n1 + n2 / 2, o cast(double) (soma / qtd),n & 1 == 0,1 << 2 + 1e*p++. - Por que precedência não é a ordem em que as coisas são calculadas.
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 fila do banco com prioridade
Num banco, há uma fila só, mas algumas pessoas têm prioridade: são atendidas primeiro, não importa onde estejam. Entre duas pessoas com a mesma prioridade, vale a ordem de chegada.
Uma expressão em C funciona assim:
- Precedência é a prioridade: em
2 + 3 * 4, o*tem prioridade maior que o+, então3 * 4é agrupado primeiro. - Associatividade é o desempate entre operadores do mesmo nível: em
10 - 4 - 3, os dois-têm a mesma prioridade, e o C agrupa da esquerda para a direita:(10 - 4) - 3. - Parênteses são o "fura-fila" oficial: o que está dentro deles é agrupado primeiro, sempre.
1. Contas: como na escola
#include <stdio.h>
int main(void) {
printf("%d\n", 2 + 3 * 4);
printf("%d\n", (2 + 3) * 4);
printf("%d\n", 10 - 4 - 3);
printf("%d\n", 100 / 10 / 2);
printf("%d\n", 7 + 10 % 3);
printf("%d\n", -2 * -3);
return 0;
}
$ gcc -std=c23 -Wall -Wextra conta.c -o conta $ ./conta 14 « 2 + (3 * 4): a multiplicação vai primeiro 20 « (2 + 3) * 4: os parênteses mandam 3 « (10 - 4) - 3: da esquerda para a direita 5 « (100 / 10) / 2, e não 100 / (10 / 2), que daria 20 8 « 7 + (10 % 3): o resto (%) tem a prioridade do * e do / 6 « (-2) * (-3): o sinal de menos "grudado" vai antes de tudo
*,/e%- Mesmo nível, acima do
+e do-. Como na escola, com o%(resto da divisão; aula Operadores Aritméticos) junto da multiplicação. - esquerda para a direita
- Quase todos os operadores de dois lados se agrupam assim. Com
+e*tanto faz, mas com-e/faz diferença. -2- O
-na frente de um número só (o menos unário, "de um operando") tem prioridade maior que qualquer conta de dois lados.
Experimente: antes de rodar, calcule de cabeça o resultado de cada linha. Depois rode e confira. Mude os parênteses de lugar e veja o que muda.
#include <stdio.h>
int main(void) {
printf("%d\n", 1 + 2 * 3 + 4);
printf("%d\n", (1 + 2) * (3 + 4));
printf("%d\n", 20 / 2 * 5);
printf("%d\n", 20 / (2 * 5));
return 0;
}
2. Dois erros de conta muito comuns
A média que sai errada
#include <stdio.h>
int main(void) {
int n1 = 8;
int n2 = 6;
printf("Errado: %d\n", n1 + n2 / 2);
printf("Certo: %d\n", (n1 + n2) / 2);
int soma = 15;
int qtd = 2;
printf("%.1f\n", (double) soma / qtd);
printf("%.1f\n", (double) (soma / qtd));
return 0;
}
$ gcc -std=c23 -Wall -Wextra media.c -o media $ ./media Errado: 11 « 8 + (6 / 2) = 8 + 3 Certo: 7 « (8 + 6) / 2 = 14 / 2 7.5 « ((double) 15) / 2 = 15.0 / 2 7.0 « (double) (15 / 2) = (double) 7: a vírgula já tinha sido perdida
O primeiro erro é de matemática pura: sem parênteses, só o n2 é dividido. O gcc não avisa nada, porque n1 + n2 / 2 é uma conta válida; só você sabe que queria a média.
O segundo é do C: o cast (double) (a conversão de tipo; aula Conversão de Tipos) é um operador de um operando só e tem prioridade alta, como o menos unário. Ele pega só o que vem logo depois:
(double) soma / qtdconvertesomapara 15.0 e então divide: 15.0 / 2 é uma divisão com vírgula, 7.5.(double) (soma / qtd)primeiro divide os inteiros (15 / 2 = 7, a parte depois da vírgula é jogada fora) e só depois converte o 7 em 7.0. Tarde demais.
Missão: a média das três notas da Ana (7, 8 e 9) é 8, mas o programa mostra 18. Coloque os parênteses no lugar certo:
Média: 8
#include <stdio.h>
int main(void) {
int n1 = 7;
int n2 = 8;
int n3 = 9;
int media = n1 + n2 + n3 / 3;
printf("Média: %d\n", media);
return 0;
}
#include <stdio.h>
int main(void) {
int n1 = 7;
int n2 = 8;
int n3 = 9;
int media = (n1 + n2 + n3) / 3;
printf("Média: %d\n", media);
return 0;
}
3. A tabela completa
Esta é a tabela de precedência do C23, do nível mais alto (agrupado primeiro) ao mais baixo. Você não precisa decorá-la: guarde a ideia geral e consulte quando tiver dúvida. Os operadores que você ainda não conhece têm aula própria.
| Nível | Operadores | O que são | Associatividade |
|---|---|---|---|
| 1 | x++ x-- f() v[i] s.campo p->campo (tipo){…} | pós-incremento (++ e --), chamada de função, índice de array, campo de struct, literal composto | esquerda → direita |
| 2 | ++x --x +x -x ! ~ (tipo) *p &x sizeof alignof | operadores de um operando: pré-incremento, sinal, não lógico, não de bits, cast, conteúdo e endereço (ponteiros), tamanho | direita → esquerda |
| 3 | * / % | multiplicação, divisão, resto | esquerda → direita |
| 4 | + - | soma, subtração | esquerda → direita |
| 5 | << >> | deslocamento de bits (Operadores de Bits) | esquerda → direita |
| 6 | < <= > >= | comparações de ordem (Comparação) | esquerda → direita |
| 7 | == != | igual, diferente | esquerda → direita |
| 8 | & | E de bits | esquerda → direita |
| 9 | ^ | OU exclusivo de bits | esquerda → direita |
| 10 | | | OU de bits | esquerda → direita |
| 11 | && | E lógico (Lógicos) | esquerda → direita |
| 12 | || | OU lógico | esquerda → direita |
| 13 | ? : | operador ternário (Ternário) | direita → esquerda |
| 14 | = += -= *= /= %= <<= >>= &= ^= |= | atribuições (Atribuição) | direita → esquerda |
| 15 | , | operador vírgula | esquerda → direita |
Um resumo para guardar de cabeça, de cima para baixo: "de um lado só" → contas (* / antes de + -) → comparações → lógicos (&& antes de ||) → ternário → atribuição. Os operadores de bits (&, ^, |) ficam num lugar estranho, abaixo das comparações, e é daí que vêm as armadilhas da seção 5.
Sobre o nível 2: alignof virou palavra-chave no C23 C23; antes era _Alignof (C11). A vírgula do nível 15 é um operador raro; a vírgula que separa os argumentos do printf é outra coisa, só um separador.
4. Os que agrupam da direita para a esquerda
Três grupos da tabela se agrupam da direita para a esquerda: os de um operando, o ternário e as atribuições. Isso permite escrever:
#include <stdio.h>
int main(void) {
int a, b, c;
a = b = c = 7;
printf("%d %d %d\n", a, b, c);
int x = 2;
x *= 3 + 1;
printf("%d\n", x);
return 0;
}
$ gcc -std=c23 -Wall -Wextra atribs.c -o atribs $ ./atribs 7 7 7 « a = (b = (c = 7)): o 7 "escorre" da direita para a esquerda 8 « x *= (3 + 1): a atribuição é a última, então x = 2 * 4
- a = b = c = 7;
- Agrupado como
a = (b = (c = 7)). Primeirocrecebe 7; o valor dessa atribuição (7) vai parab; e o debvai paraa. Útil para zerar várias variáveis de uma vez. - x *= 3 + 1;
- As atribuições estão quase no fim da tabela: todo o lado direito é calculado antes. Então é
x = x * (3 + 1), e não(x * 3) + 1. - -!x, !-x…
- Com os de um operando, "da direita para a esquerda" quer dizer: o que está mais perto do valor vai primeiro. Em
-!x, primeiro o!, depois o-.
5. Armadilhas: operadores de bits e comparações
Este programa quer saber se n é par. Um número é par quando o último bit dele é 0, e n & 1 pega esse último bit (aula Operadores de Bits; aqui basta saber que n & 1 dá 0 para números pares e 1 para ímpares):
#include <stdio.h>
int main(void) {
int n = 4;
printf("%d\n", n & 1 == 0);
printf("%d\n", (n & 1) == 0);
printf("%d\n", 1 << 2 + 1);
return 0;
}
$ gcc -std=c23 -Wall -Wextra par.c -o par
par.c: In function ‘main’:
par.c:5:22: warning: suggest parentheses around comparison in operand of ‘&’ [-Wparentheses]
5 | printf("%d\n", n & 1 == 0);
| ^
par.c:7:22: warning: suggest parentheses around ‘+’ inside ‘<<’ [-Wparentheses]
7 | printf("%d\n", 1 << 2 + 1);
| ^~
$ ./par
0 « "4 é par?" respondeu NÃO
1 « com parênteses: sim
8 « 1 << (2 + 1) = 1 << 3, e não (1 << 2) + 1 = 5
- Na tabela, o
==(nível 7) está acima do&(nível 8). Entãon & 1 == 0é agrupado comon & (1 == 0). 1 == 0dá 0. Sobran & 0, que é sempre 0, para qualquern.- Com
(n & 1) == 0, o último bit de 4 (que é 0) é comparado com 0: sim, 1.
Essa posição do & abaixo do == é considerada um erro histórico do C, mantido até hoje para não quebrar programas antigos. O aviso diz "sugiro parênteses em volta da comparação usada no &". O Clang do editor explica melhor: & has lower precedence than ==; == will be evaluated first ("& tem precedência menor que ==; o == será calculado primeiro").
Na linha 7, o deslocamento << (nível 5) fica abaixo do + (nível 4), e muita gente espera o contrário. O gcc avisa: "sugiro parênteses em volta do + dentro do <<". Os dois avisos vêm do -Wparentheses, ligado pelo -Wall.
6. Prévia: *p++
Esta armadilha só faz sentido de verdade depois da aula Ponteiros e Arrays, mas ela é tão famosa que vale ver agora. Um ponteiro p é uma variável que guarda a posição de outra (ele "aponta" para ela); *p quer dizer "o valor que está onde p aponta". Aqui, p começa apontando para o primeiro número da lista v:
#include <stdio.h>
int main(void) {
int v[3] = {10, 20, 30};
int *p = v;
printf("%d\n", *p++);
printf("%d\n", *p);
(*p)++;
printf("%d\n", *p);
printf("%d %d %d\n", v[0], v[1], v[2]);
return 0;
}
$ gcc -std=c23 -Wall -Wextra ponteiro.c -o ponteiro $ ./ponteiro 10 « *p++: mostrou o 10 e ANDOU o ponteiro para o próximo número 20 « agora p aponta para o 20 21 « (*p)++: somou 1 no NÚMERO apontado 10 21 30 « a lista mudou: o 20 virou 21
- O
++de depois (nível 1) é mais forte que o*(nível 2). Então*p++é*(p++): o++anda o ponteiro, e o*lê o valor da posição antiga (porque o pós-incremento entrega o valor antigo). - Para somar 1 no valor apontado, os parênteses são obrigatórios:
(*p)++.
O *p++ é muito usado de propósito, para percorrer listas e textos (aula Ponteiros e Strings). O erro é escrever *p++ querendo (*p)++, e aí nenhum compilador avisa.
7. Precedência não é ordem de cálculo
Precedência decide como a expressão é agrupada (quem é operando de quem). Ela não decide em que ordem as partes são calculadas. Em a() + b() * c(), a precedência garante que o resultado de b() é multiplicado pelo de c() antes da soma, mas o C não promete chamar a() primeiro: o compilador pode calcular as três partes na ordem que quiser.
Na maioria das vezes isso não importa. Importa quando as partes mudam alguma coisa, como em printf("%d %d\n", n++, n++). Na aula ++ e -- esse mesmo código mostrou 2 1 no gcc e 1 2 no Clang. As exceções, que têm ordem garantida (esquerda primeiro), são &&, || (o curto-circuito de Operadores Lógicos), ? : e o operador vírgula.
Formas diferentes: parênteses que não mudam nada
Parênteses "a mais" não deixam o programa mais lento nem mudam o resultado. Eles servem para quem lê. Compare:
| Sem parênteses extras | Com parênteses extras | Qual usar |
|---|---|---|
total = preco * qtd + frete; | total = (preco * qtd) + frete; | Tanto faz: * antes de + todo mundo sabe. |
ok = nota >= 6 && faltas < 10; | ok = (nota >= 6) && (faltas < 10); | Os dois são comuns; os parênteses ajudam quem está começando. |
a || b && c | a || (b && c) | Com parênteses: o gcc até avisa sem eles. |
n & 1 == 0 | (n & 1) == 0 | Com parênteses, sempre: sem eles a resposta é outra. |
x << 2 + 1 | (x << 2) + 1 ou x << (2 + 1) | Com parênteses, deixando claro qual você quer. |
A regra prática: se você teve de pensar para saber a ordem, ponha parênteses. E sempre compile com -Wall, que avisa nos casos mais perigosos.
Quando dá errado
warning: suggest parentheses around comparison in operand of ‘&’
O que significa: você escreveu algo como n & 1 == 0. O == é feito antes do &. Também aparece com | e ^. No Clang: & has lower precedence than ==; == will be evaluated first.
Como consertar: (n & 1) == 0.
warning: suggest parentheses around ‘+’ inside ‘<<’
O que significa: em 1 << 2 + 1, a soma é feita antes do deslocamento. No Clang: operator '<<' has lower precedence than '+'; '+' will be evaluated first [-Wshift-op-parentheses].
Como consertar: parênteses em volta do que você quer primeiro.
warning: suggest parentheses around ‘&&’ within ‘||’
O que significa: mistura de && e || sem parênteses. O && vai primeiro (veja Operadores Lógicos).
Como consertar: escreva os parênteses do jeito que você quer.
O que significa: provavelmente faltaram parênteses numa conta: n1 + n2 / 2 no lugar de (n1 + n2) / 2, f - 32 * 5 / 9 no lugar de (f - 32) * 5 / 9, ou um cast no lugar errado, (double) (a / b). Nenhum compilador sabe o que você queria calcular.
Como consertar: leia a conta em voz alta e confira com a tabela. Teste com números fáceis, que você consegue calcular de cabeça.
Referência rápida
| Expressão | O C lê como | Cuidado |
|---|---|---|
a + b * c | a + (b * c) | Como na escola. |
a - b - c | (a - b) - c | Esquerda para a direita. |
(double) a / b | ((double) a) / b | O cast pega só o vizinho. |
a = b = c | a = (b = c) | Direita para a esquerda. |
x *= a + b | x = x * (a + b) | Atribuição por último. |
a < b == c < d | (a < b) == (c < d) | < antes de ==. |
a || b && c | a || (b && c) | && antes de ||. |
!x == y | (!x) == y | Escreva x != y. |
n & 1 == 0 | n & (1 == 0) | Sempre (n & 1) == 0. |
1 << a + b | 1 << (a + b) | Contas antes do deslocamento. |
*p++ | *(p++) | Anda o ponteiro; para mudar o valor, (*p)++. |
Exercícios
1. De cabeça
Quanto vale 10 - 2 * 3?
2. Da esquerda para a direita
Quanto vale 24 / 4 / 2?
3. O cast no lugar certo
Complete para a divisão ter vírgula (o resultado deve ser 2.5 com a = 5 e b = 2):
4. Com int n = 6;, quanto vale n & 1 == 0?
5. Depois de int a, b; a = b = 3 + 2;, quanto valem a e b?
6. O que *p++ faz?
Missão final: a temperatura
Missão: para converter Fahrenheit em Celsius, a fórmula é: subtrair 32, depois multiplicar por 5 e dividir por 9. O programa da Ana diz que a água ferve a 194.2 °C. Conserte a conta com parênteses para a saída ficar assim:
212.0 °F = 100.0 °C
#include <stdio.h>
int main(void) {
double f = 212.0;
double c = f - 32 * 5 / 9.0;
printf("%.1f °F = %.1f °C\n", f, c);
return 0;
}
#include <stdio.h>
int main(void) {
double f = 212.0;
double c = (f - 32) * 5 / 9.0;
printf("%.1f °F = %.1f °C\n", f, c);
return 0;
}
Repare no 9.0: com 9 o resultado seria o mesmo aqui, porque f já é double e contamina a conta toda. Troque f por 32.0 e por 98.6 para conferir outros valores (0 °C e 37 °C).
Resumo
- Precedência: quem é agrupado primeiro (
*antes de+). Associatividade: o desempate no mesmo nível (quase sempre da esquerda para a direita). - Ordem geral: de um operando (
! - ++ (tipo) *p) →* / %→+ -→<< >>→ comparações →& ^ |→&&→||→?:→ atribuições → vírgula. - Da direita para a esquerda: os de um operando, o
?:e as atribuições (a = b = c = 0). - Parênteses mandam em tudo e não custam nada. Use sempre que precisou pensar.
- Armadilhas:
n1 + n2 / 2,(double) (a / b),n & 1 == 0,1 << 2 + 1,*p++×(*p)++. O-Wallavisa as de bits. - Precedência não define a ordem de cálculo das partes: só
&&,||,?:e a vírgula garantem a esquerda primeiro.