Além dos tipos básicos e dos tipos estruturados (que vimos
aqui e comentamos alguma coisa
aqui), o C possui um tipo de dados especial: o tipo
ponteiro.
A função principal do
ponteiro é ...
APONTAR (não é brincadeira!).
Vamos ver como isso funciona.
Eu sei onde você está!Podemos imaginar um
ponteiro como uma posição na memória onde está armazenado algum valor que pode ser alterado. Então um
ponteiro é uma
variável?
SIM e NÃO (rsrs).
A diferença entre um
ponteiro e uma
variável está no tipo de valores que eles podem armazenar. Enquanto uma variável pode armazenar caracteres, números inteiros e reais;
um ponteiro armazena endereços de memória.
No post em que falamos sobre vetores (
aqui), vimos a relação entre as variáveis e a sua posição na memória. Observe a figura abaixo (que é a última daquele post).

Se você clicou no link e viu o post sobre vetores, irá reparar que a numeração abaixo dos 'quadrinhos' está diferente. Por quê?
É diferente... mas é a mesma coisa!Enquanto no referido post os números abaixo dos 'quadrinhos' indicavam os índices do vetor, neste se referem aos
endereços de memória.
Um
ponteiro que apontasse para o 3o elemento deste vetor estaria armazenando o valor 52.
Repare que os valores de endereço aumentam (ou diminuem) de 4 em 4. Isto acontece porque, neste exemplo, são necessários 4 bytes para armazenar um valor inteiro. Portanto, o
ponteiro de que falamos acima é um
ponteiro para inteiros. Se fosse um
vetor de caracteres, onde cada caracter é armazenado em 1 byte, precisaríamos de um
ponteiro para caracteres.
E qual é a vantagem de usar um ponteiro?Antes de ver um
ponteiro em ação (rsrs), vamos ver a sua sintaxe.
int *pint;O comando acima declara uma variável do tipo
ponteiro para inteiros com o nome pint. Se quisermos apontar para um float o comando seria:
float* pflNão é necessário que o nome do
ponteiro comece com p (embora seja uma boa prática), nem que tenha o tipo da variável. Só deve ter o
tipo e o
*, que pode estar tanto junto ao nome como ao tipo.
Uma das vantagens no uso de um
ponteiro está no fato de que além de apontar para o endereço de uma variável, também é possível ter acesso ao conteúdo desta variável usando o
ponteiro. A esta operação se dá o nome de
dereferenciar um ponteiro.
Como sempre, vamos ver isso na prática.
//EXEMPLO DO USO DE UM PONTEIRO
#include <stdio.h>
#include <stdlib.h>
int main(){
int* pnum;
int num = 10;
pnum = #
printf("O valor inicial de num eh %d.\n",*pnum);
*pnum += 2;
printf("O valor atual de num eh %d.\n",num);
num--;
printf("O valor final de num eh %d.\n",*pnum);
system("pause");
}
O código acima é muito simples, mas mostra alguns fundamentos muito importantes. Vamos entender o que ele faz.
Na linha 6 definimos uma variável
ponteiro para inteiros chamada de pnum. Na linha 7 definimos uma
variável inteira num e a inicializamos com o valor 10.
Um ponteiro armazena endereços de memória. E é isso que fazemos na linha 9. Atribuimos o
endereço da variável inteira num ao
ponteiro para inteiros pnum.
Observe que a
variável é do mesmo tipo do ponteiro. Se não fosse esta atribuição seria
NÃO poderia ser feita.
Na linha 10 exibimos o valor de num
dereferenciando o ponteiro pnum.
Além de acesso ao conteúdo da
variável, também podemos alterá-lo usando o
ponteiro, o que é mostrado na linha 11 e comprovado na linha 12. Acredito que agora você já saiba o que fazem as outras linhas (rsrs).
Você deve estar se perguntando o por quê de usar um
ponteiro ao invés da própria
variável. Isto vai se tornar claro quando falarmos sobre modularidade, que é a possibilidade de dividir um programa em várias partes, sendo que as variáveis de cada parte estão 'escondidas' das outras partes.
Outro uso muito comum de
ponteiros é como uma forma alternativa de acessar elementos de um
vetor. Esse acesso é feito através da
aritmética de ponteiros.
Vetor x PonteiroOs dados armazenados numa variável do tipo ponteiro são endereços de memória (quantas vezes eu já repeti isso???). Esses
endereços são sempre
valores numéricos. E com esses valores podemos fazer algumas operações aritméticas (
SOMENTE SOMA E SUBTRAÇÃO).
Observe este dois comandos aplicados a um
ponteiro para inteiro pnum:
(*pnum)++; pnum++;O
1o comando
incrementa o conteúdo da posição de memória apontada por pnum, ou seja, se na posição apontada por pnum estiver um inteiro com o valor 11, após este comando o valor deste inteiro passa a ser 12.
Já o
2o comando
incrementa o valor armazenado em pnum. Como pnum é um ponteiro para inteiros, que estamos supondo necessitarem de 4 bytes para serem armazenados na memória, ao valor de pnum será somado 4.
Por exemplo, digamos que no código exemplo que usamos acima o endereço da variável inteira num fosse 38. Este valor foi atribuido ao
ponteiro para inteiros pnum. Então o conteúdo de pnum é 38. Ao executar este 2o comando este conteúdo passa a ser 42. Ou seja, pnum não estará mais apontando para num, e passará a apontar para uma posição de memória na qual não sabemos o que está armazenado. Isto é
extremamente perigoso, pois torna possível alterar algo que não deveria ser alterado.
Se para uma única variável isto é perigoso, para um
vetor é um ótimo recurso, afinal um
vetor é um
grupo de variáveis do mesmo tipo armazenadas em sequência na memória.
Observe como ficaria o 2o exemplo do post sobre vetores utilizando
aritmética de ponteiros.
//PROGRAMA QUE RECEBE 100 NÚMEROS INTEIROS
#include <stdio.h>
#include <stdlib.h>
int main(){
int vetor[100], i;
int* pvet;
pvet = vetor;
printf("Digite 100 numeros inteiros:\n");
for(i=0; i < 100; i++){
printf ("Valor %d: ",i+1);
scanf("%d", pvet++);
}
system("pause");
}
Na linha 6 declaramos um
vetor de 100 inteiros e uma variável inteira i que será usada para dar ao usuário uma noção de quantos valores ele já digitou. Na linha 7 definimos uma variável
ponteiro para inteiros chamada pvet. Na linha 9 atribuimos o
endereço do vetor ao
ponteiro pvet (
Numa atribuição, o nome de um vetor corresponde ao seu endereço). Na linha 13, dentro do for, é feita a leitura do valor digitado pelo usuário para a
posição de memória apontada por pvet, que da 1a vez é a 1a posição do vetor, e depois o conteúdo de pvet é incrementado, passando a apontar para a próxima posição do vetor.
Viu como não é tão difícil quanto parece?