Estrutura de Dados Ponteiros Professor Mário Dantas Contribuição: Professor Marcelo Douglas Introdução ao uso de Ponteiros • Os ints guardam valores inteiros. Os floats guardam valores de ponto flutuante. Os chars guardam caracteres. • Ponteiros guardam endereços de memória. Esta operação pode ser entendida pelo compilador como “carregue o inteiro 55 no endereço de memória 248440”. int j; j = 55; 55 ????? ????? ????? ????? 248440 ????? Introdução ao uso de Ponteiros • Estamos acostumados a trabalhar com o “conteúdo armazenado nos endereços de memória”, no entanto podemos estender os recursos de nossos programas ao aprendermos a trabalhar com os endereçamentos. • Quando você anota o endereço de um colega está criando um ponteiro. O ponteiro é o papel. Paulo Roberto Rua Rio de janeiro, nº 3455 Introdução ao uso de Ponteiros • Qual é o sentido disto? • Quando você anota o endereço de um colega, você vai usar este endereço para achá-lo. • O C funciona assim. Você anota o endereço de algo numa variável ponteiro para depois usar. • Da mesma maneira, uma agenda, onde são guardados endereços de vários amigos, poderia ser vista como sendo uma matriz de ponteiros no C. Introdução ao uso de Ponteiros • Um ponteiro também tem tipo. No C quando declaramos ponteiros nós informamos ao compilador para que tipo de variável vamos apontá-lo. Um ponteiro int aponta para um inteiro, isto é, guarda o endereço de um inteiro. • Para declarar um ponteiro temos a seguinte forma geral: tipo_do_ponteiro = *nome_da_variável • É o asterisco (*) que faz o compilador saber que aquela variável não vai guardar um valor mas sim um endereço para aquele tipo especificado. Introdução ao uso de Ponteiros • Exemplos de criação de ponteiros: int *ptr; • O exemplo declara que desejamos criar uma variável ponteiro para armazenar o endereço de um inteiro. • Eles ainda não foram inicializados (como toda variável do C que é apenas declarada). Isto significa que eles apontam para um lugar indefinido. Este lugar pode estar, por exemplo, na porção da memória reservada ao sistema operacional do computador. Introdução ao uso de Ponteiros • Usar o ponteiro nestas circunstâncias pode levar a um travamento do micro, ou a algo pior. • O ponteiro deve ser inicializado (apontado para algum lugar conhecido) antes de ser usado. Isto é de suma importância! • Se declararmos o ponteiro fora de qualquer função ele é automaticamente inicializado com um valor que garantidamente não aponta para nenhuma posição de memória. Este valor é NULL. Introdução ao uso de Ponteiros • Para atribuir um valor a um ponteiro recém-criado poderíamos igualá-lo a um valor de memória. • Mas, como saber a posição na memória de uma variável do nosso programa? • Seria muito difícil saber o endereço de cada variável que usamos, mesmo porque estes endereços são determinados pelo compilador na hora da compilação e realocados na execução. Introdução ao uso de Ponteiros • Para saber o endereço de uma variável basta usar o operador &. Veja o exemplo: int count=10; int *pt; pt=&count; • Criamos um inteiro count com o valor 10 e um apontador para um inteiro pt. A expressão &count nos dá o endereço de count, o qual armazenamos em pt. • Vejamos o programa completo: Introdução ao uso de Ponteiros Introdução ao uso de Ponteiros • Como nós colocamos um endereço em ptr, ele está agora "liberado" para ser usado. • Podemos, por exemplo, alterar o valor de count usando ptr. • Para tanto vamos usar o operador "inverso" do operador &. • É o operador *. No exemplo acima, uma vez que fizemos ptr=&count a expressão *ptr é equivalente ao próprio count. • Isto significa que, se quisermos mudar o valor de count para 12, basta fazer *pt=12. Introdução ao uso de Ponteiros Introdução ao uso de Ponteiros Fixando Conteúdo tipo *variavel; Cria uma variável ponteiro que apontará para o endereço de memória do tipo estabelecido. variavel = &n1; Faz com que a variavel do tipo ponteiro aponte para o endereço de memória da variavel n1 e não ao seu conteúdo. *variavel = n2; *variavel é a mesma coisa de n1, portanto alterar *variavel altera n1. Introdução ao uso de Ponteiros • Qual o resultado do programa abaixo? Introdução ao uso de Ponteiros • O que faz cada linha do programa abaixo? Aritmética de Ponteiros Estrutura de Dados Aritmética de Ponteiros • Existem apenas duas operações aritméticas que podem ser usadas com ponteiros: adição e subtração. • Quando incrementamos um ponteiro ele passa a apontar para o próximo valor do mesmo tipo para o qual o ponteiro aponta. • Isto é, se temos um ponteiro para um inteiro e o incrementamos ele passa a apontar para o próximo inteiro. Aritmética de Ponteiros • Para exemplificar consideremos p1 um ponteiro para um inteiro com o valor atual de 2000. Assuma que os inteiros ocupam 4 bytes de memória. • Após a expressão: p1++; • Quanto será o valor de p1? • p1 contém 2004 e não 2001. Cada vez que p1 é incrementado ele aponta para o próximo inteiro. Aritmética de Ponteiros Aritmética de Ponteiros • se você incrementa um ponteiro char* ele anda 1 byte na memória e se você incrementa um ponteiro double* ele anda 8 bytes na memória. • Este é mais um motivo pelo qual o compilador precisa saber o tipo de um ponteiro. • O mesmo princípio da adição é aplicado para a subtração. Aritmética de Ponteiros Aritmética de Ponteiros • Você não está limitado a apenas incrementar e decrementar. Você pode também somar ou subtrair inteiros de ponteiros. p1 = p1 + 12; • Faz p1 apontar para o décimo segundo elemento do tipo p1 adiante do elemento que ele está atualmente apontando. ………… p1 1 2 3 4 11 12 13 Aritmética de Ponteiros • É importante compreender que estamos falando de operações com ponteiros e não de operações com o conteúdo das variáveis para as quais eles apontam. (*ptr)++ Incrementa o conteúdo da variável apontada por ptr. ptr++ Aponta para a próxima posição onde ptr se encontra. *(ptr+15) Acessa o conteúdo do ptr quinze posições adiante. Aritmética de Ponteiros O que não pode ser feito com ponteiros • Além de adição e subtração entre um ponteiro e um inteiro, nenhuma outra operação aritmética pode ser efetuada com ponteiros. • Você não pode multiplicar ou dividir ponteiros e não pode adicionar ou subtrair o tipo float ou o tipo double a ponteiros. Teste o programa e Informe o resultado Aritmética de Ponteiros • Uma outra operação, às vezes útil, é a comparação entre dois ponteiros. Mas que informação recebemos quando comparamos dois ponteiros? • Em primeiro lugar, podemos saber se dois ponteiros são iguais ou diferentes (== e !=). No caso de operações do tipo >, <, >= e <= estamos comparando qual ponteiro aponta para uma posição mais alta na memória. • Então uma comparação entre ponteiros pode nos dizer qual dos dois está "mais adiante" na memória. A comparação entre dois ponteiros se escreve como a comparação entre outras duas variáveis quaisquer. Aritmética de Ponteiros Exercícios de Fixação Exercícios de Fixação • Supondo que p é um ponteiro para um inteiro, explique a diferença entre: p+; (*p)++; *(p++) • O que quer dizer *(p+10)? Exercícios de Fixação • Qual o valor de y no final do programa? Após descobrir escreva um comentário em cada comando de atribuição explicando o que ele faz.