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.
- Os cinco operadores:
+(soma),-(subtração),*(multiplicação),/(divisão) e%(resto). - A divisão inteira: por que
7 / 2dá3, e como pedir o3.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 ofmod. - A divisão por zero:
Exceção de ponto flutuantecom inteiros,infenancomdouble. - Por que
2 ^ 3dá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
| Operador | Nome | Exemplo | Resultado |
|---|---|---|---|
+ | soma | 17 + 5 | 22 |
- | subtração | 17 - 5 | 12 |
* | multiplicação (o "vezes" do teclado; o x e o × não servem) | 17 * 5 | 85 |
/ | divisão (com dois int, só o quociente) | 17 / 5 | 3 |
% | resto da divisão (lê-se "módulo" ou "resto") | 17 % 5 | 2 |
- a + b
- Uma expressão: os operandos (
aeb) e o operador no meio. O C calcula o valor e o entrega a quem pediu, aqui oprintf. As variáveisaebnão mudam: fazer a conta não é guardar o resultado (para guardar, aula Operadores de Atribuição). - os espaços
a+bea + bsã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, ema % 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: doisint, então o resultado é7. - / 2.0
- O
2.0, com ponto, é umdouble. Basta um dos lados serdoublepara o C fazer a divisão com vírgula:15 / 2.0=7.5. - %.1f
- O resultado agora é
double, então oprintfusa%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
| Conta | Para que serve |
|---|---|
n % 2 | Par 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 / 10 | Separar os algarismos de um número: o último, e o resto do número. |
(agora + 5) % 24 | Contas que "dão a volta", como o relógio: depois de 23 vem 0. |
total / 60 e total % 60 | Trocar 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
letra - 'A': a posição da letra no alfabeto, contando do zero.'X' - 'A'= 23 (A é 0, B é 1… X é 23).+ chave: anda 3 posições. 23 + 3 = 26, que já passou do Z (25).% 26: o alfabeto tem 26 letras, então o resto "dá a volta". 26 % 26 = 0.+ '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.
% 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
- O
scanfleu o 0 (aula Entrada com scanf). Era um número válido, então oifdeixou passar. fatias / pessoasvirou8 / 0. O processador se recusa a fazer essa conta e o sistema derruba o programa.- 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. - 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ê quer | Forma | Observação |
|---|---|---|
Média com vírgula de dois int | (a + b) / 2.0 | A mais simples quando o divisor está escrito no código. |
(double)(a + b) / n | Quando o divisor é uma variável int (aula Conversão de Tipos). | |
| Quociente e resto | a / b e a % b | Os dois com int. Confira antes se b não é 0. |
| Resto com vírgula | fmod(x, y) | Precisa de <math.h> e de -lm no gcc. |
| Quadrado, cubo | x * x, x * x * x | Mais rápido e exato que pow para inteiros. Nunca x ^ 2. |
| Ímpar? | n % 2 != 0 | Funciona também para negativos (n % 2 == 1 falha com -7). |
Quando dá errado
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).
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ão | Com int | Com double |
|---|---|---|
a + b, a - b, a * b | soma, subtração, multiplicação | iguais |
a / b | quociente, cortado em direção ao zero (7 / 2 = 3, -7 / 2 = -3) | divisão com vírgula (7.0 / 2 = 3.5) |
a % b | resto, com o sinal de a (-7 % 2 = -1) | erro: use fmod(a, b) |
-a | o oposto | o oposto |
a / 0, a % 0 | o programa cai (136) | inf, -inf ou nan |
| int com double | o 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?
2. O resto
Quanto vale 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):
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 ladodoublepara 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-lmno gcc).- Com negativos, o resto tem o sinal do número dividido:
-7 % 2= -1. Para ímpar, usen % 2 != 0. - Inteiro dividido por zero derruba o programa (
Exceção de ponto flutuante, 136); comdouble, saiinfounan. Confira o divisor. - O C não tem potência:
^é outra coisa. Usex * xoupow. - Próximo passo: Operadores de Atribuição, para guardar o resultado das contas e os atalhos
+=,-=…