CURSO DE C

Ponteiros e Vetores


Veremos nestas seções que ponteiros e vetores têm uma ligação muito forte.

 

Vetores como ponteiros

Vamos dar agora uma idéia de como o C trata vetores.

 Quando você declara uma matriz da seguinte forma:

 tipo_da_variável nome_da_variável [tam1][tam2] ... [tamN];

 o compilador C calcula o tamanho, em bytes, necessário para armazenar esta matriz. Este tamanho é:

 tam1 x tam2 x tam3 x ... x tamN x tamanho_do_tipo

 O compilador então aloca este número de bytes em um espaço livre de memória. O nome da variável que você declarou é na verdade um ponteiro para o tipo da variável da matriz. Este conceito é fundamental. Eis porque: Tendo alocado na memória o espaço para a matriz, ele toma o nome da variável (que é um ponteiro) e aponta para o primeiro elemento da matriz.

 Mas aí surge a pergunta: então como é que podemos usar a seguinte notação?

 nome_da_variável[índice]

 Isto pode ser facilmente explicado desde que você entenda que a notação acima é absolutamente equivalente a se fazer:

 *(nome_da_variável+índice)

 Agora podemos entender como é que funciona um vetor! Vamos ver o que podemos tirar de informação deste fato. Fica claro, por exemplo, porque é que, no C, a indexação começa com zero. É porque, ao pegarmos o valor do primeiro elemento de um vetor, queremos, de fato, *nome_da_variável e então devemos ter um índice igual a zero. Então sabemos que:

 *nome_da_variável é equivalente a nome_da_variável[0]

 Outra coisa: apesar de, na maioria dos casos, não fazer muito sentido, poderíamos ter índices negativos. Estaríamos pegando posições de memória antes do vetor. Isto explica também porque o C não verifica a validade dos índices. Ele não sabe o tamanho do vetor. Ele apenas aloca a memória, ajusta o ponteiro do nome do vetor para o início do mesmo e, quando você usa os índices, encontra os elementos requisitados.

 Vamos ver agora um dos usos mais importantes dos ponteiros: a varredura sequencial de uma matriz. Quando temos que varrer todos os elementos de uma matriz de uma forma sequencial, podemos usar um ponteiro, o qual vamos incrementando. Qual a vantagem? Considere o seguinte programa para zerar uma matriz:

 

int main ()
{
	float matrx [50][50];
	int i,j;
	for (i=0;i<50;i++)
        	for (j=0;j<50;j++)
                	matrx[i][j]=0.0;
	return(0);
}

Podemos reescrevê-lo usando ponteiros:

 

int main ()
{
	float matrx [50][50];
	float *p;
	int count;
	p=matrx[0];
	for (count=0;count<2500;count++)
        {
        	*p=0.0;
        	p++;
        }
	return(0);
}

No primeiro programa, cada vez que se faz matrx[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 cálculo que deve ser feito é o de um incremento de ponteiro. Fazer 2500 incrementos em um ponteiro é muito mais rápido que calcular 2500 deslocamentos completos.

 Há uma diferença entre o nome de um vetor e um ponteiro que deve ser frisada: um ponteiro é uma variável, mas o nome de um vetor não é uma variável. Isto significa, que não se consegue alterar o endereço que é apontado pelo "nome do vetor". Seja:

     int vetor[10];
     int *ponteiro, i;
     ponteiro = &i;

     /* as operacoes a seguir sao invalidas */

     vetor = vetor + 2;     /* ERRADO: vetor nao e' variavel */
     vetor++;               /* ERRADO: vetor nao e' variavel */
     vetor = ponteiro;      /* ERRADO: vetor nao e' variavel */

Teste as operações acima no seu compilador. Ele dará uma mensagem de erro. Alguns compiladores dirão que vetor não é um Lvalue. Lvalue, significa "Left value", um símbolo que pode ser colocado do lado esquerdo de uma expressão de atribuição, isto é, uma variável. Outros compiladores dirão que tem-se "incompatible types in assignment", tipos incompatíveis em uma atribuição.

/* as operacoes abaixo sao validas */

     ponteiro = vetor;      /* CERTO: ponteiro e' variavel */
     ponteiro = vetor+2;    /* CERTO: ponteiro e' variavel */

 O que você aprendeu nesta seção é de suma importância. Não siga adiante antes de entendê- la bem.

 

Ponteiros como vetores

Sabemos agora que, na verdade, o nome de um vetor é um ponteiro constante. Sabemos também que podemos indexar o nome de um vetor. Como consequência podemos também indexar um ponteiro qualquer. O programa mostrado a seguir funciona perfeitamente:

 

#include <stdio.h>
int main ()
{
	int matrx [10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int *p;
	p=matrx;
	printf ("O terceiro elemento do vetor e: %d",p[2]);
	return(0);
}

Podemos ver que p[2] equivale a *(p+2).

 

Strings

Seguindo o raciocínio acima, nomes de strings, são do tipo char*. Isto nos permite escrever a nossa função StrCpy(), que funcionará de forma semelhante à função strcpy() da biblioteca:

 

#include <stdio.h>
void StrCpy (char *destino,char *origem)
{
while (*origem)
        {
        *destino=*origem;
        origem++;
        destino++;
        }
*destino='\0';
}
int 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);
	return(0);
}

Há vários pontos a destacar no programa acima. Observe que podemos passar ponteiros como argumentos de funções. Na verdade é assim que funções como gets() e strcpy() funcionam. Passando o ponteiro você possibilita à função alterar o conteúdo das strings. Você já estava passando os ponteiros e não sabia. No comando while (*origem) estamos usando o fato de que a string termina com '\0' como critério 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 seção anterior. O que o leitor talvez não saiba ainda (e que será estudado em detalhe mais adiante) é que, no C, são passados para as funções cópias dos argumentos. Desta maneira, quando alteramos o ponteiro origem na função StrCpy() o ponteiro str2 permanece inalterado na função main().

 

Endereços de elementos de vetores

Nesta seção vamos apenas ressaltar que a notação

 &nome_da_variável[índice]

 é válida e retorna o endereço do ponto do vetor indexado por índice. Isto seria equivalente a nome_da_variável + indice. É interessante notar que, como consequência, o ponteiro nome_da_variável tem o endereço &nome_da_variável[0], que indica onde na memória 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 declaração de um vetor de ponteiros inteiros poderia ser:

 

                int *pmatrx [10];

No caso acima, pmatrx é um vetor que armazena 10 ponteiros para inteiros.  


AUTO AVALIAÇÃO

Veja como você está.

Fizemos a função StrCpy(). Faça uma função StrLen() e StrCat() que funcionem como as funções strlen() e strcat() de string.h respectivamente


Página Anterior Índice da Aula Próxima Página


Curso de C do CPDEE/UFMG - 1996 - 1999