Removendo o uso de ponteiro de ponteiro de funções

O uso de ponteiros de ponteiros é uma forma de armazenar estrutura bidimensionais (uma matriz por exemplo) ou quando se deseja atualizar o valor de um ponteiro.
O primeiro caso, é uma ótima forma de representar estruturas bidimensionais, mas o segundo caso deve ser evitado.
Por que evitar o segundo caso? Você terá que se preocupar com acesso ao conteudo usando o operador *, isso deixará seu código menos legível, uma vez que você poderá ter que usar parenteses para indicar a precedência do operador * sobre o operador ->, um código eu seria algo como ptr->k = ptr->k+10 passa a ser (*ptr)->k = (*ptr)->k+10.
Essa falta de legibilidade trás também uma complexidade e pode trazer também confusão, uma vez que você passa a trabalhar com um apontardor para um apontador de onde está a sua estrutura.
Essa forma de acesso indireto é mais lenta que o acesso direto, uma vez que será necessário primeiro descobrir o endereço onde está o ponteiro, depois com esse endereço acessar a estrutura no endereço indicado pelo ponteiro.
Uma forma bastente utilizada do ponteiro de ponteiro é o seguinte
{    …
  MeuTipo * ptr = NULL;
  criaTipo(&ptr)
    …
}
e a função que recebe o endereço do ponteiro:
void criaTipo(MeuTipo ** pdp){    …
  (*pdp) =  (MeuTipo*) malloc ….;
    … inicializa dados em pdp usando (*pdp)
}
O que esse trecho de código faz é uma inicialização de um ponteiro, mas você pode considerar que ptr não apontava para NULL mas para o início de sua lista ligada ou para a raiz de sua árvore enraizada e a função criaTipo irá incluir um novo elemento na sua árvore ou lista (o que pode mudar o apontador).
Temos 3 pontos básicos: Primeiro a chamada da função criaTipo que recebe como parâmetro o endereço do ponteiro ptr (operador &). Veja que ele possui um valor nulo, mas ainda assim possui um endereço válido, uma vez que um ponteiro é uma variável (que armazena endereços de memória) e esse ponteiro está em algum lugar da memória.
Segundo ponto é a inicialização de um espaço de memória (o malloc) que será armazenado no ponteiro que teve seu endereço passado como parâmetro (nessa caso o endereço para o qual ptr apontava é substituido pelo endereço alocado).
O terceiro ponto que é a inicialização dos valores da estrutura (pode ser apontar prox para null) que deve ser feito usando o acesso ao conteudo do ponteiro de ponteiro (*pdp).
Podemos modificar o código para que não seja mais necessário passar o ponteiro de ponteiro (e com isso tornar mais legível o código). Primeiro vamos mudar a função criaTipo trocando o parâmetro de ponteiro de ponteiro para um ponteiro simples e mudando o seu retorno também.
MeuTipo * criaTipo(MeuTipo * pdp) {
    ….
Com essa alteração, é esperado o seguinte comportamento:
Se houve a necessidade de alterer o valor para o qual o ponteiro original apontava, esse novo valor deve ser retornado, se não houve mudança, então o valor anterior (pdp) deve ser retornado.
Para o caso de alocação de memória como o caso anterior:
MeuTipo * criaTipo(MeuTipo * pdp) {
  pdp = (MeuTipo*) malloc ….
    … Inicializa dados em pdp
  return pdp;
}
Veja que agora não será mais necessário usar o operado * e nem (* ). e agora existe um return que retorna o endereço criado.
o código completo do exemplo anterior fica:
{    …
  MeuTipo * ptr = NULL;
  ptr = criaTipo(ptr)
    …
}
e a função que recebe o endereço do ponteiro:
MeuTipo* criaTipo(MeuTipo * p){    …
  p = (MeuTipo*) malloc ….
    … Inicializa dados em p
  return p;
}
Para exemplificar, uma inserção ordenada em uma lista ligada poderia ser algo como
Lista* criaTipo(Lista * ptr, int valor){
  se (ptr == NULL){     //lista vazia
    ptr = (MeuTipo*) malloc ….
    … Inicializa valor em ptr
    return ptr;
  }
  senão{     //Lista não vazia
    Lista * lTmp = malloc …
    … Inicializa valor em lTmp
    se (lTmp->n < ptr->n){   //insere no começo da lista
lTmp->prox = ptr
return lTmp;
    }senão {
      …
      Trata outros casos de inserção em uma lista
    }
  }
  return ptr;
}
Veja que podem ser retornados: um novo valor caso a lista esteja vazia (ptr = (MeuTipo*) malloc), uma novo valor caso a lista não esteja vazia mas o valor deve ser inserido no começo da lista (return lTmp;), ou pode ser retornado o mesmo valor de ptr (return ptr;).
Se ficarem com dúvidas, deixe um comentário
Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: