duvida ponteiro para ponteiro [RESOLVIDO]

1. duvida ponteiro para ponteiro [RESOLVIDO]

berghetti
berghetti

(usa Debian)

Enviado em 28/04/2019 - 17:34h

Estou com uma duvida sobre a passagem de um ponteiro para ponteiro para uma função.

função a ser chamada:

void receivAll(int sockIn, uint8_t **buff)
{
ssize_t bytesReceived;
uint8_t *p_buff = *buff; // ponteiro utilzado para percorrer 'buff'

do
{
bytesReceived = recv(sockIn, p_buff, MAXBUFF, 0);
if (bytesReceived == 0) // cliente desconectou
{
puts("cliente desconectado");
*buff = NULL;
return;
}


se chamar assim assim da certo

uint8_t *temp_buff;

temp_buff = (uint8_t *) malloc(MAXBUFF);

receivAll(sock, &temp_buff);
if(temp_buff == NULL){
return DISCONECTED;
}


porem assim não consigo

uint8_t temp_buff[MAXBUFF];

receivAll(sock, &temp_buff);
if(temp_buff == NULL){
return DISCONECTED;
}


ou seja, é possível passar um array local para uma função que recebe argumento **?

gostaria de utilizar a versão sem malloc para não ter que me preocupar em liberar memoria com free, por mais que eu esteja apenas praticando.


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 29/04/2019 - 05:54h

Na resposta anterior, eu estava no celular. Agora posso responder com mais cuidado.

berghetti escreveu:

Estou com uma duvida sobre a passagem de um ponteiro para ponteiro para uma função.

função a ser chamada:

void receivAll(int sockIn, uint8_t **buff)
{
ssize_t bytesReceived;
uint8_t *p_buff = *buff; // ponteiro utilzado para percorrer 'buff'

do
{
bytesReceived = recv(sockIn, p_buff, MAXBUFF, 0);
if (bytesReceived == 0) // cliente desconectou
{
puts("cliente desconectado");
*buff = NULL;
return;
}


Um dos motivos para usar ponteiros como argumentos de função é ter uma maneira de modificar, dentro da função, um objeto que existe fora dessa função. Como todos os argumentos de funções em C são sempre passados por cópia de valor, a forma de conseguir modificar um objeto é passar (uma cópia de) seu endereço, a fim de usar esse endereço dentro da função chamada para chegar ao objeto original.

Assim, o principal motivo para usar ponteiro para ponteiro como argumento de função é se você tiver uma variável ponteiro fora da função e quiser que o valor dessa variável (que já é de um tipo ponteiro) seja modificado pela função. (O outro uso possível é se você tiver um array de ponteiros e quiser passar um ponteiro para o primeiro elemento; como o tipo do primeiro elemento (e de todos os elementos) já é ponteiro, um ponteiro para esse elemento será necessariamente um ponteiro para ponteiros.)

se chamar assim assim da certo
uint8_t *temp_buff;

temp_buff = (uint8_t *) malloc(MAXBUFF);

receivAll(sock, &temp_buff);
if(temp_buff == NULL){
return DISCONECTED;
}


“Dá certo”, nesse caso, quer dizer apenas que compila sem erros, não que o código esteja certo.

Veja que você fez uma alocação dinâmica. No entanto, dentro da função existe um fluxo de execução possível (a saber, quando o valor de retorno de recv() é igual a zero) em que você altera o valor do ponteiro sem o cuidado de o desalocar. Isso pode provocar vazamento de memória num programa que fique rodando por tempo mais longo.

Mas não seria certo colocar código de desalocação dentro da função, pois ela não tem como saber se o argumento passado através do ponteiro foi alocado dinamicamente ou não.

Assim sendo, seria mais prudente que a função sinalizasse erros de alguma outra maneira (por exemplo, alterando o tipo de valor de retorno para outro tipo, tal como bool ou int, e devolver um valor que indicasse sucesso/falha ou a quantidade de bytes recebidos, e deixasse o chamador decidir o que ele vai fazer com o ponteiro em caso de sinalização que indique falha.

porem assim não consigo
uint8_t temp_buff[MAXBUFF];

receivAll(sock, &temp_buff);
if(temp_buff == NULL){
return DISCONECTED;
}


Ainda bem que não, porque os tipos não são compatíveis.

O tipo de “&tempo_buff” é “ponteiro para array com MAXBUFF elementos to tipo uint8_t” (em C, a declaração de uma variável com esse tipo teria a forma “uint8_t (*variavel)[MAXBUFF];”), não “ponteiro para ponteiro para uint8_t”, que é o que o argumento da função espera.

Em C, na maioria dos casos em que se usa o nome de uma variável declarada como array, esse nome produz um valor cujo tipo é um ponteiro para o primeiro elemento. Nesses casos, costuma-se dizer que o array decaiu para ponteiro. Mas há três casos em que o decaimento não acontece, a saber:

  • ao longo da própria declaração (que é, inclusive, o único momento em que o array pode aparecer no lado esquerdo de uma atribuição);

  • quando o nome do array é passado ao operador de cálculo de tamanho ocupado sizeof, fazendo-o calcular o tamanho ocupado pelo array inteiro, considerando-se todos os seus elementos;

  • quando o nome do array é passado ao operador de obtenção de endereço &, obtendo-se o endereço do array como um todo, não o de um elemento em particular, produzindo um valor cujo tipo é “ponteiro para o tipo do array inteiro”, como se viu acima.

O decaimento de array para ponteiro se aplica ao valor produzido pelo uso do nome do array numa expressão, não é uma equivalência entre arrays e ponteiros enquanto tipos de dados, e muito menos equivalência entre variáveis declaradas como arrays e aquelas declaradas como ponteiros. No caso de uma variável declarada como ponteiro, o compilador reserva espaço para que essa variável armazene um endereço qualquer, e que pode ser alterado ao longo de programa; já para um array, o compilador reserva espaço fixo para armazenar os elementos desse array, e o compilador “se lembra” do endereço (fixo) desse espaço fixo, a fim de poder usá-lo quando for necessário, mas esse endereço não está em nenhum lugar acessível ao programa, para que você possa modificá-lo.

char *pc=NULL;  // Declaração com atribuição de valor inicial: cria espaço para guardar um endereço, e coloca nesse espaço o valor do ponteiro nulo.
char ac[50]="Fulano de Tal"; // Declaração com atribuição de valor inicial (ou, mais propriamente, de valores iniciais a cada um 50 dos elementos).

pc=malloc(50); // Posso alterar o valor gravado na região de memória designada por “pc”:
// esse valor agora contém o endereço da região de memória alocada.
ac="Beltrano d'Outro"; // ERRO: não posso reapontar um array para outro lugar (aqui, “ac” produz um valor
// que sofre decaimento de tipo, tornando-se um ponteiro, mas esse ponteiro é calculado,
// não é obtido de um lugar reservado na memória para guardá-lo, então não faz sentido
// tentar alterar esse valor).

// Fazendo de conta que o erro acima não aconteceu...
*pc='E'; // Posso usar o ponteiro para alterar o valor armazenado na região para a qual ele aponta.
*ac='E'; // Posso usar o ponteiro (mesmo obtido por decaimento) para alterar o valor armazenado
// para a qual ele aponta (essa operação é equivalente a “ac[0]='E';” — agora a string contém o
// nome “Eulano de Tal”).

char **ppc; // Cria espaço para guardar um endereço.
char (*pac)[50]; // Cria espaço para guardar um endereço.

ppc=&pc; // Altero o valor, fazendo-o apontar para um objeto válido.
pac=∾ // Altero o valor, fazendo-o apontar para um objeto válido.

*ppc="Paulo1205"; // Uso o ponteiro para obter o objeto original, e altero o valor desse objeto
// (vou provocar um vazamento de memória ao perder a referência a memória
// alocada dinamicamente e guardada em “pc”, mas isso não vem ao caso aqui):
// pc agora aponta para outro lugar, a string "Paulo1205".
*pac="Berghetti"; // ERRO: Aqui também eu uso o ponteiro para obter o objeto original, e até aí
// tudo bem, mas esse objeto se comporta como o objeto original se comportaria,
// decaindo para um ponteiro obtido por cálculo e que, por conseguinte, não pode
// ser alterado como parte de uma atribuição.


ou seja, é possível passar um array local para uma função que recebe argumento **?


Não. Espero que as explicações acima ajudem a entender por que.

gostaria de utilizar a versão sem malloc para não ter que me preocupar em liberar memoria com free, por mais que eu esteja apenas praticando.


Altere a função receivAll(), de modo ter como parâmetro um ponteiro simples, que pode ser usado para se referir a um array por meio de um ponteiro para seu primeiro elemento, e também um outro parâmetro que indique o tamanho máximo do array indicado por esse ponteiro. Mude também o tipo de retorno, de modo a poder retornar um valor que permita a quem chamou reconhecer se a execução foi bem sucedida ou não. Eis um exemplo de esqueleto de implementação.

// Retorna a quantidade de bytes lidos: zero indica fim de dados e -1 indica erro.
int receivAll(int sock, uint8_t *buff, size_t buffsize){
do {
ssize_t recv_count=recv(sock, buff, buffsize, 0);
if(recv_count<=0)
return recv_count; // Retorna permaturamente.
// Faz alguma coisa com bytes lidos.
} while(alguma_condição);
return algum_valor; // Provavelmente positivo, se tudo correu bem.
}



... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)

3. Re: duvida ponteiro para ponteiro [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 28/04/2019 - 18:55h

Por que você acha que precisaria de ponteiro para ponteiro nesse caso?

Eu diria que você não precisa.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


4. Re: duvida ponteiro para ponteiro

berghetti
berghetti

(usa Debian)

Enviado em 28/04/2019 - 19:33h

Foi a maneira que encontrei, gambiarra, de conseguir fazer com que o ponteiro "temp_buff" aponte para NULL quando a condição
if(bytesReceived == 0) 
for satisfeita,

Pensando um pouco mais, creio que vou ter que simplesmente atribuir um valor 0, e não um endereço a buff, para conseguir o efeito.

ou tenho outra opção?


5. Re: duvida ponteiro para ponteiro

berghetti
berghetti

(usa Debian)

Enviado em 29/04/2019 - 17:48h

já para um array, o compilador reserva espaço fixo para armazenar os elementos desse array, e o compilador “se lembra” do endereço (fixo) desse espaço fixo, a fim de poder usá-lo quando for necessário, mas esse endereço não está em nenhum lugar acessível ao programa, para que você possa modificá-lo.


Percebi que era isso minha confusão, tentar mudar para onde o endereço do array apontava.



char (*pac)[50]; // Cria espaço para guardar um endereço.
pac=&#8766; // Altero o valor, fazendo-o apontar para um objeto válido.



não consegui compreender essas declarações,
como leio essa declaração char (*pac)[50] ?
esse # em pac=&#8766; também é novo para mim, em uma rápida pesquisa não achei nada sobre.


Altere a função receivAll(), de modo ter como parâmetro um ponteiro simples, que pode ser usado para se referir a um array por meio de um ponteiro para seu primeiro elemento, e também um outro parâmetro que indique o tamanho máximo do array indicado por esse ponteiro. Mude também o tipo de retorno, de modo a poder retornar um valor que permita a quem chamou reconhecer se a execução foi bem sucedida ou não. Eis um exemplo de esqueleto de implementação.

// Retorna a quantidade de bytes lidos: zero indica fim de dados e -1 indica erro.
int receivAll(int sock, uint8_t *buff, size_t buffsize){
do {
ssize_t recv_count=recv(sock, buff, buffsize, 0);
if(recv_count<=0)
return recv_count; // Retorna permaturamente.
// Faz alguma coisa com bytes lidos.
} while(alguma_condição);
return algum_valor; // Provavelmente positivo, se tudo correu bem.
}


farei isso.

Agradeço a excelente explicação.






6. Re: duvida ponteiro para ponteiro

Paulo
paulo1205

(usa Ubuntu)

Enviado em 29/04/2019 - 20:01h

berghetti escreveu:

não consegui compreender essas declarações,
como leio essa declaração char (*pac)[50] ?


pac é um ponteiro para array com 50 elementos do tipo char.

O programa cdecl é muito útil para converter sintaxe C em linguagem humana (Inglês, no caso) e vice-versa.

esse # em pac=&#8766; também é novo para mim, em uma rápida pesquisa não achei nada sobre.


Isso é o VOL e o Chrome se desentendendo com relação a símbolos HTML de caracteres Unicode, indicados na forma “&nome;” ou “&#número;”. Eu escrevi “pac=&ac;”, e aparentemente o VOL entendeu isso como algum símbolo de Unicode (conhecido como “inverted lazy s”, e que ocupa a posição 8766 (ou 0x223e, em hexadecimal), e que, de fato, de acordo com https://www.compart.com/en/unicode/U+223E, tem “&ac;” como uma de suas representações possíveis). Baita azar.

Editei novamente a postagem original para desfazer essa confusão.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


7. Re: duvida ponteiro para ponteiro [RESOLVIDO]

berghetti
berghetti

(usa Debian)

Enviado em 29/04/2019 - 21:33h

O programa cdecl é muito útil para converter sintaxe C em linguagem humana (Inglês, no caso) e vice-versa.


Obrigado pela dica.







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts