Você está na página 1de 31

Estrutura de Dados

Aula 07 – Ponteiros (Apontadores)

Parte I

1
Ponteiro

 A uma variável x como declarada a seguir, tem a ela


associadas as seguintes informações:

2
Ponteiro

 A memória é composta por uma sequência de bytes, onde


cada byte é identificado por um endereço numérico único.
 Cada objeto (variáveis, strings, vetores etc.) que está na
memória ocupa um certo nº de bytes ou seja possui um
endereço.

3
Ponteiro

 Os ints guardam inteiros. Os floats guardam números de


ponto flutuante. Os chars guardam caracteres.
 Ponteiros guardam endereços de memória.
 Isto é semelhante a: quando você anota o endereço de um
colega, você está criando um ponteiro. O ponteiro é este
seu pedaço de papel. Ao anotar o endereço de um colega,
isto servirá para achar este colega.
 É desta mesma forma que o C funciona. Você anota o
endereço de algo em uma variável ponteiro para depois
usar.
 Assim como uma agenda, onde são guardados endereços
de vários amigos, essa agenda poderia ser vista como
sendo uma matriz de ponteiros no C.
 Logo, um ponteiro é uma variável que contém o endereço
de outra variável.
4
Ponteiro

 Um ponteiro proporciona um modo de acesso a uma


variável sem referi-la diretamente (modo indireto).
 Seu valor indica em que parte da memória uma outra
variável está alocada, não o que está nela armazenado.
 Algumas razões para o uso de ponteiros:
Manipular elementos de matrizes;
Receber argumentos em funções que necessitem
modificar o argumento original;
Passar strings de uma função para outra;
Criar estrutura de dados complexas, como listas
encadeadas e árvores binárias, em que um item deve
conter referências de outro;
Alocar e desalocar memória do sistema;
Passar para uma função o endereço de outra.

5
Ponteiro

 Por ser uma variável, todo ponteiro também tem tipo.


 No C, no momento da declaração do ponteiro devemos
informar ao compilador para que tipo de variável vamos
apontá-lo.
 Um ponteiro int aponta para um inteiro, isto é, guarda o
endereço para um inteiro.
 Sintaxe para declaração de um ponteiro:
tipo_do_ponteiro *nome_da_variável;

 Neste caso, o asterisco * é um operador unário que precede


o nome da variável e identifica ao compilador que a variável
não vai guardar um valor qualquer, mas sim que é um ponteiro
que guarda um endereço para aquele tipo especificado.

6
Ponteiro

 Exemplos:

7
Ponteiro

 Há vários tipos de ponteiro:


 ponteiros para caracteres;
 ponteiros para inteiros;
 ponteiros para ponteiros para inteiros;
 ponteiros para vetores;
 ponteiros para estruturas.
 Mas como inicializar um ponteiro já que ele guarda um
endereço de memória?
 Para atribuir um valor/endereço a um ponteiro recém-
criado podemos igualá-lo a um valor/endereço de memória.
 Mas, como saber a posição na memória de uma variável
do programa? Seria muito difícil saber o endereço de cada
variável usada, mesmo porque estes endereços são
determinados pelo compilador na hora da compilação e
realocados na execução. Podemos então deixar que o
compilador faça este trabalho.
8
Ponteiro: inicialização

 Para saber o endereço de uma variável basta usar o


operador &.
 Exemplo:
int count = 10;
int *pt;
pt = &count; //ponteiro pt, recebe o endereço de
count

 criamos um inteiro count de valor 10 e um apontador para


um inteiro que é pt. A expressão &count nos dá o endereço de
count, o qual armazenamos em pt.
 Repare que não alteramos o valor de count, que continua 10.
Ao colocarmos um endereço em pt, ele está agora "liberado"
para ser usado. Se alterarmos o valor de count, pt fará
referência a esse valor modificado, mas o endereço contido
nessa variável ponteiro (para qual aponta) continuará o
mesmo.
9
Ponteiro: inicialização

 Para imprimir o endereço de uma variável qualquer na


tela utilizamos o formatador %p.
 Um ponteiro pode ser inicializado com NULL (não contém
nenhum endereço), quando não aponta para nenhuma
variável.
 NULL é uma constante definida na biblioteca stdlib.h.
 Ex:
char *p; //declara um ponteiro p para string
p = NULL; //p não aponta para nenhum endereço

10
Ponteiro: manipulação

 No exemplo:
int count = 10;
int *pt;
pt = &count; //pt, recebe o endereço de count
 Após essa indicação de pt=&count, podemos alterar o
valor de count usando pt.
 No exemplo acima, uma vez que fizemos pt=&count;
podemos utilizar a expressão *pt como equivalente para
manipular o conteúdo da variável count indiretamente.
 Se quisermos mudar o valor de count para 12, basta
acrescentar a seguinte linha:
*pt =12; //altera o conteúdo apontado por pt.
 O que acabamos de fazer foi uma alteração indireta do
conteúdo da variável count.
 Logo, o operador "inverso" de & é o operador *.
11
Ponteiro: manipulação

 Ex2:

 Nessa última linha do exemplo, a variável y receberá o


conteúdo apontado pelo ponteiro px, ou seja, o valor 8.
Pois, y foi declarado como inteiro.

12
Ponteiro: manipulação
 Ex:main()

13
Ponteiro perdido

 Se uma variável irá conter um ponteiro, então ela deve


ser declarada como tal.
 Ex:

int *pt, *pt2; //declara 2 ponteiros para um inteiro


char *temp; //declara um ponteiro para caracteres.

 No exemplo acima, foram declarados alguns ponteiros,


mas não foram inicializados. Isto significa que eles
apontam para um lugar indefinido. Este lugar pode estar,
inclusive, por exemplo, na porção da memória reservada
ao SO.

14
Ponteiro perdido

 Este erro é chamado de ponteiro perdido e é um dos


mais difíceis de se encontrar, pois a cada vez que a
operação com o ponteiro é utilizada, poderá estar sendo
lido ou gravado em posições desconhecidas da memória.
Isso pode acarretar em sobreposições sobre áreas de
dados ou mesmo área do programa na memória.
 Usar o ponteiro nestas circunstâncias pode levar a um
travamento do micro, ou a algo pior.
 O ponteiro deve ser inicializado (apontado para algum
lugar conhecido) antes de ser usado mesmo que ele não
aponte para “ninguém”, ou seja, NULL.

15
Exemplos:

16
Operações com ponteiros

 Igualar dois ponteiros:


 se temos dois ponteiros p1 e p2, de mesmo tipo,
podemos igualá-los (fazer uma atribuição) fazendo
p1=p2;. Isto significa que p1 aponta para o mesmo
endereço que p2, pois ambos guardam o mesmo
endereço.
 Se quisermos que a variável apontada por p1 tenha o
mesmo conteúdo da variável apontada por p2 devemos
fazer *p1=*p2; Nesta operação não significa que p1 e p2
estejam apontando para o mesmo endereço!.

17
Operações com ponteiros

 Incremento:
 quando incrementamos um ponteiro ele passa a
apontar para o próximo endereço de acordo com o tipo
para o qual o ponteiro aponta.
 Isto é, se temos um ponteiro para um inteiro e o
incrementamos ele passa a apontar para o próximo
inteiro. Se você incrementa um ponteiro char* ele anda
1 byte na memória e se você incrementa um ponteiro
double* ele anda 8 bytes na memória. O decremento
funciona semelhantemente.

18
Operações com ponteiros - Exemplo

19
Operações aritméticas com ponteiros

 Um conjunto limitado de operações aritméticas pode


ser realizado com ponteiros.
 No caso de uma operação de adição ou subtração com
ponteiro estaremos na verdade manipulando para
endereços mais acima ou mais abaixo que o endereço
atual.

20
Operações aritméticas com ponteiros

 Se p é um ponteiro, as operações de incremento e decremento são:


p++;
p--;
ATENÇÃO! As operações acima estão sendo realizadas sobre os
endereços referenciados pelos ponteiros e não sobre os conteúdos
das variáveis para as quais os ponteiros apontam.
 Os incrementos e decrementos dos endereços possuem
procedência sobre o * e sobre as operações matemáticas e são
avaliados da direita para a esquerda. Ex:

O incremento ou decremento de ponteiros está diretamente


relacionado ao tipo de dado do ponteiro. No exemplo acima, se p é
um ponteiro para inteiro (ou seja, int *p;), e p armazena o endereço
100, então p++ faz p armazenar o endereço 104 (pois um inteiro ocupa
4 bytes).
21
Operações com ponteiros

Com a utilização de parênteses o conteúdo que é apontado é


incrementado, porque os operadores * e ++ são avaliados da
direita para esquerda, sem eles o px (seu endereço) seria
incrementado.
 O operador * tem maior precedência que as operações
aritméticas, a expressão abaixo pega o conteúdo do endereço
que px aponta e soma 1 ao seu conteúdo.
y = *px+1;

 Se tivermos y=*(px+1);, então, somente o ponteiro


(endereço) será incrementado e o conteúdo da próxima
posição da memória será atribuído a y.
 Soma e subtração de inteiros com ponteiros, na verdade
também é um incremento e decremento, respectivamente.
Para incrementar um ponteiro em 15 posições (endereço),
basta fazer: p=p+15; ou p+=15;

22
Operações aritméticas com ponteiros

 Usar o conteúdo do apontado pelo ponteiro 15 posições


adiante, basta fazer: *(p+15); A subtração funciona da mesma
maneira.
 Comparar dois ponteiros:
 Podemos saber se dois ponteiros são iguais ou
diferentes (== e !=). Neste caso, lembre-se que estaremos
comparando os endereços que eles se referenciam, ou
seja, os endereços que estão dentro dos ponteiros.
23
Operações aritméticas com ponteiros

 No caso de operações do tipo >, <, >= e <= :


 estamos comparando qual ponteiro aponta para uma
posição mais alta na memória. Então, uma comparação
entre ponteiros pode nos dizer qual dos dois está "mais
adiante" na memória.
 A comparação entre dois ponteiros se escreve como a
comparação entre outras duas variáveis quaisquer:
p1>p2
 Há entretanto operações que você não pode efetuar em um
ponteiro.
 Você não pode:
 dividir ou multiplicar ponteiros;
 adicionar dois ponteiros;
 adicionar ou subtrair floats ou doubles de ponteiros.

24
Ponteiro manipulação - Exemplo

25
Ponteiro manipulação

26
Ponteiro para ponteiro

 Um ponteiro para um ponteiro é uma forma de indicação


múltipla.
 Em um ponteiro normal, o valor do contido no ponteiro
é o endereço que aponta para o valor desejado.
 Quando se tem ponteiro para ponteiro, o primeiro
ponteiro contém o endereço do segundo ponteiro, que
aponta para a variável que contém o valor desejado.
 Exemplo:

float **b; //b é um ponteiro para um ponteiro float.

27
Ponteiro para ponteiro

 Exemplo:

28
Ponteiro para ponteiro

 No C podemos declarar ponteiros para ponteiros para


ponteiros, ou então, ponteiros para ponteiros para ponteiros
para ponteiros e assim por diante.
 Para fazer isto basta aumentar o número de * na declaração.
A lógica é a mesma. Para acessar o valor desejado apontado
por um ponteiro para ponteiro, o operador asterisco deve ser
aplicado duas vezes ou mais vezes. Exemplo:

29
Exercícios

30
Exercícios

31

Você também pode gostar