Você está na página 1de 11

Como Funcionam os Ponteiros

Os ints guardam inteiros. Os floats guardam nmeros de ponto flutuante. Os chars guardam caracteres. Ponteiros guardam endereos de memria. Quando declaramos ponteiros ns informamos ao compilador para que tipo de varivel vamos apont-lo. Um ponteiro int aponta para um inteiro, isto , guarda o endereo de um inteiro.

Declarando e Utilizando Ponteiros Para declarar um ponteiro temos a seguinte forma geral: tipo_do_ponteiro *nome_da_varivel; o asterisco (*) que faz o compilador saber que aquela varivel no vai guardar um valor mas sim um endereo para aquele tipo especificado. Vamos ver exemplos de declaraes: int *pt; char *temp, *pt2; O primeiro exemplo declara um ponteiro para um inteiro. O segundo declara dois ponteiros para caracteres. Eles ainda no foram inicializados (como toda varivel do C que apenas declarada). Isto significa que eles apontam para um lugar indefinido. Este lugar pode estar, por exemplo, na poro da memria reservada ao sistema operacional do computador. Usar o ponteiro nestas circunstnicias 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! Isto de suma importncia! Para atribuir um valor a um ponteiro recm-criado poderamos igual-lo a um valor de memria. Mas, como saber a posio na memria de uma varivel do nosso programa? Seria muito difcil saber o endereo de cada varivel que usamos, mesmo porque estes endereos so determinados pelo compilador na hora da compilao. Podemos ento deixar que o compilador faa este trabalho por ns. Para saber o endereo de uma varivel basta usar o operador &. Veja o exemplo: int cont=10; int *pt; pt=&cont; Criamos um inteiro cont com o valor 10 e um apontador para um inteiro pt. A expresso &cont nos d o endereo de cont, o qual armazenamos em pt. Simples, no ? Repare que no alteramos o valor de cont, que continua valendo 10.

Como ns colocamos um endereo em pt, ele est agora "liberado" para ser usado. Podemos, por exemplo, alterar o valor de cont usando pt. Para tanto vamos usar o operador "inverso" do operador &. o operador *. No exemplo acima, uma vez que fizemos pt=&cont a expresso *pt equivalente ao prprio cont. Isto significa que, se quisermos mudar o valor de cont para 12, basta fazer *pt=12. Uma observao importante: apesar do smbolo ser o mesmo, o operador * (multiplicao) no o mesmo operador que o * (referncia de ponteiros). Para comear o primeiro binrio, e o segundo unrio pr-fixado. Aqui vo dois exemplos de usos simples de ponteiros:

#include <stdio.h>
void main (){ int num, valor; int *p; num=55; p=&num; valor=*p; /* Pega o endereco de num */ /* Valor e igualado a num de uma maneira indireta */

printf ("\n\n%d\n",valor); printf ("Endereco para onde o ponteiro aponta: %p\n",p); printf ("Valor da variavel apontada: %d\n",*p); } #include <stdio.h> void main (){ int num,*p; num=55; p=&num; /* Pega o endereco de num */ printf ("\nValor inicial: %d\n",num); *p=100; /* Muda o valor de num de uma maneira indireta */ printf ("\nValor final: %d\n",num); } Nos exemplos acima vemos um primeiro exemplo do funcionamento dos ponteiros. No primeiro exemplo, o cdigo %p usado na funo printf() indica funo que ela deve imprimir um endereo. Podemos fazer algumas operaes aritmticas com ponteiros. A primeira, e mais simples, igualar dois ponteiros. Se temos dois ponteiros p1 e p2 podemos igual-los fazendo p1=p2. Repare que estamos fazendo com que p1 aponte para o mesmo lugar que p2. Se quisermos que a varivel apontada por p1 tenha o mesmo contedo da varivel apontada por p2 devemos fazer *p1=*p2. Basicamente, depois que se aprende a usar os dois operadores (& e *) fica fcil entender operaes com ponteiros.
2

As prximas operaes, tambm muito usadas, so o incremento e o decremento. Quando incrementamos um ponteiro ele passa a apontar para o prximo valor do mesmo tipo para o qual o ponteiro aponta. Isto , se temos um ponteiro para um inteiro e o incrementamos ele passa a apontar para o prximo inteiro. Esta mais uma razo pela qual o compilador precisa saber o tipo de um ponteiro: se voc incrementa um ponteiro char* ele anda 1 byte na memria e se voc incrementa um ponteiro double* ele anda 8 bytes na memria. O decremento funciona semelhantemente. Supondo que p um ponteiro, as operaes so escritas como: p++; p--; Mais uma vez insisto. Estamos falando de operaes com ponteiros e no de operaes com o contedo das variveis para as quais eles apontam. Por exemplo, para incrementar o contedo da varivel apontada pelo ponteiro p, faz-se: (*p)++; Outras operaes aritmticas teis so a soma e subtrao de inteiros com ponteiros. Vamos supor que voc queira incrementar um ponteiro de 15. Basta fazer: p=p+15; ou p+=15; E se voc quiser usar o contedo do ponteiro 15 posies adiante: *(p+15); A subtrao funciona da mesma maneira. Uma outra operao, s vezes til, a comparao entre dois ponteiros. Mas que informao recebemos quando comparamos dois ponteiros? Bem, em primeiro lugar, podemos saber se dois ponteiros so iguais ou diferentes (== e !=). No caso de operaes do tipo >, <, >= e <= estamos comparando qual ponteiro aponta para uma posio mais alta na memria. Ento uma comparao entre ponteiros pode nos dizer qual dos dois est "mais adiante" na memria. A comparao entre dois ponteiros se escreve como a comparao entre outras duas variveis quaisquer:

p1>p2
H entretanto operaes que voc no pode efetuar num ponteiro. Voc no pode dividir ou multiplicar ponteiros, adicionar dois ponteiros, adicionar ou subtrair floats ou double de ponteiros. AUTO AVALIAO Veja como voc est. a) Explique a diferena entre p++; (*p)++; *(p++);

O que quer dizer *(p+10);? Explique o que voc entendeu da comparao entre ponteiros

b) Qual o valor de y no final do programa? Tente primeiro descobrir e depois verifique no computador o resultado. A seguir, escreva um /* comentrio */ em cada comando de atribuio explicando o que ele faz e o valor da varivel esquerda do '=' aps sua execuo.
void main() { int y, *p, x; y = 0; p = &y; x = *p; x = 4; (*p)++; x--; (*p) += x; printf ("y = %d\n", y); }

Ponteiros e Vetores
Veremos nestas sees que ponteiros e vetores tm uma ligao muito forte. Vetores como ponteiros Vamos dar agora uma idia de como o C trata vetores. Quando voc declara uma matriz da seguinte forma: tipo_da_varivel nome_da_varivel [tam1][tam2] ... [tamN]; o compilador C calcula o tamanho, em bytes, necessrio para armazenar esta matriz. Este tamanho : tam1 x tam2 x tam3 x ... x tamN x tamanho_do_tipo O compilador ento aloca este nmero de bytes em um espao livre de memria. O nome da varivel que voc declarou na verdade um ponteiro para o tipo da varivel da matriz. Este conceito fundamental. Eis porque: Tendo alocado na memria o espao para a matriz, ele toma o nome da varivel (que um ponteiro) e aponta para o primeiro elemento da matriz. Mas a surge a pergunta: ento como que podemos usar a seguinte notao? nome_da_varivel[ndice] Isto pode ser facilmente explicado desde que voc entenda que a notao acima absolutamente equivalente a se fazer: *(nome_da_varivel+ndice) Agora podemos entender como que funciona um vetor! Vamos ver o que podemos tirar de informao deste fato. Fica claro, por exemplo, porque que, no C, a indexao comea com zero.

porque, ao pegarmos o valor do primeiro elemento de um vetor, queremos, de fato, *nome_da_varivel e ento devemos ter um ndice igual a zero. Ento sabemos que: *nome_da_varivel equivalente a nome_da_varivel[0] Outra coisa: apesar de, na maioria dos casos, no fazer muito sentido, poderamos ter ndices negativos. Estaramos pegando posies de memria antes do vetor. Isto explica tambm porque o C no verifica a validade dos ndices. Ele no sabe o tamanho do vetor. Ele apenas aloca a memria, ajusta o ponteiro do nome do vetor para o incio do mesmo e, quando voc usa os ndices, encontra os elementos requisitados. Vamos ver agora um dos usos mais importantes dos ponteiros: a varredura seqencial de uma matriz. Quando temos que varrer todos os elementos de uma matriz de uma forma seqencial, podemos usar um ponteiro, o qual vamos incrementando. Qual a vantagem? Considere o seguinte programa para zerar uma matriz: void main (){ int mat [50][50]; int i,j; for (i=0;i<50;i++) for (j=0;j<50;j++) mat [i][j]=0; } Podemos reescrev-lo usando ponteiros: void main (){ int mat [50][50], *p; int count; p=&mat[0][0]; for (count=0;count<2500;count++){ *p=0; p++; } } No primeiro programa, cada vez que se faz mat[i][j] o programa tem que calcular o deslocamento para dar ao ponteiro. Ou seja, o programa tem que calcular 2500 deslocamentos. No segundo programa o nico clculo que deve ser feito o de um incremento de ponteiro. Fazer 2500 incrementos em um ponteiro muito mais rpido que calcular 2500 deslocamentos completos.

H uma diferena entre o nome de um vetor e um ponteiro que deve ser frisada: um ponteiro uma varivel, mas o nome de um vetor no uma varivel. Isto significa, que no se consegue alterar o endereo que apontado pelo "nome do vetor". Seja: int vetor[10]; int *ponteiro, i; ponteiro = &i; /* as operaes a seguir so invlidas */ vetor = vetor + 2; /* ERRADO: vetor no varivel */ vetor++; /* ERRADO: vetor no varivel */ vetor = ponteiro; /* ERRADO: vetor no varivel */ Teste as operaes acima no seu compilador. Ele dar uma mensagem de erro. Alguns compiladores diro que vetor no um Lvalue. Lvalue, significa "Left value", um smbolo que pode ser colocado do lado esquerdo de uma expresso de atribuio, isto , uma varivel. Outros compiladores diro que tem-se "incompatible types in assignment", tipos incompatveis em uma atribuio. /* as operaes abaixo so vlidas */ ponteiro = vetor; /* CERTO: ponteiro varivel */ ponteiro = vetor+2; /* CERTO: ponteiro varivel */ O que voc aprendeu nesta seo de suma importncia. No siga adiante antes de entend-la bem. Ponteiros como vetores Sabemos agora que, na verdade, o nome de um vetor um ponteiro constante. Sabemos tambm que podemos indexar o nome de um vetor. Como conseqncia podemos tambm indexar um ponteiro qualquer. O programa mostrado a seguir funciona perfeitamente: #include <stdio.h> void main (){ int mat [10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int *p; p=mat; printf ("O terceiro elemento do vetor e: %d",p[2]); } Podemos ver que p[2] equivale a *(p+2).

Strings
Seguindo o raciocnio acima, nomes de strings, so do tipo char*. Isto nos permite escrever a nossa funo StrCpy(), que funcionar de forma semelhante funo strcpy() da biblioteca: #include <stdio.h> void StrCpy (char *destino, char *origem){ while (*origem) { *destino = *origem; origem++; destino++; } *destino='\0'; } void main (){ char str1[100],str2[100],str3[100]; printf ("Entre com uma string: "); gets (str1); StrCpy (str2,str1); StrCpy (str3,"Voce digitou a string : "); printf ("\n\n%s%s",str3,str2); } H vrios pontos a destacar no programa acima. Observe que podemos passar ponteiros como argumentos de funes. Na verdade assim que funes como gets() e strcpy() funcionam. Passando o ponteiro voc possibilita funo alterar o contedo das strings. Voc j estava passando os ponteiros e no sabia. No comando while (*origem) estamos usando o fato de que a string termina com '\0' como critrio de parada. Quando fazemos origem++ e destino++ o leitor poderia argumentar que estamos alterando o valor do ponteiro-base da string, contradizendo o que recomendei que se deveria fazer, no final de uma seo anterior. O que o leitor talvez no saiba ainda (e que ser estudado em detalhe mais adiante) que, no C, so passados para as funes cpias dos argumentos. Desta maneira, quando alteramos o ponteiro origem na funo StrCpy() o ponteiro str2 permanece inalterado na funo main().

Endereos de elementos de vetores


Nesta seo vamos apenas ressaltar que a notao &nome_da_varivel[ndice] vlida e retorna o endereo do ponto do vetor indexado por ndice. Isto seria equivalente a nome_da_varivel + ndice. interessante notar que, como conseqncia, o ponteiro nome_da_varivel
7

tem o endereo &nome_da_varivel[0], que indica onde na memria est guardado o valor do primeiro elemento do vetor.

Vetores de ponteiros
Podemos construir vetores de ponteiros como declaramos vetores de qualquer outro tipo. Uma declarao de um vetor de ponteiros inteiros poderia ser:

int *pmat [10];


No caso acima, pmat um vetor que armazena 10 ponteiros para inteiros. AUTO AVALIAO Veja como voc est. Fizemos a funo StrCpy(). Faa uma funo StrLen() e StrCat() que funcionem como as funes strlen() e strcat() de string.h respectivamente

Inicializando Ponteiros
Podemos inicializar ponteiros. Vamos ver um caso interessante dessa inicializao de ponteiros com strings. Precisamos, para isto, entender como o C trata as strings constantes. Toda string que o programador insere no programa colocada num banco de strings que o compilador cria. No local onde est uma string no programa, o compilador coloca o endereo do incio daquela string (que est no banco de strings). por isto que podemos usar strcpy() do seguinte modo: strcpy (string,"String constante."); strcpy() pede dois parmetros do tipo char*. Como o compilador substitui a string "String constante." pelo seu endereo no banco de strings, tudo est bem para a funo strcpy(). O que isto tem a ver com a inicializao de ponteiros? que, para uma string que vamos usar vrias vezes, podemos fazer: char *str1="String constante."; A poderamos, em todo lugar que precisarmos da string, usar a varivel str1. Devemos apenas tomar cuidado ao usar este ponteiro. Se o alterarmos vamos perder a string. Se o usarmos para alterar a string podemos facilmente corromper o banco de strings que o compilador criou. Mais uma vez fica o aviso: ponteiros so poderosos mas, se usados com descuido, podem ser uma tima fonte de dores de cabea.

AUTO AVALIAO Veja como voc est.

Escreva a funo int strend(char *s, char *t)


que retorna 1 (um) se a cadeia de caracteres t ocorrer no final da cadeia s, e 0 (zero) caso contrrio.

Cuidados a Serem Tomados ao se Usar Ponteiros


O principal cuidado ao se usar um ponteiro deve ser: saiba sempre para onde o ponteiro est apontando. Isto inclui: nunca use um ponteiro que no foi inicializado. Um pequeno programa que demonstra como no usar um ponteiro:

void main (){ /* Errado - Nao Execute */


int x, *p; x = 13; *p=x; } Este programa compilar e rodar. O que acontecer? Ningum sabe. O ponteiro p pode estar apontando para qualquer lugar. Voc estar gravando o nmero 13 em um lugar desconhecido. Com um nmero apenas, voc provavelmente no vai ver nenhum defeito. Agora, se voc comear a gravar nmeros em posies aleatrias no seu computador, no vai demorar muito para travar o micro (se no acontecer coisa pior). AUTO AVALIAO Veja como voc est. Escreva um programa que declare uma matriz 100x100 de inteiros. Voc deve inicializar a matriz com zeros usando ponteiros para enderear seus elementos. Preencha depois a matriz com os nmeros de 1 a 10000, tambm usando ponteiros.

Responda as perguntas abaixo, escolhendo a alternativa adequada para cada questo. 1- Seja um vetor declarado por int vet[10]; Qual elemento deste vetor acessado quando se escreve vet[2] ? a) Primeiro elemento b) Segundo elemento
9

c) Terceiro elemento d) Quarto elemento e) n.d.a. 2- Se declararmos um vetor como: int vet[30] a instruo abaixo acessa corretamente os elementos deste vetor? for (j=0; j <= 30; j++) vet[j] = j*j; a) sim b) no 3- Seja a matriz matrx declarada e inicializada por: int matrx[][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; O que conter o elemento matrx[1][2] ? a) 2 b) 5 c) 6 d) 7 e) Nenhuma das opes anteriores 4- Se uma string for declarada como: char str[20]; o nmero mximo de caracteres que podero ser lidos e armazenados nela : a) 18 b) 19 c) 20 d) 21 5- Qual funo pode ser usada para determinar o comprimento de uma string? a) gets b) strcpy c) strcat d) strlen e) strcmp

6- Seja a seguinte seqncia de instrues em um programa C:


10

int *pti; int i = 10; pti = &i; Qual afirmativa falsa? a) pti armazena o endereo de i b) *pti igual a 10 c) ao se executar *pti = 20; i passar a ter o valor 20 d) ao se alterar o valor de i, *pti ser modificado e) pti igual a 10 7- Seja a seguinte seqncia de instrues em um programa C: int *pti; int veti[]={10,7,2,6,3}; pti = veti; Qual afirmativa falsa? a) *pti igual a 10 b) *(pti+2) igual a 2 c) pti[4] igual a 3 d) pti[1] igual a 10 e) *(veti +3) igual a 6

11

Você também pode gostar