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.
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.