C Operadores
Esta é a página de consulta rápida dos operadores do C: os símbolos que fazem contas, comparam, combinam condições, mexem em bits e em ponteiros. Primeiro vem a tabela completa de precedência (quem é calculado primeiro); depois, uma tabela por grupo, com o exemplo e a saída real; no fim, as armadilhas. Para aprender com calma, comece pela aula Operadores Aritméticos e siga até Precedência.
Todas as saídas foram produzidas de verdade: no gcc 15.2 da máquina da Ana (pasta ~/c/ref, comando gcc -std=c23 -Wall -Wextra) e no editor do guia (Clang em WebAssembly). Os operadores dão o mesmo resultado nos dois; só as mensagens de aviso mudam.
Atalhos: Precedência · Aritméticos · Atribuição · ++ e -- · Comparação · Lógicos · Bits · Ponteiros e membros · Outros · Armadilhas · Experimente
Tabela de precedência e associatividade
A precedência diz qual operador "pega" seus valores primeiro, como na matemática: em 2 + 3 * 4, o * vem antes, e o resultado é 14. A associatividade decide entre operadores da mesma linha: da esquerda para a direita (10 - 3 - 2 = (10 - 3) - 2 = 5) ou da direita para a esquerda (a = b = 0 = a = (b = 0)).
Na tabela, a linha 1 é a mais forte (calculada primeiro) e a 15, a mais fraca. Na dúvida, use parênteses: eles sempre ganham, e deixam o código mais claro.
| Nível | Operadores | Nome | Associatividade | Exemplo |
|---|---|---|---|---|
| 1 | a++ a--f() v[i]s.campo p->campo(tipo){…} | pós-incremento e pós-decremento, chamada de função, índice, membro, membro por ponteiro, literal composto C99 | esquerda → direita | p->notas[2] = (p->notas)[2] |
| 2 | ++a --a+a -a! ~(tipo)*p &xsizeof alignof | pré-incremento e pré-decremento, sinal, "não" lógico, "não" de bits, conversão (cast), conteúdo, endereço, tamanho, alinhamento C23 | direita → esquerda | *p++ = *(p++); -x * 2 = (-x) * 2 |
| 3 | * / % | multiplicação, divisão, resto | esquerda → direita | 2 + 3 * 4 = 14 |
| 4 | + - | soma, subtração | esquerda → direita | 10 - 3 - 2 = 5 |
| 5 | << >> | deslocamento de bits | esquerda → direita | 1 << 2 + 1 = 1 << 3 = 8 (!) |
| 6 | < <= > >= | comparação de ordem | esquerda → direita | a + 1 < b * 2 = (a + 1) < (b * 2) |
| 7 | == != | igual, diferente | esquerda → direita | a < b == c < d = (a < b) == (c < d) |
| 8 | & | "e" de bits | esquerda → direita | flags & 4 == 4 = flags & (4 == 4) (!) |
| 9 | ^ | "ou exclusivo" de bits | esquerda → direita | a ^ b & c = a ^ (b & c) |
| 10 | | | "ou" de bits | esquerda → direita | a | b ^ c = a | (b ^ c) |
| 11 | && | "e" lógico | esquerda → direita | a > 0 && a < 10 |
| 12 | || | "ou" lógico | esquerda → direita | a || b && c = a || (b && c) |
| 13 | ? : | ternário (condicional) | direita → esquerda | n < 0 ? -1 : n > 0 ? 1 : 0 = n < 0 ? -1 : (n > 0 ? 1 : 0) |
| 14 | = += -= *= /= %=<<= >>= &= ^= |= | atribuição | direita → esquerda | a = b = 0 = a = (b = 0) |
| 15 | , | vírgula (operador) | esquerda → direita | k = (a = 1, b = 2, a + b) → k = 3 |
De cima para baixo: unários (um valor só) → contas (* / %, depois + -) → deslocamento → comparações → bits (& ^ |) → lógicos (&&, depois ||) → ternário → atribuição → vírgula. Os operadores de bits ficam abaixo das comparações (é uma herança antiga do C): por isso, com &, | e ^, sempre use parênteses.
Precedência não é ordem de execução. Ela diz como a expressão é agrupada, não qual parte roda primeiro. Em f() + g() * h(), a multiplicação é agrupada antes, mas o C não diz qual das três funções é chamada primeiro. Só &&, ||, ?: e a vírgula garantem a ordem (da esquerda para a direita).
Aritméticos
Todas as tabelas daqui até Outros foram medidas com um só programa, ops.c (com int a = 17, b = 5;). Ele está no fim da seção, com a saída completa.
| Operador | Faz | Exemplo → resultado | Cuidado | Aula |
|---|---|---|---|---|
+ - * | soma, subtração, multiplicação | 17 + 5 → 22; 17 - 5 → 12; 17 * 5 → 85 | com int, pode estourar (veja) | Aritméticos |
/ | divisão | 17 / 5 → 3; 17 / 5.0 → 3.40; (double)a / b → 3.40 | entre inteiros, corta a parte decimal (em direção ao zero: -17 / 5 → -3); dividir inteiro por zero derruba o programa | Aritméticos |
% | resto da divisão (só inteiros) | 17 % 5 → 2; -17 % 5 → -2; 17 % -5 → 2 | o resto tem o sinal do primeiro número; para double, é o fmod | Aritméticos |
-a, +a | troca o sinal; mantém | -a → -17 | -INT_MIN estoura | Aritméticos |
Atribuição
| Operador | É o mesmo que | Exemplo (começando com x = 10, em sequência) → x | Aula |
|---|---|---|---|
= | guarda o valor da direita na variável da esquerda | x = 10 → 10 | Variáveis |
+= -= | x = x + 5, x = x - 3 | x += 5 → 15; x -= 3 → 12 | Atribuição |
*= /= %= | x = x * 2… | x *= 2 → 24; x /= 5 → 4; x %= 3 → 1 | Atribuição |
<<= >>= | x = x << 4… | x <<= 4 → 16; x >>= 1 → 8 | Bits |
&= |= ^= | x = x & 6… | x &= 6 → 0; x |= 1 → 1; x ^= 3 → 2 | Máscaras e Flags |
Toda atribuição também é um valor (o valor guardado). É por isso que a = b = 0 funciona, e é por isso que if (idade = 18) compila (veja Armadilhas).
++ e --
| Forma | Faz | Exemplo (com i = 5) → resultado | Aula |
|---|---|---|---|
i++ (pós) | soma 1 em i; a expressão vale o valor antigo | pos = i++ → pos = 5, i = 6 | ++ e -- |
++i (pré) | soma 1 em i; a expressão vale o valor novo | depois, pre = ++i → pre = 7, i = 7 | ++ e -- |
i--, --i | o mesmo, subtraindo 1 | — | ++ e -- |
Sozinhos numa linha (i++; ou ++i;), os dois fazem a mesma coisa. Nunca use a mesma variável duas vezes numa expressão que a muda: i = i++ + 1 é comportamento indefinido (veja Armadilhas).
Comparação
| Operador | Pergunta | Exemplo (a = 17, b = 5) → resultado | Aula |
|---|---|---|---|
== | igual? | a == b → 0 | Comparação |
!= | diferente? | a != b → 1 | Comparação |
< > | menor? maior? | a < b → 0; a > b → 1 | Comparação |
<= >= | menor ou igual? maior ou igual? | a <= 17 → 1; a >= 18 → 0 | Comparação |
O resultado é um int: 1 (verdadeiro) ou 0 (falso). Para strings, use strcmp (veja); para double, uma tolerância.
Lógicos
| Operador | Verdadeiro quando | Exemplo → resultado | Curto-circuito | Aula |
|---|---|---|---|---|
&& ("e") | os dois lados são verdadeiros (não zero) | 1 && 0 → 0 | se a esquerda é falsa, a direita nem é calculada: p != NULL && *p > 0 é seguro | Lógicos |
|| ("ou") | pelo menos um lado é verdadeiro | 1 || 0 → 1 | se a esquerda é verdadeira, a direita não é calculada | Lógicos |
! ("não") | o valor é falso (zero) | !0 → 1; !5 → 0 | — | Lógicos |
Bits
Com 12 = 0b1100 e 10 = 0b1010:
| Operador | Faz, bit a bit | Exemplo → resultado | Aula |
|---|---|---|---|
& ("e") | 1 onde os dois têm 1 | 12 & 10 → 8 (0b1000) | Bits |
| ("ou") | 1 onde pelo menos um tem 1 | 12 | 10 → 14 (0b1110) | Bits |
^ ("ou exclusivo", XOR) | 1 onde só um tem 1 | 12 ^ 10 → 6 (0b0110) | Bits |
~ ("não") | inverte todos os bits | ~12 → -13 | Bits |
<< | empurra os bits para a esquerda (× 2 a cada casa) | 1 << 4 → 16 | Máscaras e Flags |
>> | empurra para a direita (÷ 2 a cada casa) | 200 >> 3 → 25; -16 >> 2 → -4 (nos dois; com negativo, depende do compilador) | Máscaras e Flags |
Use unsigned com operadores de bits. Com int, 1 << 31 é comportamento indefinido (deu -2147483648 nos dois, sem aviso); com 1u << 31, é 2147483648. Deslocar por um número de casas maior ou igual ao tamanho do tipo também é indefinido: 1 << n com n = 32 deu 1 nos dois, e não 0.
Ponteiros, arrays e membros
Com int v[3] = {10, 20, 30}; int *p = v; e struct ponto { int x, y; } pt = {3, 4}, *pp = &pt;:
| Operador | Faz | Exemplo → resultado | Aula |
|---|---|---|---|
&x | o endereço de x | scanf("%d", &idade) | Memória e Endereços |
*p | o valor guardado no endereço p | *p → 10 | Ponteiros |
v[i] | o item i (é o mesmo que *(v + i)) | p[2] → 30; *(p + 1) → 20 | Ponteiros e Arrays |
p + n, p - q | anda n itens (não bytes); quantos itens entre dois ponteiros | &v[2] - p → 2 | Ponteiros e Arrays |
s.campo | um campo de uma struct | pt.x → 3 | Structs |
p->campo | um campo pela struct apontada (é o mesmo que (*p).campo) | pp->y → 4 | Structs e Ponteiros |
f(…) | chama a função (também por um ponteiro para função) | qsort(v, 3, sizeof v[0], comparar) | Ponteiros para Funções |
Outros operadores
| Operador | Faz | Exemplo → resultado | Aula |
|---|---|---|---|
cond ? x : y | vale x se a condição é verdadeira, senão y | a > b ? "a é maior" : "b é maior" → a é maior | Ternário |
sizeof x, sizeof(tipo) | tamanho em bytes (é calculado na compilação; não roda a expressão) | sizeof a → 4; sizeof(double) → 8; sizeof v → 12 | sizeof |
alignof(tipo) C23 | alinhamento (no C11, _Alignof) | alignof(double) → 8 | Tipos e Limites |
(tipo)x | converte (cast) | (double)a / b → 3.40 | Conversão de Tipos |
a, b | calcula a, joga fora, e vale b | (a = 1, b = 2, a + b) → 3. O uso comum: for (i = 0, j = n - 1; i < j; i++, j--) | for |
typeof(x) C23 | o tipo de x, para declarar outra variável igual | typeof(a) c = 9; → um int | Novidades |
_Generic(x, …) C11 | escolhe uma expressão conforme o tipo de x | veja em Tipos e Limites | _Generic |
Vírgula como operador e vírgula que separa argumentos são coisas diferentes: em printf("%d %d", a, b), as vírgulas só separam.
O programa das tabelas
#include <stdio.h>
int main(void) {
int a = 17, b = 5;
printf("aritméticos: %d %d %d %d %d\n", a + b, a - b, a * b, a / b, a % b);
printf("negativos: %d %d %d %d\n", -17 / 5, -17 % 5, 17 / -5, 17 % -5);
printf("com double: %.2f %.2f\n", 17 / 5.0, (double)a / b);
printf("unários: %d %d\n", -a, +a);
int x = 10;
x += 5; printf("x += 5 -> %d\n", x);
x -= 3; printf("x -= 3 -> %d\n", x);
x *= 2; printf("x *= 2 -> %d\n", x);
x /= 5; printf("x /= 5 -> %d\n", x);
x %= 3; printf("x %%= 3 -> %d\n", x);
x <<= 4; printf("x <<= 4 -> %d\n", x);
x >>= 1; printf("x >>= 1 -> %d\n", x);
x &= 6; printf("x &= 6 -> %d\n", x);
x |= 1; printf("x |= 1 -> %d\n", x);
x ^= 3; printf("x ^= 3 -> %d\n", x);
int i = 5;
int pos = i++;
printf("i++: valor %d, i agora %d\n", pos, i);
int pre = ++i;
printf("++i: valor %d, i agora %d\n", pre, i);
printf("comparação: %d %d %d %d %d %d\n", a == b, a != b, a < b, a > b, a <= 17, a >= 18);
printf("lógicos: %d %d %d %d\n", 1 && 0, 1 || 0, !0, !5);
printf("bits: %d %d %d %d %d %d\n", 12 & 10, 12 | 10, 12 ^ 10, ~12, 1 << 4, 200 >> 3);
printf("ternário: %s\n", a > b ? "a é maior" : "b é maior");
int v[3] = {10, 20, 30};
int *p = v;
printf("ponteiros: %d %d %d %d\n", *p, p[2], *(p + 1), (int)(&v[2] - p));
struct ponto { int x, y; } pt = {3, 4}, *pp = &pt;
printf("membros: %d %d\n", pt.x, pp->y);
printf("sizeof: %zu %zu %zu\n", sizeof a, sizeof(double), sizeof v);
printf("alignof(double): %zu\n", alignof(double));
int k = (a = 1, b = 2, a + b);
printf("vírgula: %d\n", k);
typeof(a) c = 9;
printf("typeof: %d\n", c);
return 0;
}
$ gcc -std=c23 -Wall -Wextra ops.c -o ops $ ./ops aritméticos: 22 12 85 3 2 negativos: -3 -2 -3 2 com double: 3.40 3.40 unários: -17 17 x += 5 -> 15 x -= 3 -> 12 x *= 2 -> 24 x /= 5 -> 4 x %= 3 -> 1 x <<= 4 -> 16 x >>= 1 -> 8 x &= 6 -> 0 x |= 1 -> 1 x ^= 3 -> 2 i++: valor 5, i agora 6 ++i: valor 7, i agora 7 comparação: 0 1 0 1 1 0 lógicos: 0 1 1 0 bits: 8 14 6 -13 16 25 ternário: a é maior ponteiros: 10 30 20 2 membros: 3 4 sizeof: 4 8 12 alignof(double): 8 vírgula: 3 typeof: 9
A saída do editor é idêntica, linha por linha.
Armadilhas
Este programa compila (no gcc) e roda, mas quase nenhuma linha faz o que parece. O -Wall avisa de todas:
#include <stdio.h>
int main(void) {
int flags = 6;
if (flags & 4 == 4) printf("bit 2 ligado\n");
int x = 1 << 2 + 1;
printf("1 << 2 + 1 = %d\n", x);
int idade = 15;
if (idade = 18) printf("maior de idade (idade = %d)\n", idade);
int a = 2, b = 3;
if (a < b < 1) printf("a < b < 1 é verdade?!\n");
int i = 1;
i = i++ + 1;
printf("i = %d\n", i);
if (!a == 5) printf("nunca\n");
printf("%d\n", a && b || 0 && 1);
return 0;
}
$ gcc -std=c23 -Wall -Wextra armadilhas.c -o armadilhas
armadilhas.c: In function ‘main’:
armadilhas.c:5:15: warning: suggest parentheses around comparison in operand of ‘&’ [-Wparentheses]
5 | if (flags & 4 == 4) printf("bit 2 ligado\n");
| ^
armadilhas.c:6:15: warning: suggest parentheses around ‘+’ inside ‘<<’ [-Wparentheses]
6 | int x = 1 << 2 + 1;
| ^~
armadilhas.c:9:9: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
9 | if (idade = 18) printf("maior de idade (idade = %d)\n", idade);
| ^~~~~
armadilhas.c:11:11: warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
11 | if (a < b < 1) printf("a < b < 1 é verdade?!\n");
| ~~^~~
armadilhas.c:13:7: warning: operation on ‘i’ may be undefined [-Wsequence-point]
13 | i = i++ + 1;
| ~~^~~~~~~~~
armadilhas.c:15:12: warning: comparison of constant ‘5’ with boolean expression is always false [-Wbool-compare]
15 | if (!a == 5) printf("nunca\n");
| ^~
armadilhas.c:15:12: warning: logical not is only applied to the left hand side of comparison [-Wlogical-not-parentheses]
armadilhas.c:15:9: note: add parentheses around left hand side expression to silence this warning
15 | if (!a == 5) printf("nunca\n");
| ^~
| ( )
armadilhas.c:16:22: warning: suggest parentheses around ‘&&’ within ‘||’ [-Wparentheses]
16 | printf("%d\n", a && b || 0 && 1);
| ~~^~~~
$ ./armadilhas
1 << 2 + 1 = 8
maior de idade (idade = 18)
i = 2
1
| Linha | gcc 15 | Clang (editor) | O que acontece e como consertar |
|---|---|---|---|
flags & 4 == 4 | suggest parentheses around comparison in operand of ‘&’ [-Wparentheses] | & has lower precedence than ==; == will be evaluated first [-Wparentheses] | O == é mais forte: vira flags & (4 == 4) = 6 & 1 = 0, e o bit ligado não foi visto. Escreva (flags & 4) == 4 ou só if (flags & 4). Aula: Máscaras e Flags. |
1 << 2 + 1 | suggest parentheses around ‘+’ inside ‘<<’ [-Wparentheses] | operator '<<' has lower precedence than '+'; '+' will be evaluated first [-Wshift-op-parentheses] | Deu 8 (1 << 3), não 5. Escreva (1 << 2) + 1. |
if (idade = 18) | suggest parentheses around assignment used as truth value [-Wparentheses] | using the result of an assignment as a condition without parentheses [-Wparentheses] | Um = só guarda 18 em idade e a condição vale 18 (verdadeiro): a Ana, com 15, virou "maior de idade". Comparar é ==. Aula: Comparação. |
a < b < 1 | comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses] (aviso) | erro: chained comparison 'X < Y < Z' does not behave the same as a mathematical expression [-Wparentheses] | Vira (a < b) < 1 = 1 < 1 = falso. No gcc compila; no editor, o Clang 22 recusa. Escreva a < b && b < 1. Aula: Lógicos. |
i = i++ + 1 | operation on ‘i’ may be undefined [-Wsequence-point] | multiple unsequenced modifications to 'i' [-Wunsequenced] | Muda i duas vezes sem ordem definida: comportamento indefinido (deu 2 nos dois, mas podia ser outro valor). Escreva i += 2; ou separe em duas linhas. Aula: Comportamento Indefinido. |
!a == 5 | logical not is only applied to the left hand side of comparison [-Wlogical-not-parentheses] e comparison of constant ‘5’ with boolean expression is always false [-Wbool-compare] | logical not is only applied to the left hand side of this comparison [-Wlogical-not-parentheses] e result of comparison of constant 5 with boolean expression is always false | O ! é mais forte: vira (!a) == 5, e !a só vale 0 ou 1. Para "a não é 5", escreva a != 5 ou !(a == 5). |
a && b || 0 && 1 | suggest parentheses around ‘&&’ within ‘||’ [-Wparentheses] | '&&' within '||' [-Wlogical-op-parentheses] | Está certo (&& vem antes de ||), mas é fácil de ler errado. Os parênteses deixam claro: (a && b) || (0 && 1). |
*p++ não soma 1 no valor
Com int *p = v;, a linha *p++; não aumenta v[0]: o ++ (nível 1) pega o p antes do * (nível 2), então ela anda o ponteiro para o próximo item (depois dela, v[0] continua 10 e *p vale 20). O compilador desconfia: value computed is not used [-Wunused-value] no gcc, expression result unused [-Wunused-value] no Clang. Como consertar: para aumentar o valor apontado, (*p)++. Aula: Ponteiros e Arrays.
7 / zero com int zero = 0; não dá aviso, e o programa morre nos dois: no gcc, Exceção de ponto flutuante (imagem do núcleo gravada); no editor, a mesma mensagem e [código de saída: 136]. (O nome é antigo: vale também para divisão de inteiros.) Como consertar: teste antes, if (divisor != 0). Com double, dividir por zero não derruba: 7 / 0.0 mostra inf (infinito) e 0 / 0.0 mostra -nan ("não é um número"), nos dois. Aula: Aritméticos.
Experimente
Experimente: antes de clicar em Rodar, escreva num papel o que cada linha vai mostrar, usando a tabela de precedência. Depois, ponha parênteses e veja o que muda.
#include <stdio.h>
int main(void) {
int a = 6, b = 3, c = 2;
printf("%d\n", a + b * c);
printf("%d\n", a / b * c);
printf("%d\n", a - b - c);
printf("%d\n", a % 4 + b);
printf("%d\n", -a / 4);
printf("%d\n", a > b && b > c);
printf("%d\n", a == 6 || b++ == 99);
printf("b depois: %d\n", b);
printf("%d\n", a > b ? a : b);
return 0;
}
Missão: as permissões da Ana estão em flags = 6 (0b110): o bit 2 (que vale 4) está ligado, mas o programa não o vê, e o compilador avisa. Ponha os parênteses no lugar certo para sair bit 2 ligado, sem nenhum aviso.
#include <stdio.h>
int main(void) {
int flags = 6;
if (flags & 4 == 4) {
printf("bit 2 ligado\n");
}
return 0;
}
#include <stdio.h>
int main(void) {
int flags = 6;
if ((flags & 4) == 4) {
printf("bit 2 ligado\n");
}
return 0;
}
Resumo
- Precedência, do mais forte ao mais fraco: pós-fixos (
a++ f() v[i] . ->) → unários (! ~ - * & sizeofcast) →* / %→+ -→<< >>→< <= > >=→== !=→&→^→|→&&→||→?:→ atribuições →,. - Da direita para a esquerda: os unários, o
?:e as atribuições. O resto, da esquerda para a direita. - Divisão de inteiros corta a parte decimal;
%tem o sinal do primeiro número. &&e||param assim que sabem a resposta (curto-circuito).- Com
&,|,^e<<, use parênteses.=guarda,==compara. Não mude a mesma variável duas vezes numa expressão.