C Operadores Aritméticos

Computador vem de "computar", calcular. Somar o carrinho de compras, dividir a conta da pizza, calcular a média das provas: tudo isso são contas, e em C elas são feitas com os operadores aritméticos: +, -, *, / e %. Quatro deles você conhece da escola. O quinto, o %, é novidade e vai aparecer o tempo todo. E a divisão tem uma surpresa: em C, 7 / 2 dá 3.

Você vai aprender
  • Os cinco operadores: + (soma), - (subtração), * (multiplicação), / (divisão) e % (resto).
  • A divisão inteira: por que 7 / 2 dá 3, e como pedir o 3.5.
  • O resto da divisão, %, e os truques que ele resolve: par ou ímpar, último dígito, horas no relógio, minutos e segundos, cifra de César.
  • O que acontece com números negativos e com números com vírgula (double), e o fmod.
  • A divisão por zero: Exceção de ponto flutuante com inteiros, inf e nan com double.
  • Por que 2 ^ 3 dá 1: o C não tem operador de potência.

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. As saídas foram iguais no editor do guia.


A ideia: dois jeitos de dividir

A Ana tem 7 balas para dividir entre 2 sobrinhos. Na calculadora, 7 ÷ 2 = 3,5. Mas ninguém corta uma bala ao meio: cada sobrinho ganha 3 balas e sobra 1. Essa é a "conta armada" da escola, com quociente (3) e resto (1).

O C sabe dividir dos dois jeitos. Com números inteiros (int), ele faz a conta das balas: o / dá o quociente e o % dá o resto. Com números com vírgula (double), ele faz a conta da calculadora. Quem escolhe qual conta é feita é o tipo dos números.


1. As cinco contas

#include <stdio.h>

int main(void) {
    int a = 17;
    int b = 5;
    printf("%d + %d = %d\n", a, b, a + b);
    printf("%d - %d = %d\n", a, b, a - b);
    printf("%d * %d = %d\n", a, b, a * b);
    printf("%d / %d = %d\n", a, b, a / b);
    printf("%d %% %d = %d\n", a, b, a % b);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra contas.c -o contas
$ ./contas
17 + 5 = 22
17 - 5 = 12
17 * 5 = 85
17 / 5 = 3       « 17 dividido por 5 dá 3 (e sobram 2)
17 % 5 = 2       « o que sobrou
OperadorNomeExemploResultado
+soma17 + 522
-subtração17 - 512
*multiplicação (o "vezes" do teclado; o x e o × não servem)17 * 585
/divisão (com dois int, só o quociente)17 / 53
%resto da divisão (lê-se "módulo" ou "resto")17 % 52
a + b
Uma expressão: os operandos (a e b) e o operador no meio. O C calcula o valor e o entrega a quem pediu, aqui o printf. As variáveis a e b não mudam: fazer a conta não é guardar o resultado (para guardar, aula Operadores de Atribuição).
os espaços
a+b e a + b são a mesma coisa para o compilador. O guia usa espaços em volta dos operadores para ficar mais fácil de ler.
%% no printf
Dentro do texto do printf, o % começa uma lacuna (%d). Para mostrar o sinal %, escreve-se %% (aula Formatos do printf). Fora das aspas, em a % b, o % é o operador de resto.

Quando há várias contas na mesma expressão, vale a regra da escola: *, / e % antes de + e -, e os parênteses mandam em tudo. 2 + 3 * 4 dá 14; (2 + 3) * 4 dá 20. Os detalhes estão em Precedência.


2. A divisão inteira

Quando os dois lados do / são inteiros, o resultado também é inteiro: o C calcula o quociente e joga fora a parte depois da vírgula. Não arredonda: corta. 17 / 5 = 3,4 vira 3; 19 / 5 = 3,8 também vira 3.

#include <stdio.h>

int main(void) {
    int balas = 7;
    int criancas = 2;
    int cada = balas / criancas;
    int sobram = balas % criancas;
    printf("Cada criança ganha %d balas e sobram %d.\n", cada, sobram);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra balas.c -o balas
$ ./balas
Cada criança ganha 3 balas e sobram 1.

Para as balas, é exatamente o que a Ana queria. Para uma média de notas, não:

#include <stdio.h>

int main(void) {
    int prova1 = 7;
    int prova2 = 8;
    printf("Média: %d\n", (prova1 + prova2) / 2);
    printf("Média: %.1f\n", (prova1 + prova2) / 2.0);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra media.c -o media
$ ./media
Média: 7         « 15 / 2: int com int, divisão inteira
Média: 7.5       « 15 / 2.0: um dos lados é double, divisão de calculadora
(prova1 + prova2) / 2
Os parênteses fazem a soma primeiro (15). Depois, 15 / 2: dois int, então o resultado é 7.
/ 2.0
O 2.0, com ponto, é um double. Basta um dos lados ser double para o C fazer a divisão com vírgula: 15 / 2.0 = 7.5.
%.1f
O resultado agora é double, então o printf usa %f, aqui com uma casa depois do ponto (aula Formatos do printf).

Quando os dois números estão em variáveis int e não dá para escrever 2.0, a saída é o cast: (double)soma / quantidade. A aula Conversão de Tipos mostra as cinco formas de escrever essa conta e as duas que dão errado.

Missão: a Ana tirou 8 e 9. O programa mostra Média: 8, mas a média é 8,5. Mude a conta e o formato para a saída ser:

Média: 8.5
#include <stdio.h>

int main(void) {
    int prova1 = 8;
    int prova2 = 9;
    printf("Média: %d\n", (prova1 + prova2) / 2);
    return 0;
}
#include <stdio.h>

int main(void) {
    int prova1 = 8;
    int prova2 = 9;
    printf("Média: %.1f\n", (prova1 + prova2) / 2.0);
    return 0;
}

Se você trocar só o %d por %.1f e deixar o / 2, o compilador avisa: o %f espera um double e a conta ainda é int. Se trocar só o 2 por 2.0, avisa ao contrário (o %d espera um int e a conta virou double). Nos dois casos o programa mostra lixo: no laboratório da Ana saíram Média: 0.0 e Média: 3. A conta e o formato andam juntos.


3. O resto, %: o canivete suíço

O % responde "quanto sobra?". Parece pouco, mas resolve muitos problemas:

#include <stdio.h>

int main(void) {
    int n = 1234;
    printf("%d %% 2 = %d\n", n, n % 2);
    printf("%d %% 10 = %d\n", n, n % 10);
    printf("%d / 10 = %d\n", n, n / 10);
    int agora = 22;
    printf("Daqui a 5 horas: %d h\n", (agora + 5) % 24);
    int segundos = 200;
    printf("%d s = %d min %d s\n", segundos, segundos / 60, segundos % 60);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra resto.c -o resto
$ ./resto
1234 % 2 = 0              « resto 0 na divisão por 2: é par
1234 % 10 = 4             « o último algarismo
1234 / 10 = 123           « o número sem o último algarismo
Daqui a 5 horas: 3 h      « 22 + 5 = 27, que no relógio é 3
200 s = 3 min 20 s        « 200 / 60 = 3 minutos; 200 % 60 = 20 segundos
ContaPara que serve
n % 2Par ou ímpar: 0 é par, 1 é ímpar (para positivos). Você vai usar com o if: if (n % 2 == 0).
n % 7 == 0"n é divisível por 7?" (o resto é zero).
n % 10 e n / 10Separar os algarismos de um número: o último, e o resto do número.
(agora + 5) % 24Contas que "dão a volta", como o relógio: depois de 23 vem 0.
total / 60 e total % 60Trocar de unidade: segundos em minutos, centavos em reais, dias em semanas.

A cifra de César

O general romano Júlio César escrevia mensagens secretas trocando cada letra pela que vem 3 posições depois: A vira D, B vira E… E o X? Depois do Z, a contagem volta para o começo: X vira A. "Dar a volta" é trabalho para o %. Como as letras são números para o C (aula Caracteres), dá para fazer conta com elas:

#include <stdio.h>

int main(void) {
    char letra = 'X';
    int chave = 3;
    char cifrada = (letra - 'A' + chave) % 26 + 'A';
    printf("%c vira %c\n", letra, cifrada);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra cesar.c -o cesar
$ ./cesar
X vira A
  1. letra - 'A': a posição da letra no alfabeto, contando do zero. 'X' - 'A' = 23 (A é 0, B é 1… X é 23).
  2. + chave: anda 3 posições. 23 + 3 = 26, que já passou do Z (25).
  3. % 26: o alfabeto tem 26 letras, então o resto "dá a volta". 26 % 26 = 0.
  4. + 'A': transforma a posição de volta em letra. 0 + 'A' = 'A'.

Experimente: troque a letra (sempre maiúscula) e a chave. Qual chave transforma 'A' em 'N'? E o que acontece com a chave 26? Depois tente decifrar: com a chave 23, o D volta a ser A. Por quê?

#include <stdio.h>

int main(void) {
    char letra = 'X';
    int chave = 3;
    char cifrada = (letra - 'A' + chave) % 26 + 'A';
    printf("%c vira %c\n", letra, cifrada);
    return 0;
}

4. Números negativos

O - também serve com um operando só: -temperatura é o oposto de temperatura. É o menos unário ("de um operando"):

#include <stdio.h>

int main(void) {
    int temperatura = 12;
    int oposto = -temperatura;
    printf("%d %d %d\n", temperatura, oposto, -oposto);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra sinal.c -o sinal
$ ./sinal
12 -12 12        « o oposto do oposto é o número de volta

E na divisão inteira com negativos? O C corta em direção ao zero: -3,5 vira -3 (e não -4). O resto fica com o sinal do número dividido:

#include <stdio.h>

int main(void) {
    printf("-7 / 2 = %d\n", -7 / 2);
    printf("-7 %% 2 = %d\n", -7 % 2);
    printf("7 / -2 = %d\n", 7 / -2);
    printf("7 %% -2 = %d\n", 7 % -2);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra negativos.c -o negativos
$ ./negativos
-7 / 2 = -3       « -3,5 cortado em direção ao zero
-7 % 2 = -1       « o resto tem o sinal do -7
7 / -2 = -3
7 % -2 = 1        « o resto tem o sinal do 7

A regra que amarra os dois: (a / b) * b + a % b é sempre igual a a. Confira: (-3) * 2 + (-1) = -7. Essa regra vale desde o C99; antes dele, cada compilador podia arredondar a divisão negativa para um lado.

Ímpar negativo: % 2 dá -1

Como -7 % 2 é -1, a pergunta n % 2 == 1 erra com números negativos ímpares. Para saber se um número é ímpar, pergunte n % 2 != 0, que funciona para os dois sinais.


5. Contas com double

Com números com vírgula (aula float e double), +, -, * e / funcionam como na calculadora. Se um lado é int e o outro é double, o C converte o int para double antes da conta:

#include <stdio.h>

int main(void) {
    double preco = 12.5;
    int quantidade = 3;
    printf("Total: %.2f\n", preco * quantidade);
    printf("7 / 2 = %d\n", 7 / 2);
    printf("7.0 / 2 = %.2f\n", 7.0 / 2);
    printf("7 / 2.0 = %.2f\n", 7 / 2.0);
    printf("7.5 - 0.5 = %.2f\n", 7.5 - 0.5);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra reais.c -o reais
$ ./reais
Total: 37.50        « double * int: o 3 virou 3.0
7 / 2 = 3
7.0 / 2 = 3.50
7 / 2.0 = 3.50
7.5 - 0.5 = 7.00    « o resultado é double, mesmo sem nada depois da vírgula

O % é só para inteiros

A Ana tem uma fita de 7,5 metros e quer cortar pedaços de 2 metros. Quanto sobra?

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

int main(void) {
    double comprimento = 7.5;
    printf("%.2f\n", comprimento % 2);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra restodouble.c -o restodouble
restodouble.c: In function ‘main’:
restodouble.c:5:34: error: invalid operands to binary % (have ‘double’ and ‘int’)
    5 |     printf("%.2f\n", comprimento % 2);
      |                                  ^

"Operandos inválidos para o % binário (de dois lados): são double e int." O % só aceita inteiros dos dois lados. No editor, o Clang diz invalid operands to binary expression ('double' and 'int'). Para o resto com vírgula existe a função fmod (floating-point modulo, resto com vírgula), da <math.h>:

#include <stdio.h>
#include <math.h>

int main(void) {
    double comprimento = 7.5;
    double pedaco = 2.0;
    printf("Sobram %.2f metros\n", fmod(comprimento, pedaco));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra fmod.c -o fmod
/usr/bin/x86_64-linux-gnu-ld.bfd: /tmp/ccGZtyvT.o: na função "main":
fmod.c:(.text+0x39): referência não definida para "fmod"
collect2: error: ld returned 1 exit status
$ gcc -std=c23 -Wall -Wextra fmod.c -o fmod -lm      « -lm: junte a biblioteca de matemática
$ ./fmod
Sobram 1.50 metros       « 7,5 = 3 pedaços de 2 + 1,5

No gcc, as funções da math.h moram numa biblioteca separada, e o -lm no fim do comando pede para juntá-la. Sem ele, o ligador diz que não achou o fmod (undefined reference to, em português "referência não definida para"). No editor do guia, o -lm não é preciso. Mais funções (raiz, potência, arredondar) na aula Matemática.


6. Divisão por zero

Na matemática, dividir por zero não tem resposta. No C, o que acontece depende do tipo.

Com inteiros: o programa cai

Se o zero está escrito no código, o gcc percebe e avisa:

$ gcc -std=c23 -Wall -Wextra zero.c -o zero
zero.c: In function ‘main’:
zero.c:5:45: warning: division by zero [-Wdiv-by-zero]
    5 |     printf("Fatias por pessoa: %d\n", pizza / 0);
      |                                             ^
$ ./zero
Exceção de ponto flutuante (imagem do núcleo gravada) ./zero

O caso perigoso é quando o zero vem de fora, e o compilador não tem como saber. A pizza da Ana tem 8 fatias:

#include <stdio.h>

int main(void) {
    int fatias = 8;
    int pessoas = 0;
    printf("Quantas pessoas? ");
    if (scanf("%d", &pessoas) != 1) {
        return 1;
    }
    printf("Cada uma come %d fatias.\n", fatias / pessoas);
    printf("Sobram %d fatias.\n", fatias % pessoas);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra pizza.c -o pizza          « nenhum aviso
$ ./pizza
Quantas pessoas? 3
Cada uma come 2 fatias.
Sobram 2 fatias.
$ ./pizza
Quantas pessoas? 0
Exceção de ponto flutuante (imagem do núcleo gravada) ./pizza
$ echo $?
136
  1. O scanf leu o 0 (aula Entrada com scanf). Era um número válido, então o if deixou passar.
  2. fatias / pessoas virou 8 / 0. O processador se recusa a fazer essa conta e o sistema derruba o programa.
  3. A mensagem fala em "ponto flutuante", mas a conta era de inteiros: é só o nome histórico do sinal que o Linux manda (SIGFPE, floating-point exception). O código de saída é 136.
  4. O % por zero dá o mesmo erro.

O conserto é conferir o divisor antes da conta: if (pessoas == 0) { … }, com o que você vai ver em if e else. Para o C, dividir um inteiro por zero é comportamento indefinido (aula Comportamento Indefinido): no Linux, ele cai; em outros sistemas, pode dar qualquer resultado. O editor do guia cai igual, com a mesma mensagem e o mesmo 136.

Com double: infinito e "não é número"

#include <stdio.h>

int main(void) {
    double zero = 0.0;
    printf("10.0 / 0 = %f\n", 10.0 / zero);
    printf("-10.0 / 0 = %f\n", -10.0 / zero);
    printf("0.0 / 0 = %f\n", zero / zero);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra zerod.c -o zerod
$ ./zerod
10.0 / 0 = inf       « infinito
-10.0 / 0 = -inf     « menos infinito
0.0 / 0 = -nan       « nan: "not a number", não é um número

Com double, o programa não cai: o resultado vira um valor especial. inf (de infinity) é o infinito, e nan (not a number) quer dizer que a conta não tem resultado. O sinal de menos do -nan é um detalhe do processador e não quer dizer nada. Esses valores "contaminam" as contas seguintes (inf + 1 continua inf), então o melhor é não chegar neles: confira o divisor também aqui.


7. O ^ não é potência

Em muitas calculadoras e planilhas, 2^3 é "2 elevado a 3". Em C, não existe operador de potência, e o ^ é outra coisa (o "ou exclusivo" dos bits, aula Operadores de Bits):

#include <stdio.h>

int main(void) {
    int lado = 5;
    printf("2 elevado a 3: %d\n", 2 ^ 3);
    printf("Área do quadrado: %d\n", lado ^ 2);
    printf("Área do quadrado: %d\n", lado * lado);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra potencia.c -o potencia
potencia.c: In function ‘main’:
potencia.c:5:37: warning: result of ‘2^3’ is 1; did you mean ‘1 << 3’ (8)? [-Wxor-used-as-pow]
    5 |     printf("2 elevado a 3: %d\n", 2 ^ 3);
      |                                     ^
      |                                   -
      |                                   1 <<
potencia.c:5:35: note: you can silence this warning by using a hexadecimal constant (0x2 rather than 2)
    5 |     printf("2 elevado a 3: %d\n", 2 ^ 3);
      |                                   ^
      |                                   0x2
$ ./potencia
2 elevado a 3: 1          « não é 8!
Área do quadrado: 7       « não é 25, e o gcc nem avisou
Área do quadrado: 25      « o jeito certo: lado * lado

Com dois números escritos no código, o gcc desconfia: "o resultado de 2^3 é 1; você quis dizer 1 << 3 (8)?" (o aviso -Wxor-used-as-pow, "xor usado como potência", vem ligado mesmo sem -Wall). Com uma variável, como em lado ^ 2, ele não avisa nada, e a conta sai errada em silêncio. Para potências pequenas, multiplique: lado * lado, x * x * x. Para as outras, use a função pow da <math.h> (aula Matemática).


Formas diferentes de fazer a mesma conta

Você querFormaObservação
Média com vírgula de dois int(a + b) / 2.0A mais simples quando o divisor está escrito no código.
(double)(a + b) / nQuando o divisor é uma variável int (aula Conversão de Tipos).
Quociente e restoa / b e a % bOs dois com int. Confira antes se b não é 0.
Resto com vírgulafmod(x, y)Precisa de <math.h> e de -lm no gcc.
Quadrado, cubox * x, x * x * xMais rápido e exato que pow para inteiros. Nunca x ^ 2.
Ímpar?n % 2 != 0Funciona também para negativos (n % 2 == 1 falha com -7).

Quando dá errado

A média saiu sem a parte quebrada (7 em vez de 7.5)

O que significa: os dois lados do / eram int, e a divisão inteira cortou a parte depois da vírgula. Guardar o resultado numa variável double não ajuda: o corte acontece antes.

Como consertar: faça um dos lados ser double: / 2.0 ou (double)soma / n.

Exceção de ponto flutuante (imagem do núcleo gravada)

O que significa: uma divisão (/) ou um resto (%) de inteiros por zero. O código de saída é 136. Quando o zero está escrito no código, o gcc avisa antes: warning: division by zero [-Wdiv-by-zero] (no Clang, division by zero is undefined [-Wdivision-by-zero]).

Como consertar: confira o divisor antes da conta, principalmente quando ele vem do scanf.

error: invalid operands to binary % (have ‘double’ and ‘int’)

O que significa: você usou % com um double. Ele só aceita inteiros dos dois lados.

Como consertar: use fmod(x, y), da <math.h>, ou guarde os valores em int se eles forem mesmo inteiros.

referência não definida para "fmod" (undefined reference to)

O que significa: o código compilou, mas o ligador não achou a função. As funções da math.h estão numa biblioteca à parte.

Como consertar: acrescente -lm no fim do comando do gcc.

warning: result of ‘2^3’ is 1; did you mean ‘1 << 3’ (8)? [-Wxor-used-as-pow]

O que significa: você usou ^ como potência. Em C, ele é o "ou exclusivo" dos bits.

Como consertar: x * x ou pow(x, 2).

A conta deu um número negativo enorme

O que significa: o resultado passou do maior valor que o int guarda (2147483647) e "deu a volta": 2147483647 + 1 vira -2147483648. É o estouro, explicado em Inteiros.

Como consertar: use um tipo maior, como long long, para contas que podem crescer muito.


Referência rápida

ExpressãoCom intCom double
a + b, a - b, a * bsoma, subtração, multiplicaçãoiguais
a / bquociente, cortado em direção ao zero (7 / 2 = 3, -7 / 2 = -3)divisão com vírgula (7.0 / 2 = 3.5)
a % bresto, com o sinal de a (-7 % 2 = -1)erro: use fmod(a, b)
-ao opostoo oposto
a / 0, a % 0o programa cai (136)inf, -inf ou nan
int com doubleo int vira double e a conta é com vírgula
prioridade* / % antes de + -; parênteses primeiro (Precedência)

Exercícios

1. A divisão inteira

Quanto vale 23 / 4 em C, com dois int?

23 / 4 =

2. O resto

Quanto vale 23 % 4?

23 % 4 =

3. Centavos em reais

O preço está em centavos: int preco = 1250;. Complete com o operador que dá os centavos que sobram depois dos reais inteiros (a resposta é 50):

printf("R$ %d,%02d\n", preco / 100, preco 100);

4. Qual expressão dá 2.5?

5. Quanto vale -9 % 4 em C?

6. Com int x = 4;, o que printf("%d\n", x ^ 2); mostra?

Missão final: o cronômetro

Missão: o cronômetro da corrida da Ana marcou um tempo em segundos (a entrada preparada é 7384). Calcule as horas, os minutos e os segundos com / e %. Dicas: uma hora tem 3600 segundos; os minutos são o que sobra das horas, divididos por 60; os segundos são o que sobra dos minutos. Sem avisos. A saída deve terminar com:

7384 segundos = 2 h 3 min 4 s
#include <stdio.h>

int main(void) {
    int total = 0;
    printf("Segundos: ");
    if (scanf("%d", &total) != 1) {
        return 1;
    }
    int horas = 0;     // troque o 0 pela conta
    int minutos = 0;   // troque o 0 pela conta
    int segundos = 0;  // troque o 0 pela conta
    printf("%d segundos = %d h %d min %d s\n", total, horas, minutos, segundos);
    return 0;
}
#include <stdio.h>

int main(void) {
    int total = 0;
    printf("Segundos: ");
    if (scanf("%d", &total) != 1) {
        return 1;
    }
    int horas = total / 3600;
    int minutos = total % 3600 / 60;
    int segundos = total % 60;
    printf("%d segundos = %d h %d min %d s\n", total, horas, minutos, segundos);
    return 0;
}

Troque a entrada da caixa para 59, 60 e 3600 e confira se o resultado faz sentido. Em total % 3600 / 60, o % e o / têm a mesma prioridade e são feitos da esquerda para a direita: primeiro o resto (184), depois a divisão (3).


Resumo

  • +, -, *, / e % fazem contas; a conta não muda as variáveis, só calcula um valor.
  • Com dois int, o / dá só o quociente e corta em direção ao zero: 7 / 2 = 3. Basta um lado double para a divisão ter vírgula: 7 / 2.0 = 3.5.
  • % é o resto: par ou ímpar, algarismos, relógio, minutos e segundos, cifra de César. Só com inteiros; com vírgula, fmod (e -lm no gcc).
  • Com negativos, o resto tem o sinal do número dividido: -7 % 2 = -1. Para ímpar, use n % 2 != 0.
  • Inteiro dividido por zero derruba o programa (Exceção de ponto flutuante, 136); com double, sai inf ou nan. Confira o divisor.
  • O C não tem potência: ^ é outra coisa. Use x * x ou pow.
  • Próximo passo: Operadores de Atribuição, para guardar o resultado das contas e os atalhos +=, -=…