Jump to content

C provavelmente problema com ponteiros


Recommended Posts

Posted

Olá pessoal.

Estou aqui com uns problemas que requerem alguma explicação.

tenho a seguinte estrutura:

typedef struct nomes{
char *nome;
char *apelido;
}Nomes;

typedef struct noNomes{
int numNomes;
Nomes *identificacao;
struct noNomes *next;
struct noNomes *prev;
}NoNomes;

Basicamente tenho uma lista que o cabeçalho vai conter a quantidades de nomes inseridos no sistema, um ponteiro para uma estrutura de nomes e ponteiros para os nomes que estão antes e depois na lista.

Para criar a minha lista, crio da seguinte forma:

int criaListaNomes(NoNomes **inicioLista, NoNomes **fimLista){

Nomes *n;
(*inicioLista) = (NoNomes *)malloc(sizeof(NoNomes));
n = (Nomes *)malloc(sizeof(Nomes));

if ((*inicioLista) == NULL || n == NULL){
	return -1;
}

(*inicioLista)->numEquipas = 0;
n->nome ="";
n->localidade = "";
(*inicioLista)->next = NULL;
(*inicioLista)->prev = NULL;

*fimLista = *inicioLista;
return 0;
}

No meu main vou criar a lista, chamar uma função que vai ler os dados de um ficheiro txt e esta função, chama uma função que adiciona cada uma das estruturas de nomes à lista. Os ficheiros estão num txt, linha a linha, com a seguinte forma nome;apelido

Tenho o main assim:

int main()
{
NoNomes *inicioNomes,*fimNomes;

if(criaListaNomes(&inicioNomes,&fimNomes)){
	printf("Erro. Nao alocou memoria ao criar a lista\n");
	return 0;
}

/*Ler os dados dos ficheiros*/
leFicheiroNomes(inicioNomes,&fimNomes);

/*Debug. Avança para o primeiro nome da lista e vê se está a imprimir o que deveria*/
inicioNomes = inicioNomes->next;
printf("#%s\n", (inicioNomes->identificacao)->nome);

return 0;
}

A minha função para ler o ficheiro é a seguinte:

int leFicheiroNomes(NoNomes *inicioLista, NoNomes **fimLista){
char buffer[100], *nome,*apelido;
Nome *n;
FILE *ficheiro;

ficheiro = fopen("ficheiro.txt","r"); /*Abre o ficheiro para escrita*/

if (ficheiro == NULL){
	printf("Erro\n");
}
else{
	while(fgets(buffer, 100, ficheiro)!= NULL){
		n = (Nome*) malloc(sizeof(Nome));
		nome = (char*)malloc(sizeof(char*));
		apelido = (char*)malloc(sizeof(char*));
		if (n == NULL){
			printf("Erro. Nao alocou memoria\n");
			return -1;
		}
		nome = strtok(buffer,";\n");
		apelido = strtok(NULL,";\n");
		n->nome = nome;
		n->apelido = apelido;

/*Debug para ver se a leitura do ficheiro está a ser bem efectuada*/
		printf("####%s\n",n->nome);

		addNomes(inicioLista,fimLista,n);

	}
}

fclose(ficheiro);
return 0;
}

a minha função para adicionar nomes na lista é a seguinte:

/*Cria um nó na lista*/
int addNomes(NoNomes *inicio, NoNomes **fim, Nomes **nomes){
NoNomes *no1, *no2;


no1 = (NoNomes*) malloc(sizeof(NoNomes));
if (no1 == NULL){
	return -1;
}

no2 = (NoNomes*) malloc(sizeof(NoNomes))
if (no2 == NULL){
	return -1;
}

no1->identificacao = nomes;
no1->next = inicio->next;
inicio->next = no1;
no2 = no1->next;

if (no2!= NULL){
	no2->prev = no1;
	no1->prev = inicio;
}
else{
	no1->prev = inicio;
	*fim = no1;
}

inicio->numNomes++;
return 0;
}

O meu problema é o seguinte.

Ao ler o ficheiro, quando faço o debug na função leFicheiroNomes verifico que com a leitura está tudo ok. Os nomes são impressos no ecrã. O problema é que quando envio esses dados para a função addNomes laguma coisa está mal. Na função main, quando faço o debug, é impresso o último nome da minha lista de ficheiros e além disso, o nome não é completo, isto é, se o nome for Manel, é impresso Mane.

Alguém sabe o que estou a fazer mal?

Obrigado,

kodiak

Posted

Repara na tua função leFicheiroNomes

int leFicheiroNomes(NoNomes *inicioLista, NoNomes **fimLista){
       char buffer[100], *nome,*apelido;
       Nome *n;
       FILE *ficheiro;
       ficheiro = fopen("ficheiro.txt","r"); /*Abre o ficheiro para escrita*/
       if (ficheiro == NULL){
               printf("Erro\n");
       }
       else{
               while(fgets(buffer, 100, ficheiro)!= NULL){
                       n = (Nome*) malloc(sizeof(Nome));
                       nome = (char*)malloc(sizeof(char*));
                       apelido = (char*)malloc(sizeof(char*));
                       if (n == NULL){
                               printf("Erro. Nao alocou memoria\n");
                               return -1;
                       }
                       nome = strtok(buffer,";\n");
                       apelido = strtok(NULL,";\n");
                       n->nome = nome;
                       n->apelido = apelido;
/*Debug para ver se a leitura do ficheiro está a ser bem efectuada*/
                       printf("####%s\n",n->nome);
                       addNomes(inicioLista,fimLista,n);
               }
       }
       fclose(ficheiro);
       return 0;
}

As linha assinaladas não "jogam bem" umas com as outras.

Primeiro metes um valor vindo do ficheiro em buffer;

depois reservas uns pedaços de memória (os mallocs);

depois deitas fora essa memória quando metes ponteiros para o buffer nas variaveis;

e a seguir, na proxima leitura, alteras o conteudo para o qual os ponteiros apontam.

Tens de copiar os caracteres para uma zona de memória reservada anteriormente.

What have you tried?

Não respondo a dúvidas por PM

A minha bola de cristal está para compor; deve ficar pronta para a semana.

Torna os teus tópicos mais atractivos e legíveis usando a tag CODE para colorir o código!

Posted

As linha assinaladas não "jogam bem" umas com as outras.

Primeiro metes um valor vindo do ficheiro em buffer;

depois reservas uns pedaços de memória (os mallocs);

depois deitas fora essa memória quando metes ponteiros para o buffer nas variaveis;

e a seguir, na proxima leitura, alteras o conteudo para o qual os ponteiros apontam.

Tens de copiar os caracteres para uma zona de memória reservada anteriormente.

Olá pmg.

Obrigado pela resposta.

A minha ideia era colocar no buffer o que é lido pelo ficheiro, depois trabalhar o conteúdo do buffer para retirar o nome e o apelido e finalmente, pegar nesse conteúdo e colocar na memória que criei com os mallocs.

O que estás a dizer é que o strtok não está a devolver os caracteres mas sim um ponteiro? E é esse ponteiro que eu estou a colocar nos meus chars?

Como posso copiar os caracteres devolvidos? Desculpa mas não estou a dar com isto.

Desde já obrigado,

kodiak

Posted

Para copiar uma string usa a função strcpy(). Mas verifica que tens espaço suficiente no destino!

Como vais fazer isto em loop, certifica-te que cada linha é copiada para um sítio diferente da linha anterior ... senão ficas sempre só com a última linha.

What have you tried?

Não respondo a dúvidas por PM

A minha bola de cristal está para compor; deve ficar pronta para a semana.

Torna os teus tópicos mais atractivos e legíveis usando a tag CODE para colorir o código!

Posted

Para copiar uma string usa a função strcpy(). Mas verifica que tens espaço suficiente no destino!

Como vais fazer isto em loop, certifica-te que cada linha é copiada para um sítio diferente da linha anterior ... senão ficas sempre só com a última linha.

Tinha tentado anteriormente com o strcpy() mas não tinha dado. Quando disseste

Mas verifica que tens espaço suficiente no destino!

Vi que os mallocs não estavam a allocar memória suficiente.

Acho que já está direito. Pelo menos está a mostrar como devia.

Por favor vê se é isto. Alterei para


/*...........*/
           nome = (char*)malloc(31*sizeof(char*));
           apelido = (char*)malloc(31*sizeof(char*));
/*.............*/
           strcpy(nome,strtok(buffer,";\n"));
           strcpy(apelido,strtok(NULL,";\n"));

           n->nome = nome;
           n->apelido= apelido;

Assumindo que é isto que deva fazer, imagino que o próximo passo será fazer a alocação de memória apenas depois de fazer o strtok. Assim atribuo apenas a memória necessária.

É isto?

kodiak

Posted (edited)

O malloc(31 * sizeof (char *)) reserva 124 bytes (suficiente para strings com 123 caracteres). Isso nao é necessariamente o suficiente para os dados "estapafurdios".

Assumindo que é isto que deva fazer, imagino que o próximo passo será fazer a alocação de memória apenas depois de fazer o strtok. Assim atribuo apenas a memória necessária.

Exacto!

                        tmpnome = strtok(buffer, ";\n");
                       if (tmpnome == NULL) abort(); /* erro */
                       nome = malloc(strlen(tmpnome) + 1);
                       strcpy(nome, tmpnome);

O cast ao valor devolvido pelo malloc é, quando muito, redundante; e pode esconder um erro para o qual o compilador te avisaria se não tiveese o tal cast.

Edited by pmg
  • Vote 1

What have you tried?

Não respondo a dúvidas por PM

A minha bola de cristal está para compor; deve ficar pronta para a semana.

Torna os teus tópicos mais atractivos e legíveis usando a tag CODE para colorir o código!

Posted

Obrigado pmg.

Está a tentar não usar uma variável temporária para guardar os dados do strtok mas estava a dar barracada.

Muito obrigado pela ajuda 🙂

kodiak

Posted

Apesar de o tópico já estar resolvido deixo aqui uma sugestão. Tal como o pmg te disse os teus mallocs não estávam a alocar espaço suficiente. Quando fizeres o malloc em vez de um

malloc(número*sizeof(qualquercoisa));

deves utilizar no casa de strings

(char*) malloc((strlen(str)+1)*sizeof(char));

deste modo evitas erros (o +1 deve-se às strings terem o caracter especial \0 que conta como um caracter, nunca te esqueças dele caso contrário vai-te dar erro de memória e passas horas à procura daquilo [experiencia própria])).

Posted

deves utilizar no casa de strings

(char*) malloc((strlen(str)+1)*sizeof(char));

Excepto que o cast ao valor de retorno do malloc é, quando muito, redundante; e pode esconder um erro para o qual o compilador te avisaria se não tiveese o tal cast e portanto nao deve ser usado.

Alem disso, por definicao, o valor de sizeof (char) é 1, sendo por isso perfeitamente desnecessario, ficando assim

ponteiro = malloc(strlen(str) + 1);

Se queres mesmo usar o sizeof no calculo do numero de bytes a reservar, usa o obecto propriamente dito

char *ponteiro;
ponteiro = malloc(nelementos * sizeof *ponteiro);

Assim, se o ponteiro deixar de ser um char * e passar a ser, por exemplo, um wchar_t * a instrucao que faz a reserva de memoria nao precisa de nenhuma alteracao

wchar_t *ponteiro;
ponteiro = malloc(nelementos * sizeof *ponteiro);

What have you tried?

Não respondo a dúvidas por PM

A minha bola de cristal está para compor; deve ficar pronta para a semana.

Torna os teus tópicos mais atractivos e legíveis usando a tag CODE para colorir o código!

Posted (edited)

Apenas utilizei o *sizeof(char) para se perceber que podia ser int, double, etc. Claro que o tamanho de char é 1 logo não é necessário por lá 🙂

Diz-me uma coisa, o facto de utilizarmos o sizeof(something) faz com que o programa seja mais lento?

Se queres mesmo usar o sizeof no calculo do numero de bytes a reservar, usa o obecto propriamente dito

Código :

char *ponteiro;

ponteiro = malloc(nelementos * sizeof *ponteiro);

Assim, se o ponteiro deixar de ser um char * e passar a ser, por exemplo, um wchar_t * a instrucao que faz a reserva de memoria nao precisa de nenhuma alteracao

I saw what you did there 🙂

Vou passar a implementar como disseste. Com pointers é realmente a melhor maneira para fazer as alocações, um gajo está sempre a aprender 🙂

Edited by Happening
Posted (edited)

Diz-me uma coisa, o facto de utilizarmos o sizeof(something) faz com que o programa seja mais lento?

Nao, o sizeof (something) (excepto num caso especial (*)) é uma constante de compilacao. Ter no codigo sizeof (something) ou 42 (quando o something ocupar 42 bytes!) é igual.

(*) O caso especial é quando o (something) é um VLA ("Variable Length Array"). Neste caso o valor de sizeof (something) nao é uma constante, mas é extremamente rapido a calcular ... nao faz o programa mais lento.

Edited by pmg

What have you tried?

Não respondo a dúvidas por PM

A minha bola de cristal está para compor; deve ficar pronta para a semana.

Torna os teus tópicos mais atractivos e legíveis usando a tag CODE para colorir o código!

Posted

Viva pessoal.

Não sei porquê mas não fui notificado dos novos posts à minha questão...

Venho só aqui agradecer ao pmg e ao Happening por terem esclarecido ainda mais o problema.

Obrigado.

kodiak

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.