Considere o programa abaixo que pede ao usuário dois inteiros, armazena-os em duas variáveis, troca seus valores, e os imprime.
#include <iostream> using namespace std; int main() { int a, b, temp; cout << "Entre dois numeros: "; cin >> a >> b; cout << "Voce entrou com " << a << " e " << b << endl; /* Troca a com b */ temp = a; a = b; b = temp; cout << "Trocados, eles sao " << a << " e " << b << endl; }
Aqui está um exemplo de execução do programa:
Entre dois numeros: 3 5 Voce entrou 3 e 5 Trocados, eles sao 5 e 3
O seguinte trecho do programa executa a troca de valores das variáveis a e b:
temp = a; a = b; b = temp;
É possível escrever uma função que executa esta operação de troca? Considere a tentativa abaixo de escrever esta função:
#include <iostream> using namespace std; void troca(int x, int y) { int temp; temp = x; x = y; y = temp; } int main() { int a, b; cout << "Entre dois numeros: "; cin >> a >> b; cout << "Voce entrou com " << a << " e " << b << endl; // Troca a com b troca(a, b); cout << "Trocados, eles sao " << a << " e " << b << endl; }
Se você executar este programa, verá que ele não funciona:
Entre dois numeros: 3 5 Voce entrou 3 e 5 Trocados, eles sao 3 e 5
Como você já viu nas notas anteriores, em C++ os argumentos são passados por valor. Uma vez que somente os valores das variáveis são passados, não é possível para a função troca() alterar os valores de a e b porque troca() não sabe onde na memória estas variáveis estão armazenadas. Além disso, troca() não poderia ser escrito usando a sentença return porque podemos retornar APENAS UM valor (não dois) através da sentença return.
A solução para o problema acima é ao invés de passar os valores de a e b, passar uma referência às variáveis a e b. Desta forma, troca() saberia que endereço de memória escrever, portanto poderia alterar os valores de a e b.
Lembre-se que em C++ a cada variável está associado: (i) um nome; (ii) um tipo; (iii) um valor; e (iv) um endereço. Assuma que existam as seguintes definições de variáveis.
int i = 5; char c = 'G';
Na memória, eles podem estar armazenados da forma indicada na Figura 11.1:
A variável inteira i está armazenada no endereço 1342. Ela usa dois bytes de memória (quando um objeto usa mais de um byte, seu endereço é onde ele começa - neste caso, 1342 e não 1343). A variável do tipo char c está armazenada no endereço 1346 e usa um byte de memória. O compilador é que controla do local de armazenamento destas variáveis em memória.
Considere novamente o exemplo da função troca(). Quando a e b são passados como argumentos para troca(), na verdade, somente seus valores são passados. A função não podia alterar os valores de a e b porque ela não conhece os endereços de a e b. Mas se referências para a e b forem passados como argumentos ao invés de a e b, a função troca() seria capaz de alterar seus valores; ela saberia então em que endereço de memória escrever. Na verdade, a função não sabe que os endereços de memória são associados com a e b, mas ela pode modificar o conteúdo destes endereços. Portanto, passando uma variável por referência (ao invés do valor da variável), habilitamos a função a alterar o conteúdo destas variáveis na função chamadora.
A definição da função troca() deve ser alterada, e a lista de parâmetros formais deve ter argumentos não do tipo int, mas referências para int, ou seja, int & . Quando chamamos a função troca(), nós continuamos passando parâmetros reais a e b, mas desta vez, o compilador sabe que o que será passado para a função troca() são as referências a estas variáveis, e não seus valores. Dentro da função troca() não deverá haver mudanças. Uma vez que agora os parâmetros formais são referências, o acesso aos objetos deve ser escrito normalmente, mas deve-se ter em mente que qualquer alteração nos valores dos parâmetros formais da função implica em alterar o valor dos argumentos passados para a função no momento de sua chamada. Assim, a função troca() é capaz de alterar os valores de a e b “remotamente”.
O programa abaixo é a versão correta do problema enunciado para a função troca():
#include <iostream> using namespace std; /* função troca(px, py) * ação: troca os valores inteiros apontados por px e py * entrada: apontadores px e py * saida: valor de px e py trocados na origem da chamada da função * suposições: px e py sao apontadores validos * algoritmo: primeiro guarda o primeiro valor em um temporario e * troca */ void troca(int & px, int & py) { int temp; temp = px; px = py; py = temp; } int main() { int a, b; cout << "Entre dois numeros: "; cin >> a >> b; cout << "Voce entrou com " << a << " e " << b << endl; // Troca a com b -- passa argumentos por referencia troca(a, b); cout << "Trocados, eles sao " << a << " e " << b << endl; }
A saída deste programa é:
Entre dois numeros: 3 5 Voce entrou com 3 e 5 Trocados, eles sao 5 e 3
Basicamente, se a função precisa alterar o valor de uma variável no ponto de chamada da função, então passamos o nome da variável como parâmetro real, e escrevemos a função indicando os parâmetros como sendo de SAìDA ou PASSADOS POR REFERÊNCIA.