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
0sem comentários ainda