DAS5102 – Fundamentos da Estrutura da Informação Ponteiros e Arrays Ponteiros são fundamentais para a programação bem sucedida em C: Passagem de parâmetros por referência; Alocação dinâmica de memória; Aumentar a eficiência de certar rotinas. Essencialmente, um ponteiro nada mais é do que uma variável que ao invés de conter um valor, contém um endereço de memória. O conceito de utilizar espaço em memória não é em absoluto novo. De fato, linguagens de máquina utilizam tal conceito todo o tempo. ADD A,@RI Exemplo 1 – mnemônico (Intel 80C51) que executa a soma do conteúdo da RAM interna apontada por Ri ao Acumulador. Declaração de Ponteiros <tipo> *<nome_variável> int *iptr; float *fptr; char *cptr; Exemplo 2 – exemplo da declaração de diversos ponteiros Como um ponteiro armazena um endereço de memória e não um valor específico, pode parecer estranho a primeira vista que seja requerida a associação de um tipo ao se declaram um ponteiro. No entanto, muito comumente, deseja-se acessar o valor armazenado pela variável apontada por um ponteiro. Desta forma, o tipo do ponteiro serve para instruir o compilador na maneira pela qual o dado apontado será acessado. Essencialmente, o tipo de dados e a c odificação do dado (como os bits serão interpretados, necessário por exemplo para tipos com base na notação de ponto flutuante). Operadores de Ponteiros “*” conteúdo: aplicado a um variável tipo ponteiro, retorna o conteúdo do endereço apontado pelo ponteiro; “&” endereço: aplicado a uma variável, retorna seu endereço de memória. Daniel Duarte Abdala 1 DAS5102 – Fundamentos da Estrutura da Informação int count; int *iptr; iptr = &count; printf(“%d”, *iptr); Exemplo 3 – exemplo da utilização de ambos os operadores unários Inicialização de Ponteiros Ponteiros são inicializados ao definirmos um endereço para o qual eles devem apontar. Ponteiros não inicializados são também chamados de “ponteiros selvagens” A não inicialização de ponteiros é um dos principais fatores para erros em tempo de execução em programas escritos na linguagem C. Tais erros são difíceis de identificar e corrigir, porque o tempo decorrido entre o acesso ilegal à memória de um ponteiro e a efetiva ocorrência do erro é imprevisível. Uma maneira de garantir que um ponteiro declarado não apontará para um lugar qualquer da memória é atribuir o valor NULL para o mesmo. int *iptr = null; Ponteiros e Arrays Ponteiros são muito utilizados para a navegação de arrays, principalmente de arrays alocados de maneira dinâmica. A idéia geral é alocar o array usando ou o método estático ou o dinâmico e então criar uma variável do tipo ponteiro que apontará para a primeira posição do ponteiro. Utilizando aritmética de ponteiros (incremento, decremento, saltos) é possível percorrer o array sem ter que se preocupar com quantos bytes devem ser acessados, pois a tipagem do ponteiro cuidará deste detalhe. 00H Vet[0] 01H Vet[1] 02H Vet[2] 03H Vet[3] 04H Vet[4] 05H Vet[5] 06H Vet[6] Figura 1 – Exemplo de posicionamento de um array na memória É importante ressaltar que a utilização de ponteiros para indexação de arrays apresenta um risco intrínseco. Como C não é uma linguagem fortemente tipada, arrays não possuem um marcador de fim de alocação de memória tal como acontece em PASCAL. Ponteiros permitirão Daniel Duarte Abdala 2 DAS5102 – Fundamentos da Estrutura da Informação que a memória continue sendo acessada seqüencialmente mesmo após o final da área previamente alocada para o array tenha sido toda percorrida. Aritmética de Ponteiros Uma das grandes vantagens decorrentes da utilização de ponteiros é a possibilidade de indexar posições de memória de maneira fácil e intuitiva. Tal endereçamento é alcançado a partir de operações aritméticas simples que tem como objetos variáveis do tipo ponteiro. Operandos unários: ++ incremento unitário; -- decremento unitário. Suponha que p é um ponteiro do tipo inteiro e suponha que o tipo inteiro na arquitetura de destino do exemplo sejam representados por 2 bytes cada. Considere o seguinte trecho de código: int vet[10]; int *iptr; iptr = vet; iptr++; Exemplo 4 – navegação de arrays usando ponteiros Assumindo que o array “vet” seja alocado pelo “loader” do sistema operacional iniciando na posição 00H, iptr erá apontar para a posição 00H inicialmente. Ao se executar a operação iptr++, o endereço apontado por iptr será incrementado em uma unidade, o que corresponderá, dado o fato de que inteiros são representados por dois bytes, ao endereço 02H. (veja figura1 para exemplo) Comparação de Ponteiros O ponto importante a respeito da comparação de ponteiros a ser considerado diz respeito a semântica da comparação. Como ponteiros são variáveis que contém endereços de memória, ao compararmos ponteiros o que estamos fazendo realmente é verificando se dois endereços de memória são idênticos ou não. A primeira vista, tal tipo de comparação pode parecer de pouca importância, no entanto ela será de grande valor futuramente quando ponteiros forem utilizados para manipular estruturas de dados complexas tal como listas ligadas ou árvores. Podemos comparar se dois ponteiros são iguais menores ou maiores exatamente como se compararam valores de variáveis. Daniel Duarte Abdala 3 DAS5102 – Fundamentos da Estrutura da Informação int vet[7]; int *iptr1, *iptr2; iptr1 iptr2 iptr1 iptr1 iptr1 iptr1 = &vet[0]; = &vet[6]; == iptr2; < iptr2; > iptr2; <= iptr2; Exemplo 5 – comparação de ponteiros Daniel Duarte Abdala 4 DAS5102 – Fundamentos da Estrutura da Informação Lista de Exercícios 1. O que á de errado com o programa abaixo? Como poderíamos corrigi-lo? void main( void ) { int x, *p; x = 10; *p = x; } 2. Seja o seguinte trecho de programa: int i = 3, j = 5; int *p, *q; p = &i; q = &j; Qual será o valor das seguintes expressões: a) b) c) d) p==&i; *p - *q; &p==&i; 3* *p/(*q)+7 3. Qual será a saída do programa supondo que i ocupa o endereço 4094H na memória? int main( void ) { int i=5, *p; p=&i; printf(“%d %d %d %d %d\n”, p, *p+2, **&p, 3**p, **&p+4); } 4. Assumindo que Pulo[] é um array do tipo int, quais das seguintes expressões referenciam o valor do terceiro elemento do array? a) *(pulo+2) b) *(pulo+4) c) pulo+4 d) pulo+2 5. Suponha a declaração: Daniel Duarte Abdala 5 DAS5102 – Fundamentos da Estrutura da Informação int mat[4], *p, x; Quais das expressões abaixo são válidas? Justifique: a) b) c) d) p = mat+1; p = mat++; p = ++mat; x = (*mat)++; 6. Explique a diferença entre os comandos abaixo: (assuma que p é um ponteiro do tipo int) a) p++; b) (*p)++; c) *(p++); 7. Explique o programa abaixo. Encontre o erro, corrija-o para que o mesmo escreva o número 10 na tela. #include <stdio.h> int main( void ) { int x, *p, **p; p = &x; q = &p; x = 10; printf(“\n%d\n”, &q); return ( 0 ); } Daniel Duarte Abdala 6