Alocação Dinâmica [RESOLVIDO]

1. Alocação Dinâmica [RESOLVIDO]

Ednux
ednux

(usa Arch Linux)

Enviado em 24/11/2014 - 02:48h

Tenho o seguinte trecho de código e minha dúvida é se estou fazendo corretamente a alocação e como posso verificar o tamanho de *nome, se ele realmente tem o tamanho do nome. O sizeof sempre retorna 8 bytes, acho que deve ser o tamanho de um ponteiro, mas o que eu quero saber é o tamanho do lugar para o qual ele aponta, no caso nome.

Outra dúvida, a função fgets() recebe os caracteres até encontrar o '\n' e no final adiciona o caractere '\O' correto ? Sendo assim ednux seria: 'e','d','n','u','x','\n','\O' , então usando strlen() eu teria 6 caracteres, pois ela para de contar quando encontra o '\O'. O correto seria eu deixar o '\n' ou tirar ele ? e a função strcpy() copia até encontrar o caractere nulo ?

O objetivo desse código é pedir um nome, armazenar ele, medir o tamanho necessário em bytes e armazenar na struct somente o necessário. E se possível uma forma de medir a quantidade de bytes que o nome ocupa dentro da struct.



#include stdio.h
#include stdlib.h
#include string.h

typedef struct CADASTRO {
char *nome;
}cadastro;

cadastro aluno;

int main(void) {
char nome[100];
int num;

printf("Digite seu nome: ");
fgets(nome,100,stdin);
num = strlen(nome) + 1;

printf("%i caracteres\n", num);

aluno.nome = (char *) malloc(num*sizeof(char));

strcpy(aluno.nome, nome);

printf("%s", aluno.nome);


free(aluno.nome);
return 0;
}


Bom é isso, caso você tenha uma forma melhor de se escrever esse código, pode postar embaixo... Eu só queria fugir um pouco dos exemplos genéricos encontrados em livros... Ah eu tenho funções próprias para medir o tamanho de strings e para copiar strings, só coloquei a biblioteca string.h por conveniência. ^^


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 24/11/2014 - 20:48h

Você pode usar sizeof sobre arrays, porque a quantidade de elementos é conhecida no momento da compilação (ou pode gerar código que permita um cálculo simples em tempo de execução, caso você utilize VLAs, introduzidos no C99).

Sobre ponteiros, o tamanho devolvido será, sim, o tamanho da variável ponteiro. Se tentar usar sobre o objeto apontado, o compilador entenderá como o tamanho de apenas um elemento (e.g. se você declarar “const char *pcc="teste";”, “sizeof *pcc” será igual a 1, que é o tamanho de um caráter), até porque o compilador avalia cada expressão independentemente do sentido de longo prazo que nós programadores damos a cada símbolo, e, portato, não tem como saber se um ponteiro em uma dada expressão aponta para um objeto ou para o primeiro elemento de uma série de objetos adjacentes (i.e. tem comportamento de array).

Saber o tamanho reservado para cada objeto alocado dinamicamente é uma operação que não existe porque é absolutamente redundante: você necessariamente já sabia o tamanho na hora que solicitou a alocação. Então, se acha que vai precisar da informação mais tarde, dê um jeito de guardá-la em algum lugar.

Sua alocação está certa. strlen() não considera o byte nulo como parte do tamanho do string. Desse modo, se você quiser copiar o string para outro alocado dinamicamente, tem mesmo de somar 1 para reservar espaço para o terminador. No mundo UNIX e POSIX, existe a função strdup(), que serve justamante para fazer o papel de “dest=malloc(strlen(src)+1)” mais “strcpy(dest,src)”.

Se é interessante guadar o '\n' ou não, cabe a quem usa decidir. Acho que, no seu caso, você poderia extirpá-lo. fgets() entrega o '\n' não para o obrigar a guardá-lo, mas para permitir diagnósticos da entrada. Por exemplo:

char str[N];  /* N>2 */
int len;
if(fgets(str, N, stream)==NULL){
/* Erro ou EOF antes de ler sequer um caráter. */
}
else if((len=strlen(str))==0){
/*
EPA!!! Provavelmente apareceu byte nulo tentando ler
um arquivo binário! fgets() só deveria ler texto!
*/
}
else if(str[len-1]=='\n'){
/* Leu linha de texto com sucesso. */
/*
Na maioria dos casos, '\n' será desprezado antes de
armazenar no local definitivo.

Note que linha vazia (i.e. só '\n', len==1) é um caso
particular de leitura bem sucedida. Talvez você queira
tratá-lo à parte.
*/
}
else if(len==N-1){
/*
Ocupou todos os N caracteres ((N-1) mais byte nulo) e
não encontrou '\n'. Talvez a linha seja longa demais,
caracterizando dado inválido.

Você pode tentar aumentar o buffer de entrada e continuar
do ponto de onde parou ou considerar a entrada inválida
(e consumir/descartar os caracteres excedentes, até encon-
trar o '\n', para que a próxima leitura não seja contami-
nada com esse excesso).
*/
}
else{
/* Falta '\n' mas não ocupou todo o tamanho. */
if(feof(stream)){
/*
Fim do arquivo. Aplicação decide se linha sem '\n'
é aceitável ou não. Muitas aplicações consideram
como sinal de arquivo indevidamente truncado.
*/
}
else if(ferror(stream)){
/*
Erro de leitura. Aplicação decide se aproveita os
bytes lidos ou não.
*/
}
else{
/*
Nem EOF nem erro. Pode ter sido interrompido por
evento externo. Talvez uma nova leitura a partir do
ponto em que parou, para tentar completar a linha.
*/
}
}


3. Re: Alocação Dinâmica [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 24/11/2014 - 20:53h

Ah, outra coisa...

ednux escreveu:

/* (...) */

typedef struct CADASTRO {
char *nome;
}cadastro;

cadastro aluno;

/* (...) */


Se você pretende usar essa estrutura para manipular dados em arquivos, possivelmente quererá usar arrays em lugar de ponteiros. Caso contrário, ao gravar o elemento do tipo cadastro no disco, serão salvos vários campos internos que são ponteiros para dados que provavelmente não existirão naqueles mesmos lugares na hora em que os registros forem lidos.




4. Re: Alocação Dinâmica [RESOLVIDO]

Ednux
ednux

(usa Arch Linux)

Enviado em 25/11/2014 - 12:20h

Obrigado, era exatamente essa a minha dúvida quanto a alocação, pois já havia testado com sizeof e sempre pegava o tamanho do ponteiro ou o valor do primeiro membro. E outra, na minha função de copiar eu tinha esquecido de inserir o caractere nulo, então na hora de imprimir o valor acabei pegando lixo, o que me fez pensar: "poh se eu determinei x bytes para essa variável, não deveria ter espaço restante para vir lixo..." mas agora posso concluir que printf com %s imprime até encontrar um caractere nulo, esteja ele dentro ou não do tamanho especificado pela variável.

Os seus exemplos de uso do caractere enter são ótimos, ainda não tinha visto nada parecido. Embora eu ainda não vá usa-los agora vou guarda-los para outra ocasião.

E sim, eu pretendo salvar os cadastros em um arquivo, na próxima etapa. Puxa! então eu iria encontrar um arquivo cheio de números aleatórios no cadastro, pois estaria salvando os endereços x.x







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts