Sumário 1 Ponteiros

Propaganda
Instituto Federal de Santa Catarina - Câmpus Chapecó
Ensino Médio Integrado em Informática - Módulo IV
Unidade Curricular: Programação Estruturada
Professora: Lara Popov Zambiasi Bazzi Oberderfer
Sumário
1 Ponteiros.......................................................................................................................................................1
1.1 Endereços.............................................................................................................................................1
1.2 Declarando Ponteiros............................................................................................................................2
1.3 Manipulação de Ponteiros....................................................................................................................2
1.4 Expressões com Ponteiros.....................................................................................................................2
1.5 Ponteiros para ponteiros.......................................................................................................................3
1.6 Problemas com ponteiros.....................................................................................................................4
1.7 Exemplos de Ponteiros..........................................................................................................................4
1.8 Aplicação...............................................................................................................................................4
2 Vetores e endereços.....................................................................................................................................5
3 Alocação dinâmica de memória....................................................................................................................6
4 Exercícios......................................................................................................................................................6
5 Referências Bibliográficas.............................................................................................................................7
1 Ponteiros
É uma variável que contém o endereço de outra variável. Os ponteiros são utilizados para alocação
dinâmica, podendo substituir matrizes com mais eficiência. Também fornecem a maneira pelas quais
funções podem modificar os argumentos chamados, como veremos no capítulo de funções.
Sintaxe:
tipo *nome_variavel;
1.1 Endereços
A memória de qualquer computador é uma sequência de bytes. Cada byte armazena um de 256
possíveis valores. Os bytes são numerados sequencialmente. O número de um byte é o seu endereço
(=address).
Cada objeto na memória do computador ocupa um certo número de bytes consecutivos. No meu
computador, um char ocupa 1 byte, um int ocupa 4 bytes e um double ocupa 8 bytes.
Cada objeto na memória do computador tem um endereço. Na maioria dos computadores, o
endereço de um objeto é o endereço do seu primeiro byte. Por exemplo, depois das declarações
char c;
int i;
struct {
int x, y;
} ponto;
int v[4];
os endereços das variáveis poderiam ser:
c
89421
i
89422
ponto
89426
1
v[0]
89434
v[1]
89438
v[2]
89442
(O exemplo é fictício). O endereço de uma variável é dado pelo operador &. (Não confunda esse
uso de "&" com o operador lógico and, que em C se escreve "&&".) Se i é uma variável então &i é o seu
endereço. No exemplo acima, &i vale 89422 e&v[3] vale 89446.
Exemplo: O segundo argumento da função de biblioteca scanf é o endereço da posição na
memória onde devem ser depositados os objetos lidos no dispositivo padrão de entrada:
int i;
scanf( "%d", &i);
1.2 Declarando Ponteiros
Um ponteiro (apontador / pointer) é um tipo especial de variável que armazena endereços. Se uma
variável conterá um ponteiro, então ela dever ser declarada como tal:
int x,*px;
px=&x; /*a variável px aponta para x */
Se quisermos utilizar o conteúdo da variável para qual o ponteiro aponta:
y=*px;
O que é a mesma coisa que:
y=x;
1.3 Manipulação de Ponteiros
Desde que os pointers são variáveis, eles podem ser manipulados como as variáveis podem. Se py é
um outro ponteiro para um inteiro então podemos fazer a declaração:
py=px;
Exemplo:
int main(){
int x,*px,*py;
x=9;
px=&x;
py=px;
printf("x= %d\n",x);
printf("&x= %d\n",&x);
printf("px= %d\n",px);
printf("*px= %d\n",*px);
printf("*px= %d\n",*px);
return 0;
}
1.4 Expressões com Ponteiros
Os ponteiros podem aparecer em expressões, se px aponta para um inteiro x, então *px pode ser
2
utilizado em qualquer lugar que x seria. O operador * tem maior precedência que as operações aritméticas,
assim a expressão abaixo pega o conteúdo do endereço que px aponta e soma 1 ao seu conteúdo.
y=*px+1;
No próximo caso somente o ponteiro será incrementado e o conteúdo da próxima posição da
memória será atribuído a y:
y=*(px+1);
Os incrementos e decrementos dos endereços podem ser realizados com os operadores ++ e --, que
possuem procedência sobre o * e operações matemáticas e são avaliados da direita para a esquerda:
*px++; /* sob uma posição na memória */
*(px--); /* mesma coisa de *px-- */
No exemplo abaixo os parênteses são necessários, pois sem eles px seria incrementado em vez do
conteúdo que é apontado, porque os operadores * e ++ são avaliados da direita para esquerda.
(*px)++ /* equivale a x=x+1; ou *px+=1 */
Exemplo:
int main() {
nt x,*px;
x=1;
px=&x;
printf("x= %d\n",x);
printf("px= %u\n",px);
printf("*px+1= %d\n",*px+1);
printf("px= %u\n",px);
printf("*px= %d\n",*px);
printf("*px+=1= %d\n",*px+=1);
printf("px= %u\n",px);
printf("(*px)++= %d\n",(*px)++);
printf("px= %u\n",px);
printf("*(px++)= %d\n",*(px++));
printf("px= %u\n",px);
printf("*px++-= %d\n",*px++);
printf("px= %u\n",px);
return 0;
}
1.5 Ponteiros para ponteiros
Um ponteiro para um ponteiro é uma forma de indicação múltipla. Num ponteiro normal, o valor
do ponteiro é o valor do endereço da variável que contém o valor desejado. Nesse caso o primeiro ponteiro
contém o endereço do segundo, que aponta para a variável que contém o valor desejado.
float **balanço;
//balanço é um ponteiro para um ponteiro float.
Exemplo:
3
int main() {
int x,*p,**q;
x=10;
p=&x;
q=&p;
printf("%d",**q);
return 0;
}
1.6 Problemas com ponteiros
O erro chamado de ponteiro perdido é 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.
int,*p;
x=10;
*p=x;
Estamos atribuindo o valor 10 a uma localização desconhecida de memória. A consequência desta
atribuição é imprevisível.
1.7 Exemplos de Ponteiros
Suponha que a, b e c são variáveis inteiras. Eis um jeito bobo de fazer "c=a+b":
int
int
p =
q =
c =
*p;
*q;
&a;
&b;
*p + *q;
/* p é um ponteiro para um inteiro */
/* o valor de p é o endereço de a */
/* q aponta para b */
Outro exemplo bobo:
int
int
p =
r =
c =
*p;
**r;
&a;
&p;
**r + b;
/* r é um ponteiro para um ponteiro para um inteiro */
/* p aponta para a */
/* r aponta para p e *r aponta para a */
1.8 Aplicação
Suponha que precisamos de uma função que troque os valores de duas variáveis inteiras, digamos i
e j. É claro que a função
void troca( int i, int j) /* errado! */
{
int temp;
temp = i; i = j; j = temp;
}
não produz o efeito desejado, pois recebe apenas os valores das variáveis e não as variáveis propriamente
ditas. A função recebe "cópias" das variáveis e troca os valores dessas cópias, enquanto as variáveis
"originais" permanecem inalteradas. Para obter o efeito desejado, é preciso passar à função os endereços
das variáveis:
4
void troca( int *p, int *q)
{
int temp;
temp = *p; *p = *q; *q = temp;
}
Para aplicar a função às variáveis i e j basta dizer
troca( &i, &j);
ou ainda
int *p, *q;
p = &i;
q = &j;
troca( p, q);
2 Vetores e endereços
Os elementos de qualquer vetor (=array) têm endereços consecutivos na memória do computador.
[Na verdade, os endereços não são consecutivos, pois cada elemento do vetor pode ocupar vários bytes.
Mas o compilador C acerta os detalhes internos de modo a criar a ilusão de que a diferença entre os
endereços de elementos consecutivos vale 1.] Por exemplo, depois da declaração
int *v;
v = malloc( 100 * sizeof (int));
o ponteiro v aponta o primeiro elemento de um vetor de 100 elementos. O endereço do segundo
elemento do vetor é v+1 e o endereço do terceiro elemento é v+2. Se i é uma variável do tipo int
então
v + i
é o endereço do(i+1)-ésimo elemento do vetor. A propósito, as expressões v+i e &v[i] têm
exatamente o mesmo valor e portanto as atribuições
*(v+i) = 87;
v[i] = 87;
têm o mesmo efeito. Analogamente, qualquer dos fragmentos de código abaixo pode ser usado para
"carregar" o vetor v:
for (i = 0; i < 100; ++i)
for (i = 0; i < 100; ++i)
scanf( "%d", &v[i]);
scanf( "%d", v + i);
Todas essas considerações também valem se o vetor for alocado pela declaração
int v[100];
mas nesse caso v é uma espécie de "ponteiro constante", cujo valor não pode ser alterado.
5
3 Alocação dinâmica de memória
As declarações abaixo alocam memória para diversas variáveis. A alocação é estática, pois acontece
antes que o programa comece a ser executado:
char c; int i; int v[10];
Às vezes, a quantidade de memória a alocar só se torna conhecida durante a execução do programa. Para
lidar com essa situação é preciso recorrer à alocação dinâmica de memória. A alocação dinâmica é
gerenciada pelas funções malloc e free, que estão na biblioteca stdlib. Para usar esta biblioteca, é
preciso dizer
#include <stdlib.h>
no início do programa.
Função malloc
A função malloc (abreviatura de memory allocation) aloca um bloco de bytes consecutivos na memória
do computador e devolve o endereço desse bloco. O número de bytes é especificado no argumento da
função. No seguinte fragmento de código, malloc aloca 1 byte:
char *ptr;
ptr = malloc( 1);
scanf( "%c", ptr);
O endereço devolvido por malloc é do tipo "genérico" void *. O programador armazena esse
endereço num ponteiro de tipo apropriado. No exemplo acima, o endereço é armazenado num ponteiropara-char.
Para alocar um tipo-de-dado que ocupa vários bytes, é preciso recorrer ao operador sizeof, que diz
quantos bytes o tipo especificado tem:
typedef struct {
int dia, mes, ano;
} data;
data *d;
d = malloc( sizeof (data));
d->dia = 31; d->mes = 12; d->ano = 2008;
[As aparências enganam: sizeof não é uma função.]
Overhead. Cada invocação de malloc aloca um bloco de bytes consecutivos maior que o solicitado: os
bytes adicionais são usados para guardar informações administrativas sobre o bloco de bytes (essas
informações permitem que o bloco seja corretamente desalocado, mais tarde, pela função free).
O número de bytes adicionais pode ser grande, mas não depende do número de bytes solicitado no
argumento de malloc. Não é recomendável, portanto, invocarmalloc repetidas vezes com argumento
muito pequeno. É preferível alocar um grande bloco de bytes e retirar pequenas porções desse bloco na
medida do necessário.
4 Exercícios
1. Por que o código abaixo está errado?
void troca( int *i, int *j) {
int *temp;
*temp = *i; *i = *j; *j = *temp;
}
2. Um ponteiro pode ser usado para dizer a uma função onde ela deve depositar o resultado de seus
cálculos. Escreva uma função hm que converta minutos em horas-e-minutos. A função recebe um
inteiro mnts e os endereços de duas variáveis inteiras, digamos h e m, e atribui valores a essas
variáveis de modo que mseja menor que 60 e que 60*h+m seja igual a mnts. Escreva também
uma função main que use a função hm.
6
3. Escreva uma função mm que receba um vetor inteiro v[0..n-1] e os endereços de duas
variáveis inteiras, digamos min e max, e deposite nessas variáveis o valor de um elemento mínimo
e o valor de um elemento máximo do vetor. Escreva também uma função main que use a função
mm.
4. Suponha que os elementos do vetor v são do tipo int e cada int ocupa 8 bytes no seu
computador. Se o endereço de v[0] é 55000, qual o valor da expressão v+3?
5. Suponha que v é um vetor declarado assim:
int v[100];
Descreva, em português, a sequência de operações que deve ser executada para calcular o valor da
expressão
&v[k + 9];
6. Suponha que v é um vetor. Descreva a diferença conceitual entre as expressões v[3] e v+3.
7. O que há de errado com o seguinte trecho de código?
char *a, *b;
a = "abacate";
b = "uva";
if (a < b)
printf( "%s vem antes de %s no dicionário", a, b);
else
printf( "%s vem depois de %s no dicionário", a, b);
8. Diga (sem usar o computador) qual o conteúdo do vetor a depois dos seguintes comandos.
int a[99];
for (i = 0; i < 99; ++i) a[i] = 98 - i;
for (i = 0; i < 99; ++i) a[i] = a[a[i]];
7
5 Referências Bibliográficas
1. UNICAMP. Introdução a Linguagem C. Disponível em: http://www.fsc.ufsc.br/~canzian/root/tutorialc-unicamp.pdf . Acesso em: 25/03/2013.
2. TRENTIN, Paulo. Curso gratuito de programação em C. Disponível em:
http://www.paulotrentin.com.br/programacao/curso-gratuito-programacao-c/ . Acesso em:
10/04/2013.
3. NETO, Samuel Dias. Linguagem C: Intermediário. Disponível em:
http://homepages.dcc.ufmg.br/~joaoreis/Site%20de%20tutoriais/c_int/es.htm. Acesso em:
11/02/2013.
4. USP. Projeto de Algoritmos: Endereços e ponteiros. Disponível em:
http://www.ime.usp.br/~pf/algoritmos/aulas/pont.html . Acesso em: 15/04/2013.
8
Download