Vetor de estruturas e modularização

1. Vetor de estruturas e modularização

Matth
MattF

(usa Slackware)

Enviado em 08/06/2015 - 17:25h

Usto é meio que uma continuação do meu último tópico. Tenho um código de aproximadamente 500 linhas, então não colocarei aqui. Meu problema é o seguinte:

Declarei um ponteiro para vetor de estrutura dinamicamente alocável na main. Em swguida há basicamente e resumidamente um loop (while(1)) e dentro dele um switch. É um esquema de menu de opções. Cada case do menu tem um função que tem com parâmetro o vetor de estruturas, já que este não poderia ser global.


A primeira função atribui valores ao vetor(a cada campo de sua estrutura). O método para isso foi, criei todas variáveis necessárias, atribui cada valor entrado pelo usuário, pesso sua confirmação, dou um realloc no vetor. Detalhe, inicialmente ovetor é atribuido a um calloc(0,sizeof(estrutura)).

Depois que o usuário entra com os dados, eu coloque para printar usando os valores já dentro do vetor, ou seja, mostrei em função do vetor. Até aqui tudo bem.

Depois disso volta-se ao menu e o usuário poderia entrar na outra função, cuja a função é de espor os dadis que estão no vetor. Então me aparecem caracteres estranhos, que não eram da primeira função. Sendo que repeti a mesma coisa que antes só pacei para a função debaixo.


Sei que ficou difícil de entender, mas peço sua paciência para me ajudar. Resumindo é: o vetor de estrutura muda de valor inesperadamente ao passar de uma função para a outra.

Muito obrigado!


  


2. Re: Vetor de estruturas e modularização

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/06/2015 - 21:48h

O Antigo Testamento prescrevia pena de morte para quem praticasse adivinhação. E a descrição indireta e sumária do que você esperava que o programa fizesse não ajuda absolutamente nada a fazer um diagnóstico decente.

Eu concordo que postar 500 linhas aqui seria ruim. Entretanto, se quiser ajuda aqui, você tem de mostrar pelo menos as partes relevantes do código, que devem incluir as declarações de tipos e variáveis, as alocações, o preenchimento dos dados e a sua exibição. Se for impossível destacar só essas partes, ou se isso for quase o programa inteiro, então poste o código completo num site de postagem de código, como PasteBin ou CodePad.

E já que você mencionou o outro tópico, você deve levar em consideração os conselhos que os membros experientes lhe deram. Por coisas que você disse então e agora, parece que você ignorou dicas valiosas, e prefere insistir com algumas de suas próprias ideias, mesmo depois de ter sido alertado de riscos.


3. Re: Vetor de estruturas e modularização

Matth
MattF

(usa Slackware)

Enviado em 09/06/2015 - 00:17h


Antes de mais nada, me desculpe. Sou muito bom emdeixar as coisas incertas mesmo. Foi mal ae galera! Vou fazer uma simplificaçãodo que é meu problema em poucas linhas (não compilei e testei isso, estou fazendo agora para exemplificar o problema):



#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct Sobjeto{

char a1[50];
int a2;
}objeto;

int tam=0;



void escrev(objeto *pobj){
char a[50];
int be=0, i;

printf("Entre com o primeiro campo\n");
__fpurge(stdin);
gets(a);

__fpurge(stdin);
printf("Entre com o segundo campo\n");
scanf("%d",&be);

__fpurge(stdin);

tam++;
pobj=(objeto *)realloc(pobj,tam * sizeof(objeto));

strcpy((pobj[tam-1]).a1,a);
(pobj[tam-1]).a2=be;


for (i=0;i<tam;i++){
printf("Informações entradas: \n %s \n %d", pobj[i].a1, pobj[i].a2);
}
}


void ler(objeto *pobj){
int i;
for (i=0;i<tam;i++){
printf("Informações entradas: \n %s \n %d", pobj[i].a1, pobj[i].a2);
}


}

int main(){
objeto *pobj=(objeto *)calloc(tam,sizeof(objeto));
int opc;


while(1){

__fpurge(stdin);
printf("\n\nEntre com 1 para preencher ou 2 para ver, 3 para parar\n");
scanf("%d", &opc);

switch (opc){

case 1:
escrev(pobj);
break;

case 2:
ler(pobj);
break;
case 3:
return 0;

default:
printf("Opção inválida!");

}
}
}




O problema é que a função ver printa caracteres estranhos, parecendo lixo de memória. Há algo errado com esse código? O original possui uma estrutura bem mais complicada.








4. Re: Vetor de estruturas e modularização

Matth
MattF

(usa Slackware)

Enviado em 09/06/2015 - 21:18h

Então, algum comentário pelo menos? testei esse programa acima agora e ele funcionou. É a primeira vez que fico triste quando um programa funciona. Porque será que o principal não? É basicamente o mesmo acima só que com uma estrutura complicada.


5. Re: Vetor de estruturas e modularização

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/06/2015 - 22:40h

MattF escreveu:


Antes de mais nada, me desculpe. Sou muito bom emdeixar as coisas incertas mesmo. Foi mal ae galera! Vou fazer uma simplificaçãodo que é meu problema em poucas linhas (não compilei e testei isso, estou fazendo agora para exemplificar o problema):



#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct Sobjeto{

char a1[50];
int a2;
}objeto;

int tam=0;


Não acha que é uma inconsistência usar uma variável global para controlar o tamanho de um objeto dinâmico declarado e tratado localmente por uma função, e passado entre funções através de argumentos e valores de retorno?




void escrev(objeto *pobj){
char a[50];
int be=0, j;

printf("Entre com o primeiro campo\n");
__fpurge(stdin);


__fpurge(stdin) é algo tão ruim quanto fflush(stdin). Quer ver uma forma de fazer o que você quer, mas de um jeito universal e totalmente portátil? Veja.

scanf("%*[^\n]%*1[\n]"); 


Essa dica vale para todas as outras ocorrências de __fpurge(stdin) ou fflush(stdin) no seu código.

        gets(a); 


Não use gets(). Essa função tinha um erro de projeto que a tornava insegura. Tão insegura, que ela foi marcada para remoção no padrão do C de 1999 e finalmente removida da biblioteca no padrão de 2011.

Em seu lugar, prefira usar fgets(), que existe desde que surgiu a biblioteca de I/O, ou gets_s(), que veio com o padrão de 2011.



__fpurge(stdin);
printf("Entre com o segundo campo\n");
scanf("%d",&be);
__fpurge(stdin);

tam++;
pobj=(objeto *)realloc(pobj,tam * sizeof(objeto));


Aqui você incorre em dois erros, que se somam para produzir o caos.

O primeiro é infelizmente muito comum, e reside no fato de você usar a mesma variável que contém o endereço a ser realocado como destino do endereço conseguido após a realocação. O problema com esse código é que a realocação pode falhar e, se isso acontecer, o apontamento para a região de memória original, embora garantidamente preservada, é perdido, uma vez que a variável recebe o valor de ponteiro nulo, que é o resultado de realloc() em caso de falha.

A forma correta de realocar um ponteiro qualquer p é a seguinte.

/*
“p” é um ponteiro qualquer a ser realocado.
“current_size” é o tamanho da área apontada por p.
*/
void *new_p;

new_p=realloc(p, new_size);
if(!new_p){
/*
Realocação falhou. O valor original de p, contudo, está
preservado, assim como o conteúdo da memória por ele
indicada. Em alguns casos, prosseguir só com os dados
que já havia é melhor do que abortar o programa; em
outros, não haver espaço para dados novos pode ser
inaceitável, e abortar o programa pode ser o melhor a se
fazer.
*/
}
else{
p=new_p;
current_size=new_size;
}


O segundo erro é que pobj contém meramente uma cópia do valor do ponteiro usado por quem chamou a função. Quando você modifica a cópia, o valor original usado como argumento não é modificado.

O que pode acontecer é completamente indeterminado. Algumas possibilidades são:

1) realloc() falha: como você não testa essa possibilidade no código que vem depois, possivelmente vai receber uma falha de segmentação na hora de executar a strcpy() ou a atribuição quem vêm abaixo usando o valor nulo em pobj.

2) realloc() funciona e não altera o valor de pobj: alguns diriam que esse é o melhor cenário; eu não, porque caracteriza um bug que está latente no seu código, que você dá o AZAR de não ver. Isso acontece se o alocador encontrar espaço livre ao final do bloco apontado pelo valor original de pobj e conseguir alocar a memória adicional apenas aumentando o tamanho associado ao ponteiro. Nesse caso, tanto a cópia contida em pobj quanto o valor original de quem chamou a função continuam apontando para o mesmo lugar. Apesar dos erros latentes, a execução do programa continua sem que o usuário sinta... até o dia em que a realocação calhar de não pode mais ficar com memória contígua, e acontecer o que descrevo a seguir.

3) realloc() funciona, mas a realocação é feita para uma região de memória nova. Nesse caso, os dados são copiados para a nova área, e a área antiga teoricamente é devolvida para o alocador; o endereço da área nova é devolvido por realloc() e é guardado em pobj mas, como pobj é só uma cópia, não é refletido para que chamou escrev(). Os dados que acabaram de ser lidos serão copiados para a nova área indicada por pobj, mas o chamador não os verá porque ainda apontará para a região antiga.

Quando a função acabar e retornar para quem a chamou, só haverá referência para a região antiga. O que pode acontecer quando essa referência for usada é outra fonte de indeterminação, dependendo muito de como é a implementação interna do alocador e do ambiente de execução. Se o alocador já tiver devolvido a memória ao sistema, é possível que o acesso provoque uma falha de segmentação e o programa capote. No entanto, o alocador pode reter aquela área antiga por um algum tempo (ou mesmo para sempre), na expectativa de usá-la para alocar outras coisas que lhe venham a ser solicitadas. Nesse caso, uma referência a essa área por meio do ponteiro original pode encontrar alguns elementos da versão anterior do array dinâmico, ou pode encontrar dados referentes a outros objetos aos quais o alocador vier a dedicar o espaço (já que, para ele, aquele espaço tinha sido liberado).


Eu RECOMENDO FORTEMENTE que você corrija esses erros. Acostume-se a pensar de modo a não mais cometê-los, levando em conta todas as possibilidades de desfecho de cada operação, e também procurando usar apenas construções que lhe deem garantias sobre seu funcionamento.


strcpy((pobj[tam-1]).a1,a);
(pobj[tam-1]).a2=be;


for (j=0;j<tam;j++){
printf("Informações entradas: \n %s \n %d", pobj[j].a1, pobj[j].a2);
}
}


void ler(objeto *pobj){
int j;
for (j=0;j<tam;j++){
printf("Informações entradas: \n %s \n %d", pobj[j].a1, pobj[j].a2);
}


}


É impressão minha, ou você inverteu os nomes de escrev() e ler()?

Aliás, você deve manter consistência nos nomes que usar em seus programas. Ou você dá nomes a todas funções usando verbos no infinitivo, ou dá nome a todas usando verbos na terceira pessoa do singular do presente do indicativo, ou dá nome a todas usando o substantivo que descreve a ação; o que não pode é misturar. Procure não abreviar esses verbos ou substantivos. No entanto, se precisar abreviar um nome excessivamente longo, procure não descaracterizar o padrão escolhido de verbo ou substantivo.

Não misture línguas também: ou chama todas as funções e variáveis em Português, ou as chama todas em Inglês. Dado que outros elementos da linguagem e da biblioteca padrão estão em Inglês, eu prefiro usar Inglês também para os nomes dos meus símbolos.


int main(){
objeto *pobj=(objeto *)calloc(tam,sizeof(objeto));


Já tinha falei sobre essa construção de alocação com tamanho zero. Ela é uma dessas cujo comportamento é variável, a depender da implementação do alocador e do ambiente de execução. Fuja dessas coisas!

No caso acima, é mais seguro você simplesmente colocar o valor do ponteiro nulo nessa variável.

    int opc;


while(1){

__fpurge(stdin);
printf("\n\nEntre com 1 para preencher ou 2 para ver, 3 para parar\n");
scanf("%d", &opc);

switch (opc){

case 1:
escrev(pobj);
break;

case 2:
ler(pobj);
break;
case 3:
return 0;

default:
printf("Opção inválida!");

}
}
}




O problema é que a função ver printa caracteres estranhos, parecendo lixo de memória. Há algo errado com esse código? O original possui uma estrutura bem mais complicada.


Creio que as explicações acima sejam suficientes para você começar a corrigir o programa.


6. Re: Vetor de estruturas e modularização

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/06/2015 - 23:01h

MattF escreveu:

Então, algum comentário pelo menos? testei esse programa acima agora e ele funcionou. É a primeira vez que fico triste quando um programa funciona. Porque será que o principal não? É basicamente o mesmo acima só que com uma estrutura complicada.


Eu já estava escrevendo minha resposta quando você mandou essa mensagem. Para você ver que dá trabalho entender o que você fez e explicar o que precisa ser explicado.

Que bom que você ficou incomodado de não ver o defeito! Não viu porque você caiu naquilo que eu chamei acima de AZAR, dos bugs que ficam latentes enquanto certa função que pode se comportar de várias maneiras permanece com apenas um dos comportamentos possíveis. No dia em que acontece um dos outros casos, a casa cai. Só que você poderia e pode evitá-lo se considerasse e considerar todas as possibilidades que lhe são dadas.


7. Re: Vetor de estruturas e modularização

Matth
MattF

(usa Slackware)

Enviado em 10/06/2015 - 00:47h

paulo1205 escreveu:

MattF escreveu:

Então, algum comentário pelo menos? testei esse programa acima agora e ele funcionou. É a primeira vez que fico triste quando um programa funciona. Porque será que o principal não? É basicamente o mesmo acima só que com uma estrutura complicada.


Eu já estava escrevendo minha resposta quando você mandou essa mensagem. Para você ver que dá trabalho entender o que você fez e explicar o que precisa ser explicado.

Que bom que você ficou incomodado de não ver o defeito! Não viu porque você caiu naquilo que eu chamei acima de AZAR, dos bugs que ficam latentes enquanto certa função que pode se comportar de várias maneiras permanece com apenas um dos comportamentos possíveis. No dia em que acontece um dos outros casos, a casa cai. Só que você poderia e pode evitá-lo se considerasse e considerar todas as possibilidades que lhe são dadas.


Primeiramente não há como agradecer alguém que dedicou tanto tempo com o problema de outra pessoa. Realmente te devo muito pois acabei de ter uma das melhores aulas de C e de programação que já tive. Meu eterno agradecimento Paulo. Mesmo não tendo comessado a corrigir meu código ainda, realmente só tenho a agradecer. Cada dia gosto mais de fórum, vocês são mesmo os melhores, fico muito feliz de fazer parte disso mesmo que minhas colaborações sejam limitadas a perguntar e perguntar, afinal ainda não tenho muito a compartilhar.










Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts