Agnor's HQ

Aula 11 - Referências e Apontadores

Conceitos-Chave:

- apontador, variável que aponta para outra variável, contendo o endereço desta
- endereço, local de uma variável na memória. Pensem como se fosse a morada de uma variável no computador
- referência, o "nickname" de uma variável
- operador endereço de (&), serve para obter o endereço de uma variável
- operações com o valor de de variáveis apontadas por apontadores, como aceder ao valor de outra variável, através do uso de apontadores

Conceito de endereço e de valor - revisão

Na aula anterior (em que falei de memória) já dei uma breve explicação de endereço e de valor de uma variável. Nesta aula vou rever os conceitos, pois para aprenderem apontadores e referências é muito mais fácil se tiverem estes conceitos em mente. Se tivessemos um programa a correr, a memória pareceria alguma coisa como:

Valor                            
Endereço 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177

Se o programa encontrasse uma linha de código do género: int x = 25; o programa iria introduzir o valor 25 num endereço aleatório:

Valor 25                          
Endereço 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177

Apontadores - breve explicação

Apontadores são uma variável como qualquer outra só que, em vez de conterem valores, contêm endereços de variáveis. Os apontadores são definidos através de um * (asterisco) antes da variável ( int *pointer; ).

Há várias utilidades para os apontadores. Nesta lição vou só ensinar algumas operações básicas com apontadores (nomeadamente com o valor da variável apontada).

Segue-se, por enquanto, uma explicação sobre referências e o operador endereço de (que irá ser importante para o uso com apontadores)

Operador referência (&) e operador endereço de (&)

Se leram o título acima devem pensar que estou enganado, pois os dois operadores são representados com o mesmo símbolo. Não é erro e os dois operadores são mesmo representados pelo mesmo símbolo (&) e a única forma de distinguirmos um do outro é pelo contexto, mas não se preocupem porque, como vão ver, isso é muito simples.

Comecemos pelo operador referência. Quando o símbolo & é utilizado ao definirmos uma variável esta, em vez de ser uma variável normal, irá passar a ser uma referência para outra variável, ou seja, irá ser como um "nickname" (alcunha) para esta:

#include <iostream>
using namespace std;

int main()
{
    int i;
    int &i_ref = i;

    i = 20;

    cout << "Variavel i: " << i << endl;
    cout << "Referencia para i: " << i << endl;

    i_ref = 10;

    cout << "\nVariavel i: " << i << endl;
    cout << "Referencia para i: " << i << endl;

    cin.get();
    return 0;
}

Exemplo 11.1 - Usando Referências

Nota:

Quando declaramos uma referência temos que atribuir logo a variável que queremos referenciar:

int &ref;
ref = i; // errado!
---
int &ref = i; //certo!

Como podem ver pelo programa tanto i como i_ref possuem os mesmos valores e, se alterarmos o valor a uma delas, a outra irá necessariamente ser alterada para o mesmo valor. Como já devem ter percebido não há muitas vantagens na utilização de referências neste caso, mas se as utilizarmos como argumentos para funções, verão a sua verdadeira utilidade:

#include <iostream>
using namespace std;

// Função Swap
// Desc: troca os valores de 2 variáveis inteiras

void swap(int &x, int &y); //passamos os argumentos como referência

int main()
{
    int a = 20;
    int b = 10;

    cout << "A:" << a << endl;
    cout << "B:" << b << endl;

    swap(a, b);

    cout << "\n\nA:" << a << endl;
    cout << "B:" << b << endl;

    cin.get();
    return 0;
}

void swap(int &x, int &y)
{
    int temp;   //variável temporária, para não perdermos o valor do x
    temp = x;
    x = y;
    y = temp;
}

Exemplo 11.2 - Função Swap

Resumindo, para que possamos alterar os 2 valores em simultâneo (sem recurso a variáveis globais) usamos referências. Poderíamos usar variáveis globais para isso, mas com projectos enormes teríamos uma lista enorma de variáveis globais.

Agora vou discutir o uso do operador endereço de.

Operador endereço de (&)

O operador endereço de (&) serve para obter o endereço de uma variável. Vejamos o seguinte programa:

#include <iostream>
using namespace std;

int main()
{
    int var = 30;

    cout << "Endereco da variavel var: " << &var << endl;
    cout << "Valor da variavel var: " << var << endl;

    cin.get();
    return 0;
}

Exemplo 11.3 - Usando o operador endereço de (&)

Como podem observar, se correrem o programa, irão obter um número estranho quando fazemos &var. Esse número é o endereço da variável var na memória e está expresso numa base hexadecimal (por exemplo: 0x22ff31).

Falta dizer que se quiserem guardar o endereço de uma variável terão que o guardar através de um apontador. Assim:

int x = 10;
int v = &x;   //errado
int *p= &x;   //correcto

Operações com apontadores - introdução

Para demonstrar uma introdução às operações com apontadores segue-se um programa de exemplo:

#include <iostream>
using namespace std;

int main()
{
    int x = 20;
    int *pointer;   //apontador para uma variável do tipo int

    pointer = &x;   //o apontador assume o valor do endereço da variável

    cout << "Variavel x:" << endl;
    cout << "\nValor de x: " << x << endl;
    cout << "Endereco de x: " << &x << endl;

    cout << "\n\nVariavel pointer:" << endl;
    cout << "\nValor de pointer: " << pointer << endl;
    cout << "Endereco de pointer: " << &pointer << endl;
    cout << "Valor da variavel apontada por pointer (x): " << *pointer << endl;

    cin.get();
    return 0;
}

Exemplo 11.4 - Obter as propriedades da memória de uma variável e de um apontador

Não se assustem para já com a quantidade de coisas que ainda não sabem! Ao correr o programa deu-me o seguinte output (que será diferente dos vossos, uma vez que o endereço não vai ser o mesmo):

Output

Variavel x:

Valor de x: 20
Endereco de x: 0x22ff74


Variavel pointer:

Valor de pointer: 0x22ff74
Endereco de pointer: 0x22ff70
Valor da variavel apontada por pointer (x): 20

Para melhor compreenderem é melhor "desmontarmos" o programa :

Variável X:

Valor: 20
Endereço: 0x22ff74 (neste caso)

Valor 20 -- --
Endereço 0x22ff74 0x22ff75 ...

Variável pointer (o apontador para a variável x):

Valor: 0x22ff74 (o valor de um apontador é, então, o endereço da variável por ele apontada)
Endereço: 0x22ff70 (o endereço de pointer. Reparem que está 4 "posições" antes do que X; isto acontece porque um int ocupa 4 bytes)
Valor da variável apontada por pointer (x): 20

Valor 0x22ff74 ... 20
Endereço 0x22ff70 ... 0x22ff74

Como vêm é importante testarmos os exemplos por nós mesmos, alterando os valores e experimentando coisas novas, pois é com os erros que aprendemos.

Operações com apontadores - operações com o valor

Há dois tipos de operações com apontadores. Nesta lição vou só dar as operações com o valor, visto que é o mais fácil de aprender (e porque isto já vai muito longo :).

De modo a "mexermos" com o valor da variável apontada pelo apontador temos que utilizar o operador desreferência (*):

#include <iostream>
using namespace std;

int main()
{
    int x = 10;
    int *pointer = &x;

    cout << "Valor de X: " << x << endl;
    cout << "Valor apontado por pointer: " << *pointer << endl;

    x += 10;

    cout << "\nValor de X: " << x << endl;
    cout << "Valor apontado por pointer: " << *pointer << endl;

    *pointer += 10;

    cout << "\nValor de X: " << x << endl;
    cout << "Valor apontado por pointer: " << *pointer << endl;

    ++*pointer; //ou ++(*pointer) se quiserem

    cout << "\nValor de X: " << x << endl;
    cout << "Valor apontado por pointer: " << *pointer << endl;

    cin.get();
    return 0;
}

Exemplo 11.5 - Operações básicas com o valor das variáveis apontadas pelos apontadores
Nunca se esqueçam de usar o operador desreferência sempre que quiserem trabalhar com o valor. Muitos dos erros que assombram os programadores é através de mau uso de apontadores, por isso, tenham muito, mas muito cuidado :) .

Vejamos agora como ficaria a função swap com apontadores:

#include <iostream>
using namespace std;

// Função Swap -> uso de apontadores
// Desc: troca os valores de 2 variáveis inteiras

void swap(int *x, int *y); //passamos os argumentos como apontadores

int main()
{
    int a = 20;
    int b = 10;

    cout << "A:" << a << endl;
    cout << "B:" << b << endl;

    swap(&a, &b); /* não se esqueçam que estamos a utilizar apontadores, logo
                     temos que utilizar o operador endereço de para atribuir o
                     endereço das variáveis aos apontadores */

    cout << "\n\nA:" << a << endl;
    cout << "B:" << b << endl;

    cin.get();
    return 0;
}

void swap(int *x, int *y)
{
    int temp;   //variável temporária, para não perdermos o valor do x
    temp = *x;  //temp é igual ao valor da variável apontada por x
    *x = *y;    //valor da variável apontada por x vai ser igual ao valor da
                //variável apontada por y
    *y = temp;  //valor da variável apontada por y vai ser igual ao valor de temp
}

Exemplo 11.6 - Função Swap (usando apontadores)

Para quê isto tudo?

Se leram isto tudo devem estar a perguntar-se para que é que servem realmente os apontadores, pois existem referências que, além de serem muito mais fáceis de usar e de compreender, têm exactamente o mesmo efeito. Não vos culpo. Há várias razões:

1 - As referências são uma evolução do uso dos apontadores. Em C referências não existiam era tudo feito através de apontadores (até aqui vocês continuam com razão...)

2 - A verdadeira utilidade dos apontadores em C++ (e acreditem que são mesmo úteis) está com a memória dinâmica e com as operações com o endereço, algo que optei não dar nesta lição (por falta de espaço e porque exige mais conhecimentos).

3 - Ficaram com uma excelente introdução aos apontadores. Estes conhecimentos de memória e apontadores vão ser muito importantes para compreenderem a 100% o uso de apontadores

Final da aula 11 de C++:

Próxima aula -> C++ 12 - Tratamento de Caracteres

Fazer o download do código fonte dos exemplos da aula WinZip
Fazer o download da aula em PDF (Brevemente)

Ir para o topo da página

Aulas de C++

Anúncios