Ponteiros na linguagem C

  

Ponteiros na linguagem C

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