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)