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ívelOperadoresNomeAssociatividadeExemplo
1a++ 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 C99esquerda → direitap->notas[2] = (p->notas)[2]
2++a --a
+a -a
! ~
(tipo)
*p &x
sizeof alignof
pré-incremento e pré-decremento, sinal, "não" lógico, "não" de bits, conversão (cast), conteúdo, endereço, tamanho, alinhamento C23direita → esquerda*p++ = *(p++); -x * 2 = (-x) * 2
3* / %multiplicação, divisão, restoesquerda → direita2 + 3 * 4 = 14
4+ -soma, subtraçãoesquerda → direita10 - 3 - 2 = 5
5<< >>deslocamento de bitsesquerda → direita1 << 2 + 1 = 1 << 3 = 8 (!)
6< <= > >=comparação de ordemesquerda → direitaa + 1 < b * 2 = (a + 1) < (b * 2)
7== !=igual, diferenteesquerda → direitaa < b == c < d = (a < b) == (c < d)
8&"e" de bitsesquerda → direitaflags & 4 == 4 = flags & (4 == 4) (!)
9^"ou exclusivo" de bitsesquerda → direitaa ^ b & c = a ^ (b & c)
10|"ou" de bitsesquerda → direitaa | b ^ c = a | (b ^ c)
11&&"e" lógicoesquerda → direitaa > 0 && a < 10
12||"ou" lógicoesquerda → direitaa || b && c = a || (b && c)
13? :ternário (condicional)direita → esquerdan < 0 ? -1 : n > 0 ? 1 : 0 = n < 0 ? -1 : (n > 0 ? 1 : 0)
14= += -= *= /= %=
<<= >>= &= ^= |=
atribuiçãodireita → esquerdaa = b = 0 = a = (b = 0)
15,vírgula (operador)esquerda → direitak = (a = 1, b = 2, a + b) → k = 3
Para decorar

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.

OperadorFazExemplo → resultadoCuidadoAula
+ - *soma, subtração, multiplicação17 + 5 → 22; 17 - 5 → 12; 17 * 5 → 85com int, pode estourar (veja)Aritméticos
/divisão17 / 5 → 3; 17 / 5.0 → 3.40; (double)a / b → 3.40entre inteiros, corta a parte decimal (em direção ao zero: -17 / 5 → -3); dividir inteiro por zero derruba o programaAritméticos
%resto da divisão (só inteiros)17 % 5 → 2; -17 % 5 → -2; 17 % -5 → 2o resto tem o sinal do primeiro número; para double, é o fmodAritméticos
-a, +atroca o sinal; mantém-a → -17-INT_MIN estouraAritméticos

Atribuição

OperadorÉ o mesmo queExemplo (começando com x = 10, em sequência) → xAula
=guarda o valor da direita na variável da esquerdax = 10 → 10Variáveis
+= -=x = x + 5, x = x - 3x += 5 → 15; x -= 3 → 12Atribuição
*= /= %=x = x * 2…x *= 2 → 24; x /= 5 → 4; x %= 3 → 1Atribuição
<<= >>=x = x << 4…x <<= 4 → 16; x >>= 1 → 8Bits
&= |= ^=x = x & 6…x &= 6 → 0; x |= 1 → 1; x ^= 3 → 2Má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 --

FormaFazExemplo (com i = 5) → resultadoAula
i++ (pós)soma 1 em i; a expressão vale o valor antigopos = i++ → pos = 5, i = 6++ e --
++i (pré)soma 1 em i; a expressão vale o valor novodepois, pre = ++i → pre = 7, i = 7++ e --
i--, --io 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

OperadorPerguntaExemplo (a = 17, b = 5) → resultadoAula
==igual?a == b → 0Comparação
!=diferente?a != b → 1Comparação
< >menor? maior?a < b → 0; a > b → 1Comparação
<= >=menor ou igual? maior ou igual?a <= 17 → 1; a >= 18 → 0Comparação

O resultado é um int: 1 (verdadeiro) ou 0 (falso). Para strings, use strcmp (veja); para double, uma tolerância.

Lógicos

OperadorVerdadeiro quandoExemplo → resultadoCurto-circuitoAula
&& ("e")os dois lados são verdadeiros (não zero)1 && 0 → 0se a esquerda é falsa, a direita nem é calculada: p != NULL && *p > 0 é seguroLógicos
|| ("ou")pelo menos um lado é verdadeiro1 || 0 → 1se a esquerda é verdadeira, a direita não é calculadaLógicos
! ("não")o valor é falso (zero)!0 → 1; !5 → 0—Lógicos

Bits

Com 12 = 0b1100 e 10 = 0b1010:

OperadorFaz, bit a bitExemplo → resultadoAula
& ("e")1 onde os dois têm 112 & 10 → 8 (0b1000)Bits
| ("ou")1 onde pelo menos um tem 112 | 10 → 14 (0b1110)Bits
^ ("ou exclusivo", XOR)1 onde só um tem 112 ^ 10 → 6 (0b0110)Bits
~ ("não")inverte todos os bits~12 → -13Bits
<<empurra os bits para a esquerda (× 2 a cada casa)1 << 4 → 16Má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;:

OperadorFazExemplo → resultadoAula
&xo endereço de xscanf("%d", &idade)Memória e Endereços
*po valor guardado no endereço p*p → 10Ponteiros
v[i]o item i (é o mesmo que *(v + i))p[2] → 30; *(p + 1) → 20Ponteiros e Arrays
p + n, p - qanda n itens (não bytes); quantos itens entre dois ponteiros&v[2] - p → 2Ponteiros e Arrays
s.campoum campo de uma structpt.x → 3Structs
p->campoum campo pela struct apontada (é o mesmo que (*p).campo)pp->y → 4Structs 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

OperadorFazExemplo → resultadoAula
cond ? x : yvale x se a condição é verdadeira, senão ya > b ? "a é maior" : "b é maior" → a é maiorTerná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 → 12sizeof
alignof(tipo) C23alinhamento (no C11, _Alignof)alignof(double) → 8Tipos e Limites
(tipo)xconverte (cast)(double)a / b → 3.40Conversão de Tipos
a, bcalcula 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) C23o tipo de x, para declarar outra variável igualtypeof(a) c = 9; → um intNovidades
_Generic(x, …) C11escolhe uma expressão conforme o tipo de xveja 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
Linhagcc 15Clang (editor)O que acontece e como consertar
flags & 4 == 4suggest 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 + 1suggest 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 < 1comparisons 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++ + 1operation 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 == 5logical 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 falseO ! é 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 && 1suggest 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.

Dividir por zero

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 (! ~ - * & sizeof cast) → * / % → + - → << >> → < <= > >= → == != → & → ^ → | → && → || → ?: → 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.