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.

Você vai aprender
  • Que x++ soma 1 e x-- tira 1 da variável x.
  • 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 com int, double, decimal e até char.
  • O que acontece no limite do int, a armadilha x = 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 pontos e guarde de volta em pontos." Depois desta linha, pontos vale 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
FormaComo 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 de x e depois some 1". O C# entrega o 5 para o y e só então muda x para 6.
int n = ++m;
Pré-fixado (o ++ vem antes): "some 1 primeiro e use o valor novo". O m vira 6, e o 6 vai para o n.

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.

Boa prática: ++ 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.5 vira 2.5, não 1.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çãoUsePor quê
Somar 1 numa linha sozinhax++;É o costume em C#, C, Java e JavaScript.
Laço que conta de 0 a 9for (int i = 0; i < 10; i++)O i++ é a forma clássica (aula for).
Somar outro númerox += 5;Não existe +++++: o ++ só soma 1.
Usar o valor e somar na mesma linhasenha++ ou ++senhaSó se ficar claro. Na dúvida, separe em duas linhas.
A conta não pode estourarchecked { … }Transforma o estouro em exceção.

Quando dá errado

A armadilha 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;.

Um número negativo enorme apareceu do nada

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ãoNomeEfeito em xValor entregue (com x = 5)
x++pós-incrementopassa a valer 65 (o antigo)
++xpré-incrementopassa a valer 66 (o novo)
x--pós-decrementopassa a valer 45 (o antigo)
--xpré-decrementopassa a valer 44 (o novo)
x += 1atribuição compostapassa a valer 66

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:

vidas;

2. Quanto vale?

Depois de int a = 3; int b = a++;, quanto vale b?

b =

3. E agora?

Depois de int a = 3; int b = ++a;, quanto vale b?

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 e x-- tira 1 de x, e guardam o resultado na própria variável.
  • Numa linha sozinha, x++, ++x, x += 1 e x = x + 1 fazem 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'); com string, dá CS0023.
  • Só em variáveis: 5++ e Limite++ (constante) dão CS1059; variável sem valor dá CS0165.
  • int.MaxValue + 1 dá a volta para o menor int; checked transforma isso em OverflowException.
  • Nunca escreva x = x++: a variável não muda.