Quando usamos os procedimentos ou
as funções sempre temos de declarar novas variáveis para estarmos utilizando
para a manipulação de valores e quando queremos passar um valor para uma
variável, nas funções, usamos a passagem por valor. Os ponteiros servem para
trabalhamos a passagem de valor por parâmetro, ou seja, passar o endereço de
memória para estarmos trabalhando as variáveis globais, variáveis definidas na
função main().
Antes de entrarmos no assunto de
ponteiros, é necessário relembramos o papel do “&” usado no scanf. O
“&” antes de uma variável indica o endereço de memória no qual será
armazenado um valor. Os endereços de memória variam de acordo com o tipo de
dados, ex.: o tipo int ocupa 4 bytes e o tipo char 1 byte. Pois bem, o endereço
de uma variável é o local físico que esta reside na memória, no caso de uma
variável ter mais de um byte o endereço é sempre o primeiro byte ocupado.
Vejamos um exemplo:
#include <stdio.h>
int main(){
int num1=100; char A='A';
printf ("\n O valor de Num 1
e : %d \n o endereco de memoria de Num 1 e: %d ", num1, &num1); printf
("\n O valor de A e: %c \n o endereco de memoria de A e: %d", A,
&A);
return 0;
}
O simples código acima é um
programa para mostrar duas variáveis e seus respectivos endereços de memória. O
“&” usado no printf tem a função de mostrar o endereço de memória de uma
variável. Como resposta a esse programa obtivemos:
Notemos que o endereço de memória
é o local na memória onde o valor de uma variável está armazenado. Esse
endereço vai variar de acordo com o computador e a quantidade de memória no
qual este programa será executado.
O ponteiro é um tipo de dado que serve para trabalhar o armazenamento de dados na memória de um computador. Assim, um ponteiro é do tipo de dado ponteiro e não de outro tipo de dado como o char, int ou float. Trabalhar com ponteiros é trabalhar com espaços de memória para alocar dados.
Podemos concluir que uma variável
é composta de um endereço de memória (local onde a variável está armazenada na
memória), um identificador (nome da variável) e seu valor. O exemplo acima
mostra que através do caractere “&” é possível identificar esse endereço de
memória de uma variável (&num1, &A), enquanto se usarmos somente o nome
da variável (A), estaremos trabalhando apenas com o valor que estará armazenado
naquele endereço de memória.
Um ponteiro em C também é uma
variável, que pode armazenar um valor do endereço de memória de uma variável
qualquer, no caso acima “&A” estamos mostrando apenas o endereço de memória
da variável. Para declararmos uma variável do tipo ponteiro devemos atribuir um
nome a esta, precedido do caractere “*”, assim como o tipo da variável ao qual
o ponteiro está se referindo, para que seja definido o tamanho desta variável
ponteiro. Veja o exemplo abaixo:
#include <stdio.h> int
main() {
int A=4; int *pA; pA=&A;
printf("%d %d \n", A,
pA); printf("\n %d %d \n", &A, pA); return 0;
}
Neste exemplo, criamos uma
variável do tipo ponteiro de inteiro “*pA” para armazenar o valor de memória da
variável inteiro “A”, que terá o valor igual a 4. Por padrão, ao criarmos um
ponteiro, o mesmo receberá o valor “NULL”, ou seja, não tem valor algum. Ao
atribuirmos o valor do ponteiro ao valor de memória de “A”, ambas ficarão com o
mesmo valor. Vejamos nosso resultado:
Como podemos ver, a variável
ponteiro “pA” armazenou a posição de memória da variável A. Mais por que isso é
importante?
Bem, vamos a outro exemplo onde
alteraremos o código fonte do exemplo acima: #include <stdio.h>
int main() {
int A=4; int *pA; pA=&A;
printf("\n O valor
armazenado e: %d, %d \n", A, pA); printf("\n O valor de memoria e d
%d, %d \n", &A, pA);
// Alterando o valor de um
ponteiro
*pA=20;
printf("\n %d %d \n",
A, pA); return 0;
}
No exemplo anterior, como o “A” e
“pA” têm o ponteiro armazenado de mesmo valor, podemos fazer a referência a
“pA” através do apontador “*”, no caso temos “*pA” que quer dizer que ao
atribuirmos valores a “*pA” na realidade estamos atribuindo o valor ao endereço
de memória ao qual se refere. Como este endereço é o mesmo de “A”, o valor
atribuído ao apontador “*pA” será na realidade atribuído a variável “A”, pois
ponteiro não armazena valor, apenas aponta um valor.
Veja o resultado da execução do
código acima:
Ao usarmos novamente o caractere
“*” com o nome da variável do tipo ponteiro e atribuirmos um valor a este,
estaremos atribuindo tal valor não à variável ponteiro mais sim ao endereço de
memória ao qual está sendo apontada na variável tipo ponteiro.
Dessa forma, o uso do ponteiro
pode passar endereços de memória em uma função ou procedimento ao qual
desejamos, e esta modifica os valores em uma variável global, definida na
função main(). Vejamos um exemplo disso:
#include <stdio.h>
int soma (int n1, int n2, int
*p);
void main(){
int num1, num2, result; int
*presult=&result;
printf("entre com o valor
1"); scanf("%d", &num1); printf("entre com o valor
2"); scanf("%d", &num2); soma(num1, num2, presult);
printf("a soma dos dois
numeros foi %d ", result);
}
/*função para somar dois numeros
retornando o valor*/ int soma (int n1, int n2, int *p){
int s; s=n1+n2;
*p=s;
}
Notemos que a função soma não
mais traz o result, e sim passa a ser uma função sem retorno ou um
procedimento, se queira chamar assim. Trabalhamos com um ponteiro para a variável
“result” que chamamos de “presult” que tem o mesmo endereço de memória e é
passado como parâmetro para a função soma, que recebe os dois números e o
ponteiro da variável result. Na função ou procedimento soma, recebemos os dois
números e o ponteiro com as variáveis para realizarmos uma soma, enviando esta
soma ao valor apontado no ponteiro “*p” que é o mesmo endereço de memória
(aponta) para a variável “result”, passando assim o valor na variável global
“result”.
Os ponteiros se tornam bastante úteis
com as funções ou procedimentos com o uso de vetores ou matrizes. Vejamos um
exemplo:
#include <stdio.h> void
main (){
int vet[5]={1, 2, 3, 4, 5};
int *pvet = vet; int i;
for (i=0; i<5; i++){
printf ("\n valor do vetor
da posicao %d, e %d: e o ponteiro: %d: ", i, vet[i], &pvet[i]);
}
}
Para declararmos um ponteiro para
uma variável do tipo vetor é o mesmo procedimento para uma variável qualquer.
Porém, o ponteiro ficará do tamanho do vetor, ou seja, com todas as posições do
vetor. O exemplo acima mostra isso quando executamos o código. O resultado
obtido é o seguinte:
Ou seja, dá para ver claramente
que cada espaço de um vetor do tipo int ocupa 4 bytes de memória e são
sequenciais na memória.
No exemplo abaixo, passaremos um
vetor a ser trabalhado em uma função, que fará o somatório de todos os números
desse vetor e que segue o mesmo padrão de declaração o qual estamos trabalhando
até o momento. Segue o exemplo do código:
#include <stdio.h>
int somavetor (int *pvet); void
main (){
int vet[5]; int soma, i;
for (i=0; i<5; i++){
printf ("\n entre com o
valor a ser armazenado %d ", i); scanf ("%d", &vet[i]);
}
soma=somavetor(vet);
printf ("a soma dos dois
numeros foi %d ", soma);
}
int somavetor (int *pvet){ int x,
s;
for (x=0; x<5; x++){
s=s+pvet[x];
}
return s;
}
A passagem de parâmetro de um vetor pode ser realizada diretamente conforme podemos observar. O detalhe está apenas no parâmetro de recebimento que é do tipo ponteiro.
Fonte: Linguagem da programação I, UFERSA, Macedo, Luiz