Menu

Operadores em C: aritméticos, lógicos e precedência

Todo operador de C que vale a pena conhecer - aritmético, de atribuição, de comparação, lógico e de incremento - mais a armadilha da divisão inteira, como o módulo se comporta com negativos e uma tabela de precedência em que você pode confiar.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Operadores são os verbos de C: eles pegam valores e produzem novos. Existem cerca de 40 deles, mas uma dúzia cobre a maior parte do código, e dois desses têm armadilhas afiadas o bastante para merecerem suas próprias seções.

Operadores aritméticos

(%% na string de formato imprime um sinal de porcentagem literal - % sozinho começaria um especificador de formato.)

Os cinco operadores aritméticos binários são + - * / %, mais o - unário para negação e o +, que essencialmente não faz nada.

A armadilha da divisão inteira

a / b com dois inteiros faz divisão inteira e joga fora o resto. Não há arredondamento - o resultado é truncado em direção a zero.

Esse último caso é o bug que sobrevive até a produção. Atribuir a um double não ajuda: a divisão total / count já está completa, e já é um int, antes de a atribuição acontecer. A correção é tornar um dos operandos de ponto flutuante dentro da expressão, normalmente com uma conversão de tipo.

Duas regras relacionadas: dividir por zero com inteiros é comportamento indefinido e tipicamente derruba o programa, e INT_MIN / -1 estoura.

O operador de módulo

% devolve o resto e funciona só com inteiros:

Testar x % 2 == 0 para saber se é par e usar % n para dar a volta em um índice de array são os dois usos do dia a dia.

Com números negativos, o % de C segue o sinal do dividendo:

Então x % 2 == 1 é um teste quebrado para números ímpares quando x pode ser negativo - -7 % 2 é -1. Use x % 2 != 0 no lugar.

Para restos de ponto flutuante, % é erro de compilação; use fmod() da math.h.

Atribuição e atribuição composta

= guarda um valor. As formas compostas combinam uma operação com o armazenamento:

x += 5 significa x = x + 5, mas avalia x uma única vez - o que importa quando o alvo é algo como arr[compute_index()].

A atribuição é ela mesma uma expressão que devolve o valor atribuído, e é por isso que a = b = c = 0 funciona (ela atribui da direita para a esquerda) e por que o erro de = no lugar de == compila silenciosamente.

Incremento e decremento

++ soma um, -- subtrai um. Cada um vem em uma forma prefixa e uma pós-fixa:

Como instrução isolada os dois são intercambiáveis, e i++ é a escolha convencional no cabeçalho de um for por nenhuma razão mais profunda do que a tradição.

O que você não pode fazer é usar uma variável mais de uma vez em uma expressão na qual ela também é modificada:

int i = 5;
int x = i++ + i++;      /* comportamento indefinido */
arr[i] = i++;           /* comportamento indefinido */
printf("%d %d", i++, i);/* comportamento indefinido */

C não define a ordem em que essas subexpressões são avaliadas, então o resultado não é meramente não especificado - o comportamento do programa inteiro é indefinido. Separe a instrução em duas e a ambiguidade desaparece.

Operadores de comparação

São seis, e todos produzem um int: 1 para verdadeiro e 0 para falso.

O resultado ser um int comum em vez de um tipo booleano dedicado é uma característica definidora de C - veja booleanos em C para o que decorre disso, incluindo o bug de = contra ==.

Dois erros de comparação são específicos de C. Encadear não funciona do jeito que a matemática sugere: if (1 < x < 10) é sempre verdadeiro, porque 1 < x avalia para 0 ou 1 e então isso é comparado com 10. Escreva if (x > 1 && x < 10). E comparar strings com == compara ponteiros, não texto - use strcmp da string.h.

Operadores lógicos

&& é E, || é OU, ! é NÃO. Os três tratam qualquer valor diferente de zero como verdadeiro.

Eles fazem curto-circuito: && para assim que um operando é falso, e || para assim que um é verdadeiro. O resto nunca é avaliado, o que não é só uma otimização - é uma garantia em que você pode confiar por segurança:

Troque a ordem desses dois testes e a chamada com NULL desreferencia um ponteiro nulo e derruba o programa. Proteja primeiro, use depois - a ordem em uma cadeia de && faz parte da lógica.

O operador condicional

O único operador de três operandos de C escolhe entre dois valores:

condicao ? valor_se_verdadeiro : valor_se_falso. É uma expressão, então cabe onde uma instrução if não cabe - dentro de um argumento de printf, em um inicializador. Mantenha-o curto; condicionais aninhados se tornam ilegíveis rapidamente.

Operadores bit a bit, brevemente

Estes trabalham sobre os bits individuais de um inteiro:

Eles são usados para flags, máscaras e registradores de hardware. Dois cuidados: & e | não são && e || (eles não fazem curto-circuito e operam bit a bit), e deslocar um valor com sinal ou deslocar por mais do que a largura do tipo é indefinido. Use tipos sem sinal para trabalho com bits.

Precedência

Quando uma expressão não tem parênteses, esta tabela decide o que se associa a quê. Maior precedência primeiro:

NívelOperadoresAssociatividade
1() [] -> . x++ x--do início ao fim
2! ~ ++x --x +x -x *p &x sizeof (tipo)do fim ao início
3* / %do início ao fim
4+ -do início ao fim
5<< >>do início ao fim
6< <= > >=do início ao fim
7== !=do início ao fim
8&do início ao fim
9^do início ao fim
10OU bit a bitdo início ao fim
11&&do início ao fim
12OU lógicodo início ao fim
13?:do fim ao início
14= += -= *= /= %= e companhiado fim ao início
15,do início ao fim

As consequências práticas:

a + b * c       /* significa a + (b * c) */
a < b == c < d  /* significa (a < b) == (c < d) - raramente o que voce queria */
x & 1 == 0      /* significa x & (1 == 0), ou seja x & 0 - um bug classico */
*p++            /* significa *(p++) - desreferencia p, depois avanca */

Essa terceira linha vale a pena memorizar: == se associa mais forte que &, então testes de máscara de bits precisam de parênteses - (x & 1) == 0.

Ninguém lembra dos quinze níveis, e ninguém precisa. Aprenda que * ganha de +, que comparações ganham de && que ganha de ||, e que todo o resto leva parênteses:

if ((flags & MASK) != 0 && (count > 0 || force)) { ... }

Essa linha tem parênteses redundantes e é melhor por causa deles.

Perguntas frequentes

O que o % faz em C?

% é o operador de módulo: ele devolve o resto da divisão inteira. 17 % 5 é 2. Ele funciona só com inteiros - usá-lo em um float ou double é erro de compilação, e fmod() da math.h é o equivalente para ponto flutuante.

Por que 5 / 2 dá 2 em C?

Porque os dois operandos são inteiros, então C faz divisão inteira e descarta a parte fracionária. Torne um dos lados um valor de ponto flutuante para obter 2.5: 5 / 2.0, ou converta um operando com (double)a / b.

Qual é a diferença entre i++ e ++i?

Os dois somam um a i. ++i (prefixo) incrementa primeiro e devolve o novo valor; i++ (pós-fixo) devolve o valor antigo e incrementa depois. Como instrução isolada eles são idênticos - a diferença só importa quando o resultado é usado, como em int b = a++; contra int b = ++a;.

O que é precedência de operadores em C?

É a ordem em que os operadores se associam quando uma expressão não tem parênteses. *, / e % se associam mais forte que + e -, que se associam mais forte que as comparações, que se associam mais forte que &&, depois ||, depois a atribuição. Na dúvida, acrescente parênteses - eles não custam nada e eliminam a pergunta.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR