C# ++ e --
Contar é uma das coisas que os programas mais fazem: mais um ponto no jogo, mais uma visita no site, mais um item no carrinho, uma vida a menos. "Somar 1" e "tirar 1" são tão comuns que o C# tem dois operadores só para isso: ++ (incremento, "aumentar") e -- (decremento, "diminuir"). Nesta aula você vai ver como eles funcionam, a diferença entre x++ e ++x (que confunde todo mundo no começo) e as armadilhas.
- Que
x++soma 1 ex--tira 1 da variávelx. - As quatro formas de somar 1 e qual usar.
- A diferença entre pós-fixado (
x++) e pré-fixado (++x) quando o resultado é usado. - Que
++funciona comint,double,decimale atéchar. - O que acontece no limite do
int, a armadilhax = x++e os erros reais do compilador.
Todos os programas desta aula foram rodados com o .NET 10 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cs, com dotnet run arquivo.cs.
A ideia: o contador de pessoas
Na porta de um show, o segurança segura um contador manual, aquele aparelhinho de metal com um botão. Cada pessoa que entra, ele aperta: clic, mais uma. O contador não sabe fazer contas complicadas; ele só sabe somar 1 ao número que já está lá.
O ++ é esse botão. O -- é o botão do contrário, que tira 1 (alguém saiu do show).
1. Somar e tirar 1
int pontos = 10; pontos++; Console.WriteLine(pontos); pontos++; pontos++; Console.WriteLine(pontos); pontos--; Console.WriteLine(pontos);
$ dotnet run inc1.cs 11 « 10 + 1 13 « mais dois cliques 12 « o -- tirou 1
- int pontos = 10;
- Uma variável comum, com valor inicial 10.
- pontos++;
- "Some 1 a
pontose guarde de volta empontos." Depois desta linha,pontosvale 11. Lê-se "pontos mais mais". Os dois+ficam grudados, sem espaço, e grudados também no nome da variável. - pontos--;
- "Tire 1 de
pontos." São dois sinais de menos.
Repare que o ++ muda a variável. Ele não é como o pontos + 1, que só calcula um valor e não guarda em lugar nenhum.
Quatro formas de somar 1
Todas as linhas abaixo fazem a mesma coisa: somar 1 à variável.
int a = 5; int b = 5; int c = 5; int d = 5; a = a + 1; b += 1; c++; ++d; Console.WriteLine(a + " " + b + " " + c + " " + d);
$ dotnet run formas.cs 6 6 6 6
| Forma | Como se lê | Quando usar |
|---|---|---|
a = a + 1; | "a recebe a mais 1" | Funciona sempre, mas é longa. |
b += 1; | "some 1 a b" | Quando o número pode mudar: b += 5, saldo += deposito (aula Operadores de Atribuição). |
c++; | "c mais mais" | A forma mais usada para somar exatamente 1, numa linha sozinha e nos laços for. |
++d; | "mais mais d" | Numa linha sozinha, é igual ao c++. A diferença aparece na próxima seção. |
Missão: o site da lojinha da Ana recebeu 3 visitas. Use o ++ (três vezes) para o contador mostrar Visitas: 3. Não mude o valor inicial.
Visitas: 3
int visitas = 0;
// some uma visita de cada vez aqui
Console.WriteLine("Visitas: " + visitas);
int visitas = 0;
visitas++;
visitas++;
visitas++;
Console.WriteLine("Visitas: " + visitas);
2. x++ × ++x: quando o resultado é usado
Numa linha sozinha, x++ e ++x fazem a mesma coisa. A diferença aparece quando o ++ está dentro de outra coisa, como uma atribuição ou um Console.WriteLine. Aí importa: o valor usado é o de antes ou o de depois de somar?
int x = 5;
int y = x++;
Console.WriteLine("x = " + x + ", y = " + y);
int m = 5;
int n = ++m;
Console.WriteLine("m = " + m + ", n = " + n);
$ dotnet run prepos.cs x = 6, y = 5 « y recebeu o valor ANTIGO de x m = 6, n = 6 « n recebeu o valor NOVO de m
- int y = x++;
- Pós-fixado (o
++vem depois): "use o valor dexe depois some 1". O C# entrega o 5 para oye só então mudaxpara 6. - int n = ++m;
- Pré-fixado (o
++vem antes): "some 1 primeiro e use o valor novo". Omvira 6, e o 6 vai para on.
Um jeito de lembrar: leia da esquerda para a direita. Em x++, você encontra o x primeiro (usa o valor) e o ++ depois (soma). Em ++x, encontra o ++ primeiro (soma) e o x depois (usa o valor já somado).
Um exemplo de verdade: a fila de senhas
No banco, o painel chama a senha atual e já prepara a próxima. O pós-fixado faz exatamente isso: "mostre a senha e passe para a próxima".
int senha = 1;
Console.WriteLine("Chamando senha " + senha++);
Console.WriteLine("Chamando senha " + senha++);
Console.WriteLine("Chamando senha " + senha++);
Console.WriteLine("Próxima senha: " + senha);
$ dotnet run fila.cs Chamando senha 1 « mostrou 1, depois senha virou 2 Chamando senha 2 Chamando senha 3 Próxima senha: 4 « depois de três chamadas, senha vale 4
Se a fila começasse em 0, o pré-fixado resolveria: "Chamando senha " + ++senha soma primeiro e mostra 1, depois 2.
++ numa linha sozinha
Código como Console.WriteLine(senha++) é curto, mas obriga quem lê a pensar "é o valor de antes ou de depois?". Para quem está começando (e para a maioria dos programadores experientes), é mais claro separar: primeiro Console.WriteLine(senha);, depois senha++;. Guarde a diferença entre x++ e ++x para ler o código dos outros, porque você vai encontrar os dois.
3. Com outros tipos
O ++ e o -- funcionam com todos os tipos numéricos, e também com char:
double altura = 1.5; altura++; Console.WriteLine(altura); decimal preco = 9.90m; preco--; Console.WriteLine(preco); char letra = 'a'; letra++; Console.WriteLine(letra);
$ dotnet run tipos.cs 2,5 « 1,5 + 1 (a vírgula é da cultura pt-BR) 8,90 « 9,90 - 1: o decimal guarda as duas casas b « a letra seguinte na tabela Unicode
- double e decimal
- Soma ou tira exatamente 1, não "um pouquinho".
1.5vira2.5, não1.6. - char
- Cada letra tem um número (aula char e bool);
letra++passa para o número seguinte, que é a letra seguinte:'a'vira'b'. - string e bool
- Não funcionam:
"Ana"++não faz sentido, e o compilador recusa (veja Quando dá errado).
4. No limite do int
Um int vai até 2.147.483.647 (int.MaxValue, aula Inteiros). E se você somar 1 a esse número?
int maior = int.MaxValue; Console.WriteLine(maior); maior++; Console.WriteLine(maior); byte b = 255; b++; Console.WriteLine(b);
$ dotnet run estouro.cs 2147483647 -2147483648 « deu a volta: virou o MENOR int possível 0 « o byte vai de 0 a 255: 255 + 1 deu a volta para 0
Isso é um estouro (overflow). Como o hodômetro de um carro velho que, depois de 99999 km, volta a 00000, o número "dá a volta". O C# não avisa nada, porque, por padrão, ele não confere as contas de inteiros (é mais rápido assim). Quando um estouro seria um erro grave (dinheiro, estoque), peça a conferência com checked:
int maior = int.MaxValue;
checked
{
maior++;
}
Console.WriteLine(maior);
$ dotnet run checked.cs Unhandled exception. System.OverflowException: Arithmetic operation resulted in an overflow. at Program.<Main>$(String[] args) in /home/ana/cs/checked.cs:line 4 $ echo $? 134
Dentro do bloco checked ("conferido"), o estouro vira uma exceção, OverflowException ("a operação aritmética resultou num estouro"), e o programa para em vez de continuar com um número errado. É melhor parar do que mostrar um saldo de −2 bilhões.
Formas diferentes: quando usar cada uma
| Situação | Use | Por quê |
|---|---|---|
| Somar 1 numa linha sozinha | x++; | É o costume em C#, C, Java e JavaScript. |
| Laço que conta de 0 a 9 | for (int i = 0; i < 10; i++) | O i++ é a forma clássica (aula for). |
| Somar outro número | x += 5; | Não existe +++++: o ++ só soma 1. |
| Usar o valor e somar na mesma linha | senha++ ou ++senha | Só se ficar claro. Na dúvida, separe em duas linhas. |
| A conta não pode estourar | checked { … } | Transforma o estouro em exceção. |
Quando dá errado
x = x++: a variável não muda
int vidas = 3; vidas = vidas++; Console.WriteLine(vidas);
$ dotnet run xx.cs 3 « era para ser 4!
O que significa: o vidas++ guarda o valor antigo (3), soma 1 em vidas (que vira 4) e entrega o 3. Aí o vidas = guarda esse 3 de volta, por cima do 4. O compilador não avisa.
Como consertar: o ++ já guarda o resultado; não ponha um = junto. Escreva só vidas++;.
error CS1059: O operando de aumento ou diminuição deve ser uma variável, propriedade ou indexador
int x = 5++; Console.WriteLine(x);
$ dotnet run ei1.cs /home/ana/cs/ei1.cs(1,9): error CS1059: O operando de aumento ou diminuição deve ser uma variável, propriedade ou indexador Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: o ++ precisa de um lugar onde guardar o resultado, uma variável. O 5 é só um número; não dá para mudá-lo. O mesmo erro aparece com (a + b)++ e com uma constante: const int Limite = 10; Limite++; (aula Constantes). Em inglês: The operand of an increment or decrement operator must be a variable, property or indexer.
Como consertar: use ++ só em variáveis: int x = 5; x++;, ou escreva a conta: int x = 5 + 1;.
error CS0023: O operador "++" não pode ser aplicado ao operando do tipo "string"
string nome = "Ana"; nome++; Console.WriteLine(nome);
$ dotnet run ei3.cs /home/ana/cs/ei3.cs(2,1): error CS0023: O operador "++" não pode ser aplicado ao operando do tipo "string" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: "somar 1 a um texto" não existe. Talvez a ideia fosse colar algo no texto (nome += "!", aula Strings) ou contar quantos nomes há (um int à parte).
Como consertar: use ++ com números e char; com texto, use + e +=.
error CS0165: Uso de variável local não atribuída "contador"
int contador; contador++; Console.WriteLine(contador);
$ dotnet run ei4.cs /home/ana/cs/ei4.cs(2,1): error CS0165: Uso de variável local não atribuída "contador" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: o ++ soma 1 ao valor que já está na variável. Mas contador nunca recebeu um valor, então não há de onde partir. O C# não deixa usar uma variável local antes de dar um valor a ela (aula Variáveis).
Como consertar: comece o contador com um valor: int contador = 0;.
O que significa: um int passou de int.MaxValue e deu a volta para −2.147.483.648 (ou um byte passou de 255 e voltou a 0). Não há erro nem aviso.
Como consertar: use um tipo maior (long) se o número pode crescer tanto, e checked para que um estouro pare o programa com OverflowException.
Referência
| Expressão | Nome | Efeito em x | Valor entregue (com x = 5) |
|---|---|---|---|
x++ | pós-incremento | passa a valer 6 | 5 (o antigo) |
++x | pré-incremento | passa a valer 6 | 6 (o novo) |
x-- | pós-decremento | passa a valer 4 | 5 (o antigo) |
--x | pré-decremento | passa a valer 4 | 4 (o novo) |
x += 1 | atribuição composta | passa a valer 6 | 6 |
Funcionam com: int, long, short, byte, double, float, decimal e char. Só em variáveis (e propriedades e índices de arrays, que você verá mais tarde).
Exercícios
1. Uma vida a menos
Complete para tirar 1 de vidas:
2. Quanto vale?
Depois de int a = 3; int b = a++;, quanto vale b?
3. E agora?
Depois de int a = 3; int b = ++a;, quanto vale b?
4. O que char c = 'x'; c++; Console.WriteLine(c); mostra?
5. Com int n = 7;, o que n = n++; deixa em n?
6. Por que 10++ não compila?
Missão final: o painel de senhas
Missão: o painel do banco começa com senha = 0 e deveria chamar as senhas 1 e 2. Mas ele chama 0 e 1. Sem mudar o valor inicial, troque o tipo de incremento (pós-fixado ou pré-fixado?) para aparecer Chamando senha 1 e depois Chamando senha 2.
Chamando senha 1 Chamando senha 2 Última chamada: 2
int senha = 0;
Console.WriteLine("Chamando senha " + senha++);
Console.WriteLine("Chamando senha " + senha++);
Console.WriteLine("Última chamada: " + senha);
int senha = 0;
Console.WriteLine("Chamando senha " + ++senha);
Console.WriteLine("Chamando senha " + ++senha);
Console.WriteLine("Última chamada: " + senha);
Repare na última linha: com o pós-fixado, ela mostrava 2 mesmo chamando 0 e 1, porque a variável já tinha passado para a próxima. Depois, reescreva o programa no estilo "++ numa linha sozinha" e compare qual dos dois é mais fácil de ler.
Resumo
x++soma 1 ex--tira 1 dex, e guardam o resultado na própria variável.- Numa linha sozinha,
x++,++x,x += 1ex = x + 1fazem a mesma coisa. O costume éx++. - Dentro de outra expressão:
x++entrega o valor antigo;++x, o valor novo. - Funcionam com números e
char('a'vira'b'); comstring, dáCS0023. - Só em variáveis:
5++eLimite++(constante) dãoCS1059; variável sem valor dáCS0165. int.MaxValue + 1dá a volta para o menorint;checkedtransforma isso emOverflowException.- Nunca escreva
x = x++: a variável não muda.