Introdução a
Ponteiros ou Apontadores
Créditos:
Profa. Karla de Souza - UFTPR
Modificada por
Prof. Marcus Salerno
http://www.dainf.ct.utfpr.edu.br/~karla/
Disciplina IF65A
Por que Estudar Ponteiros?
• É praticamente impossível desenvolver um programa
com algum grau de complexidade ou tamanho
significativo em C e C++ sem o uso de apontadores.
• C é altamente dependente de ponteiros
• Portanto, para ser um bom programador C, é preciso
conhecê-los
• Mas...
O uso descuidado de ponteiros pode levar a sérios bugs e a
dores de cabeça terríveis :-).
E o que são Ponteiros ou
Apontadores?
• Os ints guardam inteiros.
• Os floats guardam números de ponto flutuante.
• Os chars guardam caracteres.
• Ponteiros guardam endereços de memória
Ponteiros em Linguagem C
• Um ponteiro também tem tipo.
• Em C quando declaramos ponteiros nós informamos ao
compilador para que tipo de variável vamos apontá-lo.
• Um ponteiro int aponta para um inteiro, isto é, guarda o
endereço de um inteiro.
Como Usar Ponteiros
• Para declarar:
tipo_do_ponteiro *nome_da_variável;
• É o asterisco (*) que faz o compilador saber que aquela
variável não vai guardar um valor mas sim um endereço
para aquele tipo especificado.
• Exemplos:
– int *pt;
– char *temp,*pt2;
Como Usar Ponteiros
• char *temp,*pt2;
• Estas variáveis foram declaradas, mas não inicializadas.
• Ou seja, estes ponteiros apontam para um endereço
indefinido.
• Este endereço pode ser, por exemplo, uma porção de
memória reservada ao sistema operacional
• Usar um ponteiro sem inicializá-lo pode causar
travamento do computador, ou algo pior!
Como Usar Ponteiros
• Para inicializar um ponteiro podemos igualá-lo a um valor de
memória.
• Mas, como saber a posição na memória de uma variável do
nosso programa?
• Seria muito difícil saber o endereço de cada variável que
usamos, 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 por
nós. Para saber o endereço de uma variável basta usar o
operador &.
• Exemplo:
– float num = 2.5;
– float *pt;
– pt = #
Como Usar Ponteiros
float num = 2.5;
Variável
Valor
Endereço
num
2.5
?
Como Usar Ponteiros
float num = 2.5;
Variável
Valor
Endereço
num
2.5
?
Compilador aloca um endereço
qualquer disponível
Como Usar Ponteiros
float num = 2.5;
Variável
Valor
Endereço
num
2.5
1000
Compilador aloca um endereço
qualquer disponível
num
Memória
2.5
1000
1001
1002
1003
Como Usar Ponteiros
float num = 2.5;
float *pt;
Variável
Valor
Endereço
num
2.5
1000
pt
?
?
num
Memória
2.5
1000
1001
1002
1003
Como Usar Ponteiros
float num = 2.5;
float *pt;
Memória
Variável
Valor
Endereço
num
2.5
1000
pt
?
1001
num
pt
2.5
?
1000
1001
1002
Também feito pelo compilador,
não necessariamente na posição
seguinte.
1003
Como Usar Ponteiros
float num = 2.5;
float *pt;
pt=#
Qual valor terá a variável pt?
Memória
Variável
Valor
Endereço
num
2.5
1000
pt
?
1001
num
pt
2.5
?
1000
1001
1002
1003
Como Usar Ponteiros
float num = 2.5;
float *pt;
pt=#
O endereço da variável num!
Memória
Variável
Valor
Endereço
num
2.5
1000
pt
1000
1001
num
pt
2.5
1000
1000
1001
1002
1003
Como Usar Ponteiros
• Criamos um float com o valor 2.5 e um apontador para
um float identificado como pt.
• A expressão &num nos dá o endereço de num, o qual
armazenamos em pt.
• Agora que pt tem o endereço de num, podemos também
alterar o valor de num usando pt.
Como Usar Ponteiros
• Para tanto vamos usar o operador "inverso" do operador &:
– É o próprio operador *.
• No exemplo acima, uma vez que fizemos pt=&num a
expressão *pt é equivalente ao próprio num.
• Isto significa que, se quisermos mudar o valor de num para
3.5, basta fazer *pt=3.5.
Exemplo 1
// Programa que exemplifica o uso de ponteiros.
int main ()
{
float num,valor;
float *pt;
num=2.5;
pt=#
valor=*pt;
// Pega o endereço de num.
// Atribuído a valor de maneira indireta
printf ("\n%f\n",valor);
printf ("Endereco para onde o ponteiro aponta: %p \n", pt);
printf ("Valor da variavel apontada: %f \n",*pt);
system("PAUSE");
return(0);
}
Especificador de formato para ponteiro
Exemplo 2
// Outro exemplo para ponteiros.
int main ()
{
float num,*pt;
num= 2.5;
pt= #
// Pega o endereco de num
printf ("\nValor inicial: %f \n",num);
*pt=3.5;
// Muda o valor de num de uma maneira indireta
printf ("\nValor final: %f \n",num);
system("PAUSE");
return(0);
}
• Exemplos:
• Variáveis e Apontadores.cpp
• ap_x.c
• Operações com Apontador.cpp
• http://www.youtube.com/watch?v=6pmWojisM_E&feature=pl
ayer_embedded
Exercício
• Procure determinar quais valores são impressos ao final
destes programas. Confira sua resposta testando o programa.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a,b,*c;
a = 4;
b = 3;
c = &a;
*c = *c + 1;
c = &b;
b = b+4;
printf("%d %d
%d",a,b,*c);
system(“PAUSE”);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a,b,*c,*d,*f;
a = 4;
b = 3;
c = &a;
d = &b;
*c = *c / 2;
f = c;
c = d;
d = f;
printf("%d %d",*c,*d);
system(“PAUSE”);
return 0; }
Ponteiros e Vetores
• Vetores (inclusive strings) e matrizes já são, por definição,
ponteiros.
• Por exemplo, uma string pode ser declarada também da
seguinte forma:
char *s = "Programacao";
• O compilador irá criar a variável s, com tipo apontador de
caractere e inicializá-la com o endereço do byte que armazena
o primeiro “P".
• Repare que a declaração "char s[]" é equivalente a "char *s".
Onde Usar Ponteiros?
• Funções
– Passagem de parâmetros por referência
• Alocação Dinâmica
– Não sei o tamanho que o vetor precisa ter….
– Não sei o tamanho que cada string precisa ter…
– Não sei o tamanho que a matriz precisa ter…
Onde Usar Ponteiros?
• Funções
– Passagem de parâmetros por referência
• Alocação Dinâmica
– Não sei o tamanho que o vetor precisa ter….
– Não sei o tamanho que cada string precisa ter…
– Não sei o tamanho que a matriz precisa ter…
Passagem de Parâmetros
Por Valor e por Referência
Passagem por Valor
• Quando chamamos uma função os valores dos
parâmetros passados são copiados para os parâmetros
‘formais’ da função.
•
Isto quer dizer que não são alterados os valores que os
parâmetros têm fora da função.
• Este tipo de chamada de função é denominado
chamada por valor.
•
Isto ocorre porque são passados para a função apenas
os valores dos parâmetros e não os próprios
parâmetros.
Passagem por Valor
int main()
{
int x, y;
// Declaração de funções.
void troca(int, int);
// Chama função para trocar
valores.
x=5;
y=6;
troca(x, y);
printf("O valor de x agora eh %d
e o de y eh %d \n", x, y);
system("PAUSE");
}
void troca(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
Passagem por Valor
• Funcionou? Por que?
• As variáveis a e b da função troca sofrem alterações
dentro da função, mas as variáveis a e b da main não.
• É a passagem por valor.
• Somente os valores são copiados, não havendo
alteração nas variáveis.
Passagem por Referência
• Outro tipo de passagem de parâmetros para uma função
ocorre quando as alterações nos parâmetros dentro
da função (chamados parâmetros formais) alteram
os valores dos parâmetros que foram passados para
a função.
• Este tipo de passagem de parâmetro tem o nome de
“passagem por referência".
• Este nome vem do fato de que, neste tipo de chamada,
não se passa para a função os valores das variáveis,
mas sim suas referências (ou endereços):
– a função usa as referências para alterar os valores
das variáveis fora da função.
Passagem por Referência
• Quando queremos alterar as variáveis que são
passadas para uma função, nós podemos declarar
seus parâmetros como sendo apontadores.
• Os apontadores são a "referência" que precisamos para
poder alterar a variável fora da função.
• O único inconveniente é que, quando usarmos a função,
teremos de lembrar de colocar um & na frente das
variáveis que estivermos passando para a função.
Passagem por Referência
// Declaração de funções.
void troca(int *, int *);
int main()
{
int x, y;
void troca(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
// Chama função para trocar valores
x=5;
Declara os parâmetros da função como ponteiros
y=6;
troca(&x, &y);
printf("O valor de x agora eh %d
e o de y eh %d \n", x, y);
Passa os endereços das variáveis
system("PAUSE");
}
Ver troca de estruturas em: troca_struct.c
Passagem por Referência
• Funcionou agora? Por que?
• Lembra da função scanf()? Das variáveis precedidas
por &?
• Por que a função scanf() precisa usar a chamada por
referência?
• Para alterar o valor das variáveis que passamos para
ela!
Exercício 1
• Escreva uma função que receba duas variáveis inteiras
e "zere" o valor das variáveis.
– Na função principal, declare e inicie as variáveis
inteiras com qualquer valor, chame a função para
zerá-los e imprima os valores finais.
– Use o que você aprendeu para fazer a
implementação.
– Teste a passagem por valor e por referência no
programa.
Exercício 2
• Seja a função troca cujo protótipo está definido abaixo,
uma função que permute o valor de uma variável do tipo
double por outra, ambas passadas por referência.
– Defina uma função main que permute os valores das
variáveis a, b e c, declaradas abaixo, de forma que
no final a<=b<=c. A função main deve chamar a
função troca.
• void troca(double *x,double *y)
Resumo
• O que é um ponteiro?
– É uma variável que armazena o endereço na
memória do computador onde está outra variável
• Operadores relacionados a ponteiros:
* (asterisco): informa que uma variável irá armazenar o
endereço de outra variável (que a variável é um
ponteiro), ou
informa ao computador que você deseja o valor que
está no endereço armazenado;
& (e comercial): retorna o endereço de uma variável;
Resumo
• operador *
– declara-se um ponteiro com *
• int *x;
– acessa-se (alterar, modificar, ler) o valor da variável
apontada também com *
• *x = 10;
// atribui o valor 10 ao local apontado
pelo ponteiro ‘x’
• printf(“%d”, *x);
// imprime valor armazenado no local
apontado por ‘x’
– observação: strings, vetores e matrizes funcionam de
forma diferente: um vetor, string ou matriz é um ponteiro por
definição
• operador &
– acessa (alterar, modificar, ler) o endereço de uma variável
(que é um ponteiro)
Resumo
• Passagem de Parâmetro por Valor
– É a passagem de parâmetro ‘default’ da linguagem C. Se
nada é especificado, somente os valores dos parâmetros
passados são copiados para os parâmetros formais da
função
• Passagem de Parâmetro por Referência
– É a passagem de parâmetro usando ponteiros. Desta forma
os endereços das variáveis é que são passados à função e
todas as alterações feitas nas variáveis passadas por
parâmetro permanecem após o fim da função.
• Declaração: void troca (int *a,int *b);
• Chamada da função: troca (&num1,&num2);
• Definição da função: void troca (int *a, int *b) { \\
Instruções.}
Download

Notas Adicionais – Apontadores