Sie sind auf Seite 1von 37

COMPUTAÇÃO

BÁSICA

AULA 14
Ponteiros ou
Apontadores

Vandor Roberto Vilardi Rissoli


APRESENTAÇÃO
• Introdução a Ponteiros
• Parâmetros por Ponteiros
• Aritmética de Ponteiros
• Referências
Ponteiro ou Apontador
Uma das mais poderosas ferramentas disponíveis para o
programador em C é a possibilidade de tratar diretamente a
memória do computador por meio do uso de ponteiros variáveis
e constantes.
Um ponteiro é uma variável que contém um endereço de
memória, assim como uma variável inteira contém um número e
uma variável caractere contém uma letra, um ponteiro contém
um endereço de memória.
A memória do computador é o local onde esses valores são
armazenados. Esta memória é dividida em posições de memória
sequencialmente numeradas, onde cada uma destas posições é
um endereço de memória que armazena informações (variáveis).
Toda variável de todo tipo é posicionada em uma única
posição em um endereço. Observe o exemplo a seguir:
Ponteiro ou Apontador
MEMÓRIA
long int idade; idade
cada posição = 1 byte

10110101 01110110 11110110 11101110

Endereços ou
100 101 102 103 104 105 106 107 108 109 110 111 posições de
memória

Suponha que a variável idade seja do tipo long int que tem seu
tamanho de 4 bytes (por definição), onde o nome da variável aponta
para o primeiro byte. O endereço de idade é 102.
Não importa qual o endereço de cada variável, mas que elas pos-
suem endereço e que a quantidade certa de memória é reservada.
A quantidade certa é identificada pela sua declaração e o
compilador encarrega-se da atribuição do efetivo endereço.
Ponteiro ou Apontador
Toda variável possui um endereço inicial de memória e você
pode armazena-lo em um ponteiro, mesmo sem conhece-lo.
Suponha que as variáveis idade e p_idade tenham sido declaradas
da seguinte forma:
int idade = 20; // variável inteira sendo inicializada com valor 20
int *p_idade = 0; // ponteiro nulo (sendo inicializado com 0)
p_idade = &idade; // atribui o endereço de idade a p_idade

 p_idade pode ser identificado como um ponteiro, pois


apresenta um asterisco entre o seu tipo e o seu identificador.
A última linha do exemplo acima atribui o endereço (&) da
variável idade ao ponteiro p_idade. Caso o operador de endereço
não tivesse sido usado, p_idade receberia o conteúdo da variável
idade (20), estando errado. Estas instruções poderiam ser
simplificadas em:
int idade = 20; // variável inteira sendo inicializada com 20
int *p_idade = &idade; // cria p_idade iniciando com endereço de idade
Ponteiro ou Apontador
O tipo de dado de cada variável indica qual a quantidade de
bytes consecutivos que a mesma ocupa a partir deste
endereço, sendo este endereço fornecido apenas durante a
execução do programa.
No exemplo anterior, usando a variável idade, declarada
como long int, são reservados 4 bytes na memória para o
armazenamento do valor desejado. Esta variável esta
alocando a posição inicial 102 na memória, onde as posições
103, 104 e 105 também serão alocadas, pois este tipo de
dado necessita de 4 bytes para realizar o seu armazenamento
correto.
Apesar da variável idade reservar 4 bytes para seu armaze-
namento, disse que ela tem endereço 102, ou seja, o inicial.
Ponteiro ou Apontador
OPERADOR DE INDIREÇÃO
Este operador também é chamado de derreferenciamento.
Quando um ponteiro é derreferenciado, o valor existente no
endereço armazenado pelo ponteiro é recuperado.

Um ponteiro possibilita um acesso indireto ao valor da


variável cujo endereço é guardado por ele. Para atribuir o valor da
idade para uma nova variável (anos) usando ponteiro teria:
int anos; // declaração de nova variável inteira
anos = *p_idade; // atribui o conteúdo do endereço de p_idade

Este operador (*) é usado de duas maneiras diferentes com os


ponteiros (declaração e derreferenciamento), onde o contexto
identifica qual operação será executada, além da própria
multiplicação convencional que o asterisco faz em C.
Ponteiro ou Apontador
É importante distinguir entre um ponteiro, o endereço que o
ponteiro contém e o valor existente no endereço que o ponteiro
contém (conteúdo). Observe a declaração:
int variavel = 5; //declarando uma variável iniciada com 5
int *p_var = &variavel; //declarando um ponteiro com endereço

A segunda linha declara um ponteiro para um inteiro que é


inicializado com o endereço de variavel. O conteúdo existente
no endereço que p_var aponta é 5, que é o valor armazenado
pela variavel.
variavel p_var endereço do ponteiro
Nome da variável

0000 0000 0000 0000 0000 0110


0000 0101 0000 0000 0000 0101
5 102

100 101 102 103 104 105 106 107 108 109 110 111 posição do endereço
Ponteiro ou Apontador
DECLARAÇÃO DE PONTEIROS
Qualquer tipo de ponteiro pode apontar para
qualquer posição na memória, no entanto, toda a
aritmética de ponteiro é feita por meio do seu tipo de
dado, também denominado tipo base. Logo, é
fundamental a declaração correta do tipo base do
ponteiro para sua posterior manipulação.
<tipo de dado> * <identificador>;
Operador de indireção
tipo de dado = qualquer tipo válido em C, inclusive os
definidos pelo usuário.
identificador = nome da variável ponteiro
 O tipo base do ponteiro define que tipo de variáveis o
ponteiro pode apontar.
Ponteiro ou Apontador
OPERADORES
• OPERADOR & (endereço)
É um operador unário que devolve o endereço de
memória de seu operando (variável ou função).
• OPERADOR * (indireto)
É um operador unário que devolve o valor da variável
localizada no endereço que se aponta. É o complemento
de &. Este operador precedendo o nome do ponteiro
resulta no valor da variável apontada
Estes operadores tem uma precedência maior do que todos
os operadores aritméticos, exceto o menos unário
(negativo).
Ponteiro ou Apontador
Os operadores de endereço e de indireção tem maior
precedência que qualquer operador aritmético exceto sobre
os operadores unários (negativo e positivo).
int main (void)
{ // Declarações
long int val, novo, *p; // define 3 variáveis, sendo 1 ponteiro
// Instruções
val = 30; // atribui a val o valor 30
p = &val; // atribui a p o endereço de memória val
novo = *p; /* atribui a novo o conteúdo do endereço
apontado por p (o mesmo que novo=val) */
*p = 50; // atribui ao endereço apontado por p o
return 0; // valor 50 ou simplesmente val = 50
}
Ponteiro ou Apontador
Como um ponteiro é uma variável as atribuições são
realizadas como qualquer variável comum. Representação
Exemplo: ? x
Previna-se
#include <stdio.h> Seja um adepto da computação p
#include <conio.h> segura: inicialize seus ponteiros! ? x

int main (void) { q

float x, *p=0, *q=0; ou usar macro NULL


40.5 x

x= 40.5; // atribuição a variável x p


p = &x; // atribuição do endereço de x
q = p; // atribuição do conteúdo de p a q q

formato para ponteiro ‘%p’ 40.5 x


printf("Conteudo de p = (%p)", p); p
printf("\nValor de x=(%3.2f)",*q);
getch(); q
return 0; indireção ou derreferenciamento
}
Ponteiro ou Apontador
#include<stdio.h> /*Segunda troca de valores*/
#include<conio.h> // ou conio.c printf("\nAcao = 9\n");
int main (void) agir = 9;
{ // Declarações printf("*p_agir: %d\n",*p_agir);
int agir = 5; printf("Acao: %d",agir);
int *p_agir = 0; getch();
}
// Instruções Saída em tela
/*Demonstração da troca*/
printf("Troca de valores\n\n"); Troca de valores
printf("Acao= %d\n", agir); Acao= 5
p_agir = &agir; *p_agir= 5
printf("*p_agir= %d\n", *p_agir); *p_agir = 7
/*Primeira troca de valores*/ *p_agir: 7
printf("\n*p_agir = 7\n"); Acao: 7
*p_agir = 7; Acao = 9
printf("*p_agir: %d\n", *p_agir); *p_agir: 9
printf("Acao: %d\n", agir); Acao: 9
Ponteiro ou Apontador
Como o exemplo anterior pode demonstrar, não se sabe
qual o endereço, mas pode-se manipulá-lo e ele sempre tratará
diretamente com a variável, por meio do seu endereço de
memória.
Verifique o que aconteceria se no programa anterior fossem
incluídas as instruções:
printf("\nEndereco de Acao: %d", &agir); //endereço de agir
printf("\nEndereco de &p_agir: %d", &p_agir); //endereço de p_agir
printf("\nEndereco de &*p_agir: %d", &*p_agir); //endereço do endereço
printf("\nDerreferenciamento em *p_agir: %d",*p_agir); //oper. indireção
:

Como todas as variáveis, os ponteiros tem um endereço e


um valor. O operador & precedendo o nome do ponteiro resulta
em sua posição na memória. Só seu nome indica o valor
contido nele, no caso o endereço para o qual ele aponta.
 Com o intuito de testar e compreender as instruções acima, coloque-as no final
do programa anterior e solicite a sua execução novamente.
Ponteiro ou Apontador
Os ponteiros são utilizados em situações em que o uso de
uma variável é difícil ou indesejável. Algumas destas razões
são:
 manipular elementos de uma matriz;
 tratar argumentos em funções que alterarão os
argumentos originais (referência);
 passar uma string de uma função para outra, usando-as
como matrizes (strings);
 criar estruturas de dados complexas, como listas
encadeadas e árvores binárias, onde um item deve conter
referências a outro;
 alocar e “desalocar” de memória dinamicamente.
Ponteiro ou Apontador
PASSAGEM DE ARGUMENTOS POR REFERÊNCIA

O operador de referência cria um outro nome para a


variável já existente, e toda operação executada em
qualquer um dos nomes terá o mesmo resultado.
A referência não é uma cópia da variável e sim a
mesma variável com um outro nome. Isso permite o acesso
as variáveis da função “chamadora”, possibilitando que a
função chamada retorne mais que um valor a função
chamadora.
As funções que recebem argumentos por referência
usam o operador & na definição do tipo do argumento
Ponteiro ou Apontador
Observe que a chamada de uma função que recebe uma
referência é idêntica à chamada de uma função que passa
argumentos por valor.
Exemplo: reajuste(preco); // aciona o procedimento
#include<stdio.h> printf("\n\nNovo Preco= %3.2f",preco);
#include<conio.h> getch();
void reajuste(float& val); }
int main (void) { void reajuste(float& val) {
float preco; val *= 1.2;
clrscr(); }
printf("\nInforme o preco atual: ");
scanf("%f",&preco);

A declaração (float& val) indica que val é um outro nome para a


variável passada como argumento pela função chamadora.
Resumidamente, quando usa-se val está realmente usando preco
da função main( ).
Ponteiro ou Apontador
PASSAGEM DE ARGUMENTOS COM PONTEIROS
A terceira maneira pela qual pode-se passar
argumentos para uma função é por meio de ponteiros.
Assim como a passagem por referência, a passagem com
ponteiro também permite a alteração do valor original
enviado a outra função.

a) Primeiro, a função chamadora passará o endereço


(&) da variável desejada.
b) Depois a função chamada deve criar variáveis para
receber estes endereços, onde estas variáveis serão
ponteiros que poderão alterar a variável original.
Ponteiro ou Apontador
Exemplo: (Passagem de argumento simples)
#include <stdio.h>
void imprime (float *p)
#include <conio.h>
{
void imprime (float *p); printf("\tDentro da funcao sem alteracao p =
(%2.2f)\n" , *p);
*p = *p * 2; // valor dobrado
int main (void)
printf("\tAntes de retornar da funcao com
{ alteracao p = (%2.2f)\n", *p);
float valor = 5.12; }
clrscr();
printf("Valor de x = %2.2f - antes da chamada\n", valor);
imprime (&valor);
printf("Valor de x %2.2f - apos a chamada da funcao \n", valor);
getch();
return 0;
}
Ponteiro ou Apontador
Exercício Proposto

1) Utilizando a estrutura de ponteiros vamos elaborar um


programa que calcule o preço final de um produto a
partir de um percentual de ajuste. Os produtos serão
informados enquanto o seu preço não for igual a zero.
Solicite o preço atual do produto e o percentual de
reajuste. Em seguida informe o valor do novo preço
do produto e qual foi o valor de ajuste que ele sofreu.
O cálculo do ajuste e o preço final devem ser
encontrados em uma outra função, que receberá
argumentos por referência.
Ponteiro ou Apontador
ARITMÉTICA DE PONTEIROS
• Existem duas operações que podem ser utilizadas:
adição (+) e subtração (-)
• Cada ponteiro é incrementado ou decrementado de
acordo com o seu tipo de dado
• Cada vez que um ponteiro é incrementado ele
passa a apontar para a próxima posição de
memória do elemento de seu tipo de dado base
• Cada vez que um ponteiro é decrementado ele
passa a apontar para a posição anterior de
memória do elemento de seu tipo de dado base
Ponteiro ou Apontador
Pode-se incrementar ou decrementar um ponteiro por meio
da adição regular ou do operador de incremento. Incrementar ou
decrementar um ponteiro efetua sua movimentação para o
próximo , ou anterior, tipo apontado.
Suponha que p_x é um ponteiro para uma variável int e a
instrução p_x++; seja executada.
O valor de p_x será incrementado de um int (normalmente
2 bytes). Cada vez que p_x é incrementado ou decrementado,
ele apontará respectivamente para o próximo ou anterior int da
memória.
Incrementar um ponteiro é fazer com que seu valor aponte
para a posição de memória igual ao seu valor atual, acrescido do
valor adicionado e multiplicado pelo número de bytes do seu
tipo de dado base.
Ponteiro ou Apontador
Também é possível subtrair ou adicionar números de e para
ponteiros. A instrução p_x = p_y + 3; fará p_x deslocar-se três
inteiros adiante de p_y.
A diferença entre dois ponteiros será expressa em número
de tipo apontado entre eles. Portanto, se p_y tem um valor de
4000 e p_x o valor de 3998, a expressão: p_x – p_y resultará
em 1, quando p_x e p_y forem ponteiros para int (2 bytes para
cada posição válida de int correspondendo só a um valor
válido).
Observe o exemplo:
Imagine um p_h que seja do tipo long int e esteja apontando
para a posição 2000. A operação p_h = p_h + 2 fará com que o
valor de p_h seja alterado para 2000 + (2 * 4), ou seja 2008,
onde 4 é a quantidade de bytes reservados para uma variável do
tipo long int.
Ponteiro ou Apontador
Unidade Adotada em Operações com Ponteiros

A declaração de um ponteiro exigi o tipo de dado que o


ponteiro estará apontando, pois a correta execução das
operações aritméticas dependem desta informação.
int *p_i; A unidade com ponteiros corresponde
ao número de bytes do tipo apontado.
double *p_d;

Se for adicionado 1 a p_i estará se somando dois bytes


(um int) ou se for adicionado 1 a p_d se somarão oito bytes
(um double) e assim por diante, de acordo com o tipo de
dado que o ponteiro estará apontando.
Ponteiro ou Apontador
Exercício Proposto

2) Utilizando uma folha de papel e lápis elabore os


exercícios propostos pelo professor e os prepare
para ser entregue para avaliação.
Siga as instruções de seu professor para que a
elaboração solicitada não seja erroneamente
compreendida e o prejudique em sua avaliação de
exercícios entregues.
Ponteiro ou Apontador
CONVERSÃO PARA PONTEIROS (cast)
Caso seja necessário atribuir uma expressão do tipo
inteiro para um ponteiro (endereço de memória) deve-se
fazer antes a conversão (moldar) do valor resultante para o
tipo que o ponteiro deve apontar:
<ponteiro> = (tipo base *) <expressão ou valor inteiro>;
Exemplo: Conversão ou cast para acertar o tipo de dado
char * p_char; // definição de um ponteiro para caracter
float *p_float; // definição de um ponteiro para float
p_char = (char *) 3000; // atribui ao ponteiro o endereço 3000
p_char += 10; // corresponde a p_char => 3010;
p_float = (float *) (5000 * 2); // atribui ao ponteiro o endereço
10000
p_float += 10; // corresponde a p_float => 10040;
Ponteiro ou Apontador
#include <stdio.h>
int main (void) { ALGUNS MOLDADORES (cast)
char *p_char; // ponteiro para caracter (char *)-transforma um char em ponteiro
int *p_int; // ponteiro para inteiro (int *)-transforma um int em ponteiro
float *p_float; // ponteiro para float (float *)- transforma um float em ponteiro
/* atribui 90 ao ponteiro */
// Apresentando formatos
moldador
p_char =(char *) 90; printf("\np_char %%c = %c", *p_char);
p_char = p_char + 2; printf("\np_int %%d = %d", *p_int);
/* atribui 80 ao ponteiro*/ printf("\np_float %%f = %f", *p_float);
p_int =(int *) 80; printf("\n\nFormato para Ponteiro\n");
p_int += 1; /*p_int => 82; printf("\tponteiro p_char %%p = %p",p_char);
/* atribui 70 ao ponteiro*/ printf("\n\tponteiro p_int %%p = %p",p_int);
p_float =(float *) 70; printf("\n\tponteiro p_float %%p = %p",p_float);
p_float += 2; getch();
/* p_float => 78;*/ }
Ponteiro ou Apontador
DIFERENÇA ENTRE PONTEIROS
A diferença entre 2 ponteiros será calculado como a
diferença entre os seus valores dividido pelo número de bytes do
seu tipo base (operação inversa ao incremento de um ponteiro).

Exemplo:
float *p_a, *p_b;
p_a = (float *) 5000;
p_b = (float *) 4000;
printf("Diferenca entre os ponteiros = %d\n", p_a – p_b);

 o valor 250 será apresentado pelo printf acima, pois é o


resultado de (5000 - 4000) / 4, onde 4 é o número de bytes
usado para armazenar uma variável do tipo float.
Ponteiro ou Apontador
COMPARAÇÕES ENTRE PONTEIROS
Os testes relacionais só podem ser efetuados entre ponteiros
do mesmo tipo.
Um teste condicional com NULL ou zero também podem
ser feitos, contanto que se moldem o tipo:
:
Moldador ou cast (inteiro => ponteiro)
if (p_x = = (int *) 0 )
p_x = &x ;
:

A comparação (>, <, >= ,<= ,= =, !=) entre ponteiros de


tipos diferentes resultaram em respostas sem sentido. A
comparação deve ser feita entre ponteiros do mesmo tipo,
evitando respostas incoerentes e sem sentido (incorretas).
Ponteiro ou Apontador
Para verificar se um ponteiro contém o endereço 0
(zero), que corresponde a um ponteiro que aponte para nulo
(NULL), ou seja, para lugar nenhum (nenhum endereço
válido), pode-se usar as seguintes comparações:
int *p_d;
if ( !p_d )
ou
if ( p_d = = (int *) 0 )
ou
if ( p_d = = NULL)
 A macro NULL pode ser usada para comparar qualquer
tipo de ponteiro (ponteiro de qualquer tipo de dado base).
Ponteiro ou Apontador
#include <stdio.h>
#include <conio.h> switch (opcao)
#define TAM 100 { case '1':
void coloca(int num); printf("Informe o valor: ");
int retira(void); scanf ("%d",&valor);
int *base, *topo, pilha[TAM]; coloca(valor);
int main (void) break;
{ // declarações case '2':
char opcao; valor = retira( );
int fim, valor; if (valor >= 0)
//instruções printf("Retira=%d\n",valor);
base = pilha; // &pilha[0] break;
topo = base; case '3':
fim = 0; printf(" encerra programa\n");
while (!fim) fim = 1;
{ clrscr(); } // fim do switch
printf("Digite 1-Colocar, } // fim do while
2-Retirar e 3-Terminar\n"); getch();
opcao = getche(); } // fim principal,ver outras funções >
Ponteiro ou Apontador
// Corpo das funções
void coloca (int num) int retira ( )
{ {
if (topo == (base + TAM)) if (topo == base)
{ { printf("Pilha vazia");
printf("Pilha cheia"); return (-1) ;
return; }
} else
else { topo--;
{ return (*(topo));
*topo = num; }
topo++; }
}
}
Ponteiro ou Apontador
PONTEIROS X MATRIZES
Existe uma estreita relação entre matrizes e ponteiros.
O ponteiro para uma matriz é gerado especificando o nome
da matriz sem índices, o que indica somente o endereço
inicial da matriz.
Exemplo A: Exemplo B:
char nome[100], *p_nome; int x[10], *p, i;
int nro[100], *p_nro; for (i=0;i<10;i++)
p_nome=nome; // mesmo &nome[0] x[i] = i * 5;
/* nome[4] corresponde a
p = x; // ou também p = &x[0];
*(p_nome+4) ou p_nome[4] */ /* com isso p e x apontam para o
printf(“nome = %s => %s \n”, mesmo endereço */
nome, p_nome); for (i=0;i<10;i++)
p_nro = nro; // igual p_nro=&nro[0]
/* nro[50] corresponde a
printf("%d=%d\n",p[i], *(p+i));
*(p_nro+50) ou p_nro[50] */ // apresentará os valores de x
Ponteiro ou Apontador
PONTEIROS E MATRIZES
Como qualquer outro tipo de dado pode ser criada uma
matriz de ponteiros, ou seja, uma estrutura de dados
homogênea que armazene vários ponteiros.
Observe os exemplos a seguir:
Exemplo A: Exemplo B:
int *vetor[10], num; int aux = 0;
num = 5; static char *msg[] = {"ERRO"};
vetor[2] = &num; printf("mensagem= %s\n",msg[aux]);
printf("Numero=%d", vetor[2]);
Exercício de Fixação
3) Desenvolva um programa que armazene o valor do salário de
três funcionários e apresente-os em ordem decrescente.
Estes valores deverão ser lidos na função principal e caso
estejam fora de ordem devem ser ordenados pela função
Troca que só receberá ponteiros como parâmetros. A função
principal deverá apresentar os salários ordenados antes de
encerrar o programa.
4) Elabore um programa que receberá as temperaturas médias
diárias do mês de fevereiro, onde o principal deverá solicitar
o ano do mês de análise e verificar se o mesmo é bissexto ou
não. Esta verificação é realizada pela função bissexto este
ano seja bissexto serão solicitados 29 dias de temperatura,
caso contrário somente 28. Apresente por meio da função
analisaTemperatura qual foi a maior temperatura
encontrada e quais foram os dias em que ela ocorreu. Esta
solução só poderá passar parâmetros por referência, sendo
um ano bissexto se o mesmo for divisível por 4 e não
divisível por 100 ou for divisível por 400.
Exercício de Fixação
5) Desenvolva um programa que armazene o nome de um
funcionário, seu salário e o tempo de serviço dele nesta
empresa. O nome do departamento que ele trabalha
também é importante na identificação das atividades que
ele realiza, por isso este nome também deve ser solicitado
ao usuário. Apresente todos os dados informados em uma
tela limpa, montando uma ficha cadastral. Uma outra
função deve ser acionada para apresentar estes dados
recebendo somente parâmetros passados por referência
(ponteiro), sem o uso de nenhuma variável global. Os
funcionários que tiverem 10 anos ou mais de trabalho
devem ser notificados de uma bonificação de 10% do seu
salário. Esta informação deve também ser apresentada na
ficha cadastral do funcionário. Os dados deverão ser
informados enquanto o usuário desejar e a ficha será
apresentada para cada um deles.
Referências de Criação para Apoio ao Estudo
Material para Consulta e Apoio ao Conteúdo
• SCHILDT, H., C Completo e Total, Editora Makron Books
do Brasil Editora Ltda, 1996.
 Capítulo 5
• EVARISTO, J., Aprendendo a programar programando em
C, Book Express, 205 p., 2001.
 Capítulo 5
• MIZRAHI, V. V., Treinamento em Linguagem C, Curso
Completo, Módulo 2, Makron Books do Brasil Editora
Ltda,1990.
 Capítulo 8
• Universidade de Brasília (UnB FGA)
 cae.ucb.br/conteudo/unbfga
(escolha disciplina Computação Básica no menu superior)

Das könnte Ihnen auch gefallen