paulo1205
(usa Ubuntu)
Enviado em 02/07/2020 - 20:10h
Uma chamada a
calloc () na forma
p=calloc(n_elementos, tamanho_elementos) é funcionalmente equivalente ao seguinte:
p=malloc(n_elementos*tamanho_elementos);
memset(p, 0, n_elementos*tamanho_elementos);
A vantagem disso é que você sabe que todos os elementos terão um valor conhecido (zero), em vez de valores aleatórios (ou remanescentes de uso anterior daquela memória). Por outro lado, se você sabe que vai sobrescrever todos os elementos após a alocação, o tempo gasto com o preenchimento dos zeros, que parece ser o caso do seu programa, pode ser poupado se você usar
malloc ().
Com relação ao erro que você cometeu, eis uma razão pela qual eu recomendo que
não se usem nomes de tipos na hora de chamar funções de alocação, mas que se faça o compilador inferir o tamanho a partir do tipo do próprio ponteiro que vai receber a alocação.
Compare:
// Usando nomes de tipos.
struct teste { /* ... */ };
struct teste ***cubo_teste;
cubo_teste=malloc(comprimento*sizeof(struct teste **));
for(int x=0; x<comprimento; ++x){
cubo_teste[x]=malloc(largura*sizeof(struct teste *));
for(int y=0; y<largura; ++y)
cubo_teste[x][y]=malloc(altura*sizeof(struct teste));
}
// Fazendo o compilador usar o tipo correto.
struct teste { /* ... */ };
struct teste ***cubo_teste;
cubo_teste=malloc(comprimento*sizeof *cubo_teste);
for(int x=0; x<comprimento; ++x){
cubo_teste[x]=malloc(largura*sizeof *cubo_teste[x]);
for(int y=0; y<largura; ++y)
cubo_teste[x][y]=malloc(altura*sizeof *cubo_teste[x][y]);
}
No primeiro código, eu tive de repetir o nome do tipo em cada operação de alocação. Além disso eu tive de tomar cuidado com a quantidade de asteriscos em cada uma das alocações. Se eu decidir que o cubo não vai mais conter elementos do tipo
struct teste , mas sim
double s ou
int s ou do tipo
struct vetor , eu terei de alterar o nome do tipo na declaração e em cada uma das operações de alocação. Semelhantemente, se eu decidir que cada elemento vai guardar um ponteiro para o dado, em lugar de guardar diretamente os próprios dados, eu terei de ajustar a quantidade de asteriscos na declaração e em cada alocação que aparecer no código. Se eu esquecer de corrigir qualquer uma dessas operações, o programa estará semanticamente errado, mas o compilador não vai avisar do erro porque só consegue analisar a sintaxe.
Fazendo do jeito que eu advogo, acho que a vida fica mais simples: o que aparece do lado esquerdo da atribuição também aparece, precedido de um asterisco, como operando de
sizeof na hora de expressar o tamanho de cada elemento. Com isso, eu não preciso meu preocupar com contar asteriscos corretamente, nem com a necessidade de corrigir cada uma das operações de alocação, caso eu decida trocar o tipo dos elementos do cubo.
O exemplo foi com
malloc (), mas pode ser usado também com
calloc () e
realloc ().
O único caso em que isso não serve é se o tipo do destino da atribuição for
void * , porque não faz sequer sentido tentar obter o conteúdo de um ponteiro para um dado cujo tipo é desconhecido. Nesse caso, pode ser necessário usar o nome de um outro tipo, se tal tipo for fixo e não estiver amarrado a nenhum dado preexistente.
Por outro lado, mesmo quando o ponteiro destino tem o tipo
void * , se houver um dado de referência, pode ser conveniente deixar o compilador ter o trabalho de inferir tipo e tamanho a partir dessa referência, pelos mesmos motivos expostos acima. Um exemplo patente é o uso canônico de realocação, mostrado abaixo.
struct teste *p_dados;
size_t n_dados;
/* ... */
size_t novo_tamanho=recalcula_tamanho_necessario(p_dados, n_dados);
if(n_dados!=novo_tamanho){
// Realocação de p_dados usando a forma "canônica":
// - ponteiro intermediário diferente do ponteiro original (para preservar o valor do ponteiro caso não consiga alocar outra região de memória),
// - tipo do ponteiro intermediário "void *" (desacoplado do tipo do ponteiro original, para diminiur esforço de manutenção, caso o tipo do ponteiro original mude),
// - cálculo do novo tamanho usa o tipo do apontado pelo ponteiro original, inferido pelo compilador a partir da declaração do ponteiro, sem precisar repetir explicitamente.
void *novo_p_dados=realloc(p_dados, novo_tamanho*sizeof *p_dados);
if(novo_p_dados){
n_dados=novo_tamanho;
p_dados=novo_p_dados;
}
else{
printf(stderr, "AVISO: Não foi possível alterar nº de elementos (causa do erro: %s).\nProsseguindo com tamanho anterior. Pressione <ENTER> para continuar.\n", strerror(errno));
int n;
while((n=getchar())!=EOF && n!='\n')
;
}
}
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)