You are on page 1of 11

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

Next: 29 Strings e Ponteiros Up: 2 Tpicos Avanados Previous: 27 Estruturas Subseces 28.1 28.2 28.3 28.4 28.5 28.6 28.7 O operador de endereo (&) Tipo ponteiro O operador de dereferncia: * Atribuies envolvendo ponteiros Aritmtica de ponteiros Ponteiros e Arrays Ponteiros e Estruturas 28.7.1 Acesso a membros de estrutura via ponteiro: O operador

->

28.8 Ponteiros como argumentos de funes 28.8.1 Arrays como argumentos de funes 28.8.2 Ponteiros para estruturas como argumentos de funes 28.9 Precedncia de operadores

28 Ponteiros
Em linguagem C a cada varivel est associado: (i) um nome; (ii) um tipo; (iii) um valor; e (iv) um endereo. Considere as seguintes denies de variveis.
int i = 5; char c = 'G';

Na memria, eles podem estar armazenados da forma abaixo:

A varivel inteira i est armazenada no endereo 1342. Ela usa dois bytes de memria (quando um objeto usa mais de um byte, seu endereo onde ele comea - neste

1 de 11

14-04-2013 20:17

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

caso, 1342 e no 1343). A varivel do tipo char c est armazenada no endereo 1346 e usa um byte de memria. O compilador que controla do local de armazenamento destas variveis em memria.

28.1 O operador de endereo (&)


Ns podemos usar o operador de endereo para determinar o endereo de uma objeto na memria. Este operador s pode ser usado com lvalues (objetos que podem estar no lado esquerdo de uma atribuio, como no caso de variveis) porque lvalues tem um endereo alocado na memria. Por exemplo, no exemplo acima, poderamos usar o operador de endreo como nas expresses abaixo:
&i &c

tem valor tem valor

1342 1346

28.2 Tipo ponteiro


Em C, uma varivel que contm um endereo de memria uma varivel do tipo ponteiro. Um valor, que um endereo (como &a) um valor de ponteiro. Quando um ponteiro (a varivel) contm um determinado endereo, dizemos que ele aponta para o endereo de memria. Alm disso, se o valor deste ponteiro o endereo de uma outra varivel qualquer, dizemos que tal ponteiro aponta para esta outra varivel. H um tipo distinto de ponteiro para cada tipo bsico C (como
int, char

float).

verdade que todos os endereos tem o mesmo tamanho4, mas ns tambm precisamos saber algo sobre o que armazenado no endereo de memria apontado (quantos bytes ocupa e como os bytes devem ser interpretados). Assim, a declarao de um tipo ponteiro em C feita da seguinte forma:
tipo *nome_var;

Esta declarao indica que est sendo denido um ponteiro para tipo chamado nome_var. Por exemplo, um tipo ponteiro usado para apontar para inteiros chamado ponteiro para int e isso denotado por um int *. Variveis do tipo ponteiro para int so usadas para armazenar endereos de memria que contem valores do tipo int. Dadas as denies de i e ambos do tipo ponteiro.
int *pi; char *pc; c

acima, ns podemos denir duas novas variveis

pi

pc,

Nesta denio as variveis no foram inicializadas com nenhum valor. Podemos inicializ-las com:
pi = &i;

2 de 11

14-04-2013 20:17

28 Ponteiros
pc = &c;

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

Depois destas atribuies, o valor de Note que nesta denio da varivel (ponteiro para int).

pi

seria

1342,

e o valor de

pc

seria

1346. int *

int *pi, pi

o nome da varivel e

o tipo de

pi

28.3 O operador de dereferncia:

Quando um ponteiro aponta para um endereo de memria, a operao para acessar o contedo do endereo apontado chamado de dereferncia. O operador unrio * usado para fazer a dereferncia. Note que este uso do smbolo * no tem relao com o smbolo de multiplicao. Usando os exemplos anteriores, *pi o objeto apontado por pi (no caso, o valor de um inteiro).
*pi *pc

tem valor tem valor

5 'G'

Como um ponteiro dereferenciado (tais como *pi ou *pc) refere-se a um objeto na memria, ele pode ser usado no s como valor, mas tambm como um lvalue. Isto signica que um ponteiro dereferenciado pode ser usado no lado esquerdo de uma atribuio. Veja alguns exemplos:
printf("Valor= %d, Char = %c\n", *pi, *pc); *pi = *pi + 5; *pc = 'H';

no lado esquerdo do = refere-se ao endereo de memria para o qual pi aponta. *pi no lado direito do = refere-se ao valor armazenado no endereo apontado por pi. A sentena *pi = *pi + 5; faz com que o valor armazenado no endereo apontado por pi seja incrementado de 5. Note que o valor de *pi muda, no o valor de pi.
*pi

Neste exemplo, os valores das variveis i e utilizao de ponteiros da seguinte forma:


printf("Valor = %d, Char = %c\n", i, c); i = i + 5; c = 'H';

poderiam ter sido alterados sem a

Os exemplos acima ilustram como uma varivel pode ser acessada diretamente (atravs do seu nome) ou indiretamente (atravs de um ponteiro apontando para o endereo da varivel).

28.4 Atribuies envolvendo ponteiros


Um ponteiro pode ter atribudo a si um valor que seja o endereo de memria onde est armazenado um valor do mesmo tipo do ponteiro. Isto ocorre quando se usa o operador de endereo visto acima, ou quando se usa o valor de um outro ponteiro que aponte para um objeto do mesmo tipo do primeiro ponteiro. Observe-se o exemplo

3 de 11

14-04-2013 20:17

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

abaixo:
int *p1, *p2, x; float *p3; p1 = &x; p2 = p1; p3 = p1; /* Correto */ /* Correto */ /* Incorreto. Compilador acusa "Warning". */

No exemplo acima, a linguagem C admite a atribuio de um ponteiro para outro de outro tipo (p3 = p1;), mas a compilao acusa uma mensagem de aviso. Posteriormente sero vistas situaes em que a atribuio de ponteiros de tipos diferentes devem ocorrer e como devem ser manipuladas em C.

28.5 Aritmtica de ponteiros


Apenas as operaes de adio e subtrao (e operadores C associados) so permitidos com ponteiros. Assim, possvel adicionar ou subtrair valores inteiros de ponteiros. Operaes de soma, subtrao e comparao entre ponteiros tambm so vlidas, desde que os ponteiros envolvidos apontem para o mesmo tipo de dados. Ainda assim, o resultado somente ter algum sentido prtico se os ponteiros apontarem tambm para o mesmo objeto. Alguns exemplos:
int num[20], *pnum, diff; char str[30], *pstr, *pn, char nome[20]; pn = nome; pstr = str; pnum = num; pnum += 3; *pnum = 10; pstr++; diff = pstr - pnum; /* pnum = &num[3] */ /* equivale a num[3] = 10 */ /* pstr = &str[1] */ /* INCORRETO. Os ponteiros apontam para * tipos diferentes */ /* CORRETO, mas o valor no tem * necessriamente o sentido de "numero * de bytes entre pn e pstr". */

diff = pstr - pn;

pn = str; pstr = &str[30]; diff = pstr - pn; /* CONCEITUALMENTE CORRETO. diff == 30 */

Um ltimo ponto a respeito de operaes sobre ponteiros: Adicionar um ponteiro a outro no produz nenhum resultado prtico ou vlido.

4 de 11

14-04-2013 20:17

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

28.6 Ponteiros e Arrays


Em C, o nome de uma varivel que foi declarada como array representa um ponteiro que aponta para o incio do espao de armazenamento do array, isto , o endereo de memria do primeiro byte associado ao primeiro elemento do array:
char nome[20], *pstr; int val[10], *ptr;

pstr = nome; ptr = val; pstr = nome + 4; ptr = val + 5; pstr = nome++;

/* Equivalente a pstr = &nome[0] */ /* Equivalente a ptr = &val[0] */ /* Equivalente a pstr = &nome[4] */ /* Equivalente a ptr = &val[5] */ /* ATENCAO: INCORRETO !!! */ /* "nome" NO UM PONTEIRO */

Se um ponteiro aponta para um array, pode-se usar indistintamente as formas abaixo para acessar os elementos do array:
int val[10], x, *ptr; ptr = val; *(ptr + 3) = 7; ptr[3] = 10; /* Equivalente a ptr = &val[0] */ /* val[3] = 7 */ /* val[3] = 10 */ /* Equivalente a *(ptr + 3) = 10 */

ptr += 4; ptr[3] = 20;

/* ATENCAO:

val[7] = 20 */

28.7 Ponteiros e Estruturas


Como em qualquer outro tipo, ponteiros para estruturas podem ser denidos. Considere o exemplo abaixo:
/* declara uma estrutura */ struct facil { int num; char ch; }; main() { /* definioes de variaveis */ struct facil fac, /* uma variavel do tipo "struct facil" */ *pfac; /* um ponteiro para "struct facil" */ pfac = &fac;

5 de 11

14-04-2013 20:17

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

(*pfac).num = 32; /* o membro "num" da "struct facil" apontada por "pfac" */ (*pfac).ch = 'A'; /* o membro "char" da "struct facil" apontada por "pfac" */ }

Como se espera, quando se usa um ponteiro para um tipo struct, o ponteiro deve ter assinalado a si um valor ANTES de ser dereferenciado. A ordem pela qual um membro pode ser acessado atravs do ponteiro, the pointer : primeiro o ponteiro dereferenciado, e ento o operador de membro de estrutura e o nome do membro so usados para acessar um membro em particular da estrutura apontada pelo ponteiro. Uma vez que o operador . tem precedncia mais alta que o operador * (veja Tabela 6), os parenteses so necessrios.

28.7.1 Acesso a membros de estrutura via ponteiro: O operador

->

Uma notao do tipo (*pfac).ch confusa, de forma que a linguagem C dene um operador adicional (->) para acessar membros de estruturas atravs de ponteiros. O operador -> formalmente usado como o operador ., exceto que ao invs do nome da varivel de estrutura, um ponteiro para o tipo struct usado esquerda do operador ->. No exemplo acima, as duas ltimas linhas de cdigo podem portanto ser reescritas como:
pfac->num = 32; pfac->ch = 'A'; /* o mesmo que (*pfac).num = 32; /* o mesmo que (*pfac).ch = 'A'; */ */ struct,

Basicamente, use o operador . se voc tem uma varivel de tipo caso voc tenha um ponteiro para um tipo struct.

e o operador

->

28.8 Ponteiros como argumentos de funes


Nos exemplos acima, pode parecer que ponteiros no so teis, j que tudo que zemos pode ser feito sem usar ponteiros. Agora, considere o exemplo da funo troca() abaixo, que deve trocar os valores entre seus argumentos:
#include <stdio.h> void troca(int, int); void troca(int x, int y) { int temp; temp = x; x = y; y = temp; } main(void) { int a, b; printf("Entre dois numeros: "); scanf("%d %d", &a, &b);

6 de 11

14-04-2013 20:17

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

printf("Voce entrou com %d e %d\n", a, b); /* Troca a e b */ troca(a, b); printf("Trocados, eles sao %d e %d\n", a, b); }

Quando a e b so passados como argumentos para troca(), na verdade, somente seus valores so passados. A funo no pode alterar os valores de a e b porque ela no conhece os endereos de a e b. Mas se ponteiros para a e b forem passados como argumentos ao invs de a e b, a funo troca() seria capaz de alterar seus valores; ela saberia ento em que endereo de memria escrever. Na verdade, a funo no sabe que os endereos de memria so associados com a e b, mas ela pode modicar o contedo destes endereos. Portanto, passando um ponteiro para uma varivel (ao invs do valor da varivel), habilitamos a funo a alterar o contedo destas variveis na funo chamadora. Uma vez que endereos de variveis so do tipo ponteiro, a lista de parmetros formais da funo deve reetir isso. A denio da funo troca() deveria ser alterada, e a lista de parmetros formais deve ter argumentos no do tipo int, mas ponteiros para int, ou seja, int *. Quando chamamos a funo troca(), ns no passamos como parmetros reais a e b, que so do tipo int, mas &a e &b, que so do tipo int *. Dentro da funo troca() dever haver mudanas tambm. Uma vez que agora os parmetros formais so ponteiros, o operador de dereferncia, *, deve ser usado para acessar os objetos. Assim, a funo troca() capaz de alterar os valores de a e b ``remotamente''. O programa abaixo a verso correta do problema enunciado para a funo
#include <stdio.h> void troca(int *, int *); /* function troca(px, py) * acao: troca os valores inteiros apontados por px e py * entrada: apontadores px e py * saida: valor de *px e *py trocados * suposicoes: px e py sao apontadores validos * algoritmo: primeiro guarda o primeiro valor em um temporario e troca */ void troca(int *px, int *py) { int temp; temp = *px; *px = *py; *py = temp; } main(void) { int a, b; printf("Entre dois numeros: "); troca():

7 de 11

14-04-2013 20:17

28 Ponteiros
scanf("%d %d", &a, &b);

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

printf("Voce entrou com %d e %d\n", a, b); /* Troca a e b -- passa enderecos */ troca(&a, &b); printf("Trocados, eles sao %d e %d\n", a, b); }

A sada deste programa :


Entre dois numeros: 3 5 Voce entrou com 3 e 5 Trocados, eles sao 5 e 3

Basicamente, se a funo precisa alterar o valor de uma varivel na funo chamadora, ento passamos o endereo da varivel como parmetro real, e escrevemos a funo de acordo, ou seja, com um ponteiro como parmetro formal.

28.8.1 Arrays como argumentos de funes


Quando um array passado como argumento para uma funo, somente o ponteiro para a primeira posio do array passada e no o contedo de todo o array. Arrays so portanto passados por referncia e no por valor. Ao se denir um array como o argumento formal de uma funo em C, duas formas podem ser usadas. Elas podem ser vistas abaixo nas denies das funes func_1() e func_2().
func_1 (char vet [], int ivet[]) { vet[3] = 'A'; vet++; ivet += 3; } func_2 (char *vet, int *ivet) { vet[4] = 'B'; vet++; ivet += 3; } main() { char ender[20]; char vals[20]; func_1(ender, vals); func_2(ender, vals); }

Observe no exemplo acima que a passagem dos arrays ao se chamar as funes func_1() e func_2() feita da mesma forma: Usa-se o NOME das variveis declaradas

8 de 11

14-04-2013 20:17

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

como arrays. Note tambm o uso dos argumentos formais nas funes: vet e ivet podem ser usadas como ponteiros ou como nomes de arrays (com a notao indexada por []).

28.8.2 Ponteiros para estruturas como argumentos de funes


Quando estruturas so passadas como argumentos para funes o valor de todo o objeto agregado passado literalmente. Alm disso, se este valor alterado na funo, ele deve ser retornado (via return), o que implica em copiar de volta toda a estrutura. Isto pode ser bastante ineciente no caso de uma estrutura grande (com muitos membros, com membros de tamanho grande como arrays, etc.). Assim, em alguns casos melhor passar ponteiros para estruturas. Repare a diferena com arrays passados como argumentos para funes vista na seo anterior. O programa abaixo um exemplo do uso de passagem de ponteiros de estruturas para funes:
#define LEN 50 struct endereco { char rua[LEN]; char cidade_estado_cep[LEN]; }; void obtem_endereco(struct endereco *); void imprime_endereco(struct endereco); void obtem_endereco(struct endereco *pender) { printf("Entre rua: "); gets(pender->rua); printf("Entre cidade/estado/cep: "); gets(pender->cidade_estado_cep); } void imprime_endereco(struct endereco ender) { printf("%s\n", ender.rua); printf("%s\n", ender.cidade_estado_cep); } main() { struct endereco residencia; printf("Entre seu endereco residencial:\n"); obtem_endereco(&residencia); printf("\nSeu endereco:\n"); imprime_endereco(residencia); }

Neste caso, main() passa para a funo obtem_endereco() um ponteiro para a variavel residencia. obtem_endereco() pode ento alterar o valor de residencia ``remotamente''. Este valor, em main(), ento passado para imprime_endereco(). Note-se que no necessrio
9 de 11 14-04-2013 20:17

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

passar um ponteiro para a estrutura se seu valor no ser mudado (como o caso da funo imprime_endereco()). De um modo geral, melhor passar ponteiros para estruturas ao invs de passar e retornar valores de estruturas. Embora as duas abordagens sejam equivalentes e funcionem, o programa ir apenas passar ponteiros ao invs de toda uma estrutura que pode ser particularmente grande, implicando em um tempo nal de processamento maior.

28.9 Precedncia de operadores


A precedncia dos operadores * e & alta, a mesma que outros operadores unrios. A tabela 6 apresenta a precedncia de todos os operadores vistos at agora.

Tabela 6: Precedncia e associatividade de operadores Operador Associatividade

() [] -> . ! - ++ -- * & (cast) * / % + < <= > >= == != && || = += -= *= /= %= ,

esquerda para direita (unrios) direita para esquerda esquerda para direita esquerda para direita esquerda para direita esquerda para direita esquerda para direita esquerda para direita direita para esquerda esquerda para direita

Notas de rodap ... tamanho O operador sizeof() pode ser usado para determinar o tamanho de um ponteiro. Por exemplo, sizeof (char *)
10 de 11 14-04-2013 20:17
4

28 Ponteiros

http://www.inf.ufpr.br/nicolui/grad/ci067/Docs/NotasAula...

Next: 29 Strings e Ponteiros Up: 2 Tpicos Avanados Previous: 27 Estruturas Armando Luiz Nicolini Delgado 2011-02-03

11 de 11

14-04-2013 20:17

You might also like