C++ ++ e --
Somar 1 numa variável é, provavelmente, a conta mais feita em programação: contar visitas, passar para a próxima senha da fila, andar para o próximo item de uma lista, ganhar uma vida no jogo. É tão comum que tem um operador só para isso, o ++, e o seu irmão que subtrai 1, o --. A própria linguagem tem esse nome por causa dele: C++ é "o C, mais um passo". Nesta aula você vai usar os dois, entender a diferença entre ++x e x++, e ver por que algumas combinações são proibidas pelo bom senso (e pelo compilador).
- O que
++(incremento) e--(decremento) fazem. - A diferença entre pré (
++x) e pós (x++): quando o valor muda e qual valor é entregue. - Onde eles aparecem mais: no laço
for. - Em que tipos funcionam, e os erros com
bool,conste números. - Por que
i = i++ei++ + i++são armadilhas, e o aviso-Wsequence-point.
Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cpp, com o comando padrão do guia: g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.
A ideia: o contador de pessoas da portaria
Sabe aquele contador de mão, de metal, que o segurança aperta a cada pessoa que entra no show? Cada clique soma exatamente 1. Ele não serve para somar 5 de uma vez, só para "mais um". O ++ é esse contador. O -- é o mesmo contador apertado ao contrário, quando alguém sai.
| Escrito | Faz o mesmo que | Nome |
|---|---|---|
++x; ou x++; | x += 1; ou x = x + 1; | Incremento |
--x; ou x--; | x -= 1; ou x = x - 1; | Decremento |
Os sinais são dois + (ou dois -) grudados, e ficam grudados na variável, antes ou depois dela. O += da tabela vem da aula Operadores de Atribuição.
1. Somar e subtrair 1
#include <iostream>
int main() {
int vidas = 3;
++vidas;
std::cout << vidas << '\n';
vidas++;
std::cout << vidas << '\n';
--vidas;
std::cout << vidas << '\n';
vidas--;
std::cout << vidas << '\n';
}
$ g++ -std=c++23 -Wall -Wextra incremento.cpp -o incremento $ ./incremento 4 « ++vidas: 3 + 1 5 « vidas++: 4 + 1 4 « --vidas: 5 - 1 3 « vidas--: 4 - 1
Quando o ++ ou o -- está sozinho na linha, como aqui, tanto faz escrever antes ou depois da variável: ++vidas; e vidas++; fazem exatamente a mesma coisa. A diferença só aparece quando o resultado é usado na mesma linha, como você vai ver agora.
2. Pré × pós: ++x e x++
Além de mudar a variável, o ++ entrega um valor, que pode ser guardado ou mostrado. E aí as duas formas se separam:
- Pré-incremento,
++x(o++vem antes): primeiro soma, depois entrega o valor novo. - Pós-incremento,
x++(o++vem depois): entrega o valor antigo, e soma depois.
#include <iostream>
int main() {
int a = 5;
int b = ++a; // pré: soma primeiro, depois entrega
std::cout << "a = " << a << ", b = " << b << '\n';
int c = 5;
int d = c++; // pós: entrega o valor antigo, depois soma
std::cout << "c = " << c << ", d = " << d << '\n';
}
$ g++ -std=c++23 -Wall -Wextra prepos.cpp -o prepos $ ./prepos a = 6, b = 6 « ++a: o a virou 6 e o b recebeu o 6 novo c = 6, d = 5 « c++: o d recebeu o 5 antigo, e o c virou 6 do mesmo jeito
Repare: nos dois casos a variável terminou com 6. O que muda é só o valor entregue para quem estava esperando (b e d).
Com x = 5 | Valor entregue | x depois | Leia como |
|---|---|---|---|
++x | 6 | 6 | "some e me dê" |
x++ | 5 | 6 | "me dê e some" |
--x | 4 | 4 | "subtraia e me dê" |
x-- | 5 | 4 | "me dê e subtraia" |
Um truque para lembrar: leia da esquerda para a direita. Em ++x, você encontra primeiro o ++ (soma) e depois o x (entrega). Em x++, encontra primeiro o x (entrega) e depois o ++ (soma).
A fila de senhas
O pós-incremento combina com a ideia de "chamar e avançar". Na fila do banco, o painel mostra a senha atual e já prepara a próxima:
#include <iostream>
int main() {
int senha = 100;
std::cout << senha++ << '\n';
std::cout << senha << '\n';
std::cout << ++senha << '\n';
std::cout << senha-- << '\n';
std::cout << --senha << '\n';
}
$ g++ -std=c++23 -Wall -Wextra prepos2.cpp -o prepos2 $ ./prepos2 100 « senha++ mostra 100 e a senha vira 101 101 « confirmando 102 « ++senha: vira 102 e mostra 102 102 « senha-- mostra 102 e a senha vira 101 100 « --senha: vira 100 e mostra 100
Experimente: antes de rodar, escreva num papel o que você acha que cada linha vai mostrar. Depois rode e confira. Troque x++ por ++x na primeira linha e veja o que muda.
#include <iostream>
int main() {
int x = 10;
int y = x++;
std::cout << "x = " << x << ", y = " << y << '\n';
int z = --x;
std::cout << "x = " << x << ", z = " << z << '\n';
}
Misturar ++ com outras coisas na mesma linha economiza uma linha, mas obriga quem lê a pensar em "antes ou depois". std::cout << senha << '\n'; ++senha; é mais claro que std::cout << senha++ << '\n';. E quando estiver sozinho, prefira o pré (++x): com int não faz diferença, mas com tipos mais complexos que você verá depois (como os iteradores), o pós precisa guardar uma cópia do valor antigo, e o pré não.
3. No laço for
O lugar onde você mais vai ver ++ e -- é a terceira parte do for, o laço que repete um bloco contando (aula for). Ali, o valor entregue não é usado, então ++i e i++ dão o mesmo resultado:
#include <iostream>
int main() {
for (int i = 1; i <= 3; ++i) {
std::cout << "Volta " << i << '\n';
}
for (int i = 3; i >= 1; i--) {
std::cout << i << "... ";
}
std::cout << "Já!\n";
}
$ g++ -std=c++23 -Wall -Wextra contagem.cpp -o contagem $ ./contagem Volta 1 Volta 2 Volta 3 3... 2... 1... Já!
- for (int i = 1; i <= 3; ++i)
- "Comece com
i= 1; enquantoi <= 3, repita; depois de cada volta,++i." O++ié o que faz a contagem andar. - for (int i = 3; i >= 1; i--)
- Contagem regressiva: começa em 3 e o
i--desce de 1 em 1.
Esqueceu o ++? O i nunca muda, a condição nunca fica falsa, e o laço roda para sempre. Os detalhes estão na aula for.
4. Em que tipos funcionam
O ++ e o -- funcionam com todos os tipos de número, inclusive double (soma 1.0) e char (vai para o próximo caractere da tabela):
#include <iostream>
int main() {
double temperatura = 21.5;
temperatura++;
std::cout << temperatura << '\n';
char letra = 'a';
letra++;
std::cout << letra << '\n';
}
$ g++ -std=c++23 -Wall -Wextra doubleinc.cpp -o doubleinc $ ./doubleinc 22.5 b « 'a' (97) + 1 = 'b' (98)
Mas há três coisas em que eles não funcionam:
bool: proibido desde o C++17
$ g++ -std=c++23 -Wall -Wextra boolinc.cpp -o boolinc
boolinc.cpp: In function ‘int main()’:
boolinc.cpp:3:5: error: use of an operand of type ‘bool’ in ‘operator++’ is forbidden in C++17
3 | ligado++;
| ^~~~~~
"O uso de um operando do tipo bool no operator++ é proibido no C++17." Antigamente, ligado++ transformava false em true, mas isso confundia mais do que ajudava e foi removido. Para ligar, escreva ligado = true;; para inverter, ligado = !ligado; (veja Operadores Lógicos).
Constantes
$ g++ -std=c++23 -Wall -Wextra consti.cpp -o consti
consti.cpp: In function ‘int main()’:
consti.cpp:3:5: error: decrement of read-only variable ‘vidas’
3 | vidas--;
| ^~~~~
"Decremento de variável somente leitura." vidas foi criada como const int vidas = 3; (aula Constantes), e nada pode mudá-la.
Números escritos direto
$ g++ -std=c++23 -Wall -Wextra literal.cpp -o literal
literal.cpp: In function ‘int main()’:
literal.cpp:2:13: error: lvalue required as increment operand
2 | int x = 5++;
| ^
literal.cpp:2:9: warning: unused variable ‘x’ [-Wunused-variable]
2 | int x = 5++;
| ^
"É necessário um lvalue como operando do incremento": o ++ precisa de um lugar para guardar o resultado, ou seja, uma variável. O 5 não é um lugar (o mesmo erro da aula de atribuição, com 10 = estoque). Se você queria 6, escreva 5 + 1. O aviso de baixo é consequência: como a linha deu erro, o x nunca foi usado.
5. A armadilha: mudar e usar a mesma variável na mesma conta
O ++ faz duas coisas ao mesmo tempo: entrega um valor e muda a variável. Se a mesma variável aparece mais de uma vez na mesma expressão, e alguma dessas aparições a muda, surge a pergunta: em que ordem as coisas acontecem? Às vezes a regra do C++ não responde, e aí o programa tem comportamento indefinido: o compilador pode fazer qualquer coisa (aula Comportamento Indefinido).
i++ + i++: indefinido
#include <iostream>
int main() {
int i = 5;
int r = i++ + i++;
std::cout << r << ' ' << i << '\n';
}
$ g++ -std=c++23 -Wall -Wextra ub2.cpp -o ub2
ub2.cpp: In function ‘int main()’:
ub2.cpp:5:14: warning: operation on ‘i’ may be undefined [-Wsequence-point]
5 | int r = i++ + i++;
| ~^~
$ ./ub2
11 7
"A operação em i pode ser indefinida." O C++ não diz qual dos dois i++ é feito primeiro, nem quando cada um guarda o resultado. O g++ escolheu um jeito e deu 11 (5 + 6); outro compilador, ou o mesmo com outras opções, pode dar outra coisa, e isso é permitido pela regra. O nome do aviso, -Wsequence-point ("ponto de sequência"), vem dos antigos "pontos" em que o C e o C++ garantiam que tudo antes já tinha acontecido. Ele faz parte do -Wall. No editor do guia, o Clang avisa assim: multiple unsequenced modifications to 'i' [-Wunsequenced], "várias modificações sem ordem definida em i".
i = i++: não faça
Uma tentativa comum de quem começa: "quero somar 1 no i, então i = i++". Veja:
#include <iostream>
int main() {
int i = 5;
i = i++;
std::cout << i << '\n';
}
$ g++ -std=c++23 -Wall -Wextra ub.cpp -o ub
ub.cpp: In function ‘int main()’:
ub.cpp:5:7: warning: operation on ‘i’ may be undefined [-Wsequence-point]
5 | i = i++;
| ~~^~~~~
ub.cpp:5:7: warning: operation on ‘i’ may be undefined [-Wsequence-point]
$ ./ub
5 « não somou nada!
O i continuou 5. Até o C++14, essa linha era comportamento indefinido, como a anterior. O C++17 criou uma regra para ela: o lado direito do = é resolvido inteiro antes da atribuição. Então:
i++entrega o valor antigo, 5, e mudaipara 6.- A atribuição guarda o 5 entregue em
i, por cima do 6. - Resultado:
ivolta a ser 5. O++foi desperdiçado.
Ou seja: no C++ de hoje ela não é mais indefinida, mas é inútil, e em código C++14 ou mais antigo é indefinida. O g++ 15 continua avisando, mesmo com -std=c++23 (o Clang 21 só avisa se você pedir -std=c++14). Seja qual for a versão, a resposta é a mesma: não escreva isso. Para somar 1, basta ++i; (ou i++;, ou i += 1;), sozinho.
Numa mesma expressão, se uma variável é mudada (++, --, =, +=…), não use essa variável de novo. Separe em linhas. Assim você nunca precisa saber as regras finas de ordem, e o código fica mais fácil de ler. E nunca ignore o aviso may be undefined.
Experimente: rode e leia o aviso do compilador na linha do r. Depois reescreva em três linhas sem ambiguidade: some o i ao r, faça ++i, some de novo. O aviso deve sumir.
#include <iostream>
int main() {
int i = 5;
int r = i++ + i++;
std::cout << "r = " << r << ", i = " << i << '\n';
}
Formas diferentes: qual usar
Para somar 1 em x | Quando usar |
|---|---|
++x; | O padrão: sozinho na linha e no for. |
x++; | Igual ao ++x quando sozinho. Muito comum também; é questão de estilo. |
x += 1; | Quando o passo pode mudar depois (x += 2). |
x = x + 1; | A forma mais longa; útil para quem está começando a ler código. |
y = x++; / y = ++x; | Só quando você quer mesmo o valor antigo/novo, e a linha fica clara. |
x = x++;, x++ + x++ | Nunca. |
Quando dá errado
warning: operation on ‘i’ may be undefined [-Wsequence-point]
O que aconteceu: a mesma variável é mudada e usada (ou mudada duas vezes) numa expressão só, como i = i++ ou i++ + i++.
Como resolver: separe em linhas, uma mudança por linha. Para simplesmente somar 1, ++i;.
error: use of an operand of type ‘bool’ in ‘operator++’ is forbidden in C++17
O que aconteceu: ++ ou -- numa variável bool.
Como resolver: atribua o valor que você quer: ligado = true;, ou inverta: ligado = !ligado;.
error: lvalue required as increment operand
O que aconteceu: o ++ foi aplicado a algo que não é uma variável: um número (5++) ou uma conta ((a + b)++).
Como resolver: use + 1 para calcular sem mudar nada, ou aplique o ++ numa variável.
O que aconteceu: você usou o pós (x++) onde queria o valor novo, ou o pré (++x) onde queria o antigo.
Como resolver: separe: faça o ++x; numa linha e use o x na outra, na ordem que você quer.
Referência
| Operador | Muda x para | Entrega | Funciona com |
|---|---|---|---|
++x | x + 1 | o valor novo | inteiros, double, char (não bool, não const) |
x++ | x + 1 | o valor antigo | idem |
--x | x - 1 | o valor novo | idem |
x-- | x - 1 | o valor antigo | idem |
Exercícios
1. Pós-incremento
int a = 7; e depois int b = a++;. Quanto vale b?
2. Pré-decremento
int a = 7; e depois int b = --a;. Quanto vale b?
3. Contagem regressiva
Complete o for para contar de 10 até 1, descendo de 1 em 1:
4. int x = 3; e depois int y = x++ * 2;. Quanto valem x e y?
5. Qual destas linhas é a forma recomendada de somar 1 em i?
6. bool ativo = false; O que acontece com ativo++; compilando com -std=c++23?
Missão final: o painel de senhas
Missão: o painel precisa chamar duas senhas seguidas e mostrar qual é a próxima. Use ++ (pré ou pós, onde quiser) para a saída ficar assim:
Chamando a senha 41 Chamando a senha 42 Próxima: 43
#include <iostream>
int main() {
int senha = 41;
std::cout << "Chamando a senha " << senha << '\n';
std::cout << "Chamando a senha " << senha << '\n';
std::cout << "Próxima: " << senha << '\n';
}
#include <iostream>
int main() {
int senha = 41;
std::cout << "Chamando a senha " << senha << '\n';
++senha;
std::cout << "Chamando a senha " << senha << '\n';
++senha;
std::cout << "Próxima: " << senha << '\n';
}
Resumo
++x/x++somam 1;--x/x--subtraem 1.- Sozinhos na linha, pré e pós são iguais. Dentro de uma expressão:
++xentrega o valor novo;x++entrega o antigo. A variável muda nos dois casos. - O uso mais comum é no
for:for (int i = 0; i < n; ++i). - Funcionam com inteiros,
doubleechar. Não funcionam combool(proibido desde o C++17), comconstnem com números escritos direto. - Não mude e use a mesma variável na mesma expressão:
i++ + i++é comportamento indefinido, ei = i++é indefinido até o C++14 e inútil depois. O g++ avisa com-Wsequence-point.