paulo1205
(usa Ubuntu)
Enviado em 29/05/2015 - 17:00h
falke escreveu:
Paulo, me perdoe pelo incomodo, mas se possível, me dê uma luz:
Eu segui o que você falou e o que minha professora havia dito. Tirei duas estruturas, já que não havia necessidade, ajeitei a estrutura de repetição, pois foi um erro bobo, mas não consigo ainda fazer com que meu programa funcione direito. Na verdade eu consigo fazer com que ele grave os dados do primeiro Cliente(nome, dias, total), e consigo ler perfeitamente ele, mas o resta eu não consigo, aparentemente não estou gravando direito, pois ao ler o Cliente[2] e o Cliente[1] ele me dá respostas nada a ver. Caso possa analisar o código está abaixo:
#include <stdio.h>
#include <stdlib.h>
typedef struct Cliente{ //estrutura que usei para gravar
char nome[30];
int dias;
char endereco[101];
float conta;
}Cliente;
O código até aqui não tem nenhum erro, mas permita-me dar uma dica que se aplica a todo o código e a todo código (um artigo definido faz toda a diferença): procure espaçar e alinhar seu código de uma forma que facilite a leitura. À semelhança do que acontece numa redação quando você usa encerra um período com um ponto final ou quando muda de parágrafo, procure pular linhas quando o código do programa passa da realização de uma operação para outra que não seja diretamente relacionada à anterior. Alinhe também o conteúdo de modo a facilitar a visão de o que está subordinado a quê, ou aquilo que compõe o que é composto.
Veja como eu aconselho a reescrever as primeiras linhas do programa.
#include <stdio.h>
#include <stdlib.h>
/* Terminei a seção de includes. Eu costumo pular duas linhas entre seções distintas. */
/*
Comentário dizendo para quê serve struct Cliente, e o porquê de eu ter
usado um typedef para ele.
*/
typedef struct Cliente{
/* O membros são subordinados à estrutura
char nome[30];
int dias;
char endereco[101];
float conta;
} Cliente;
Eu tenho uma crítica ao uso de
typedef s por mera conveniência. Creio que quando o usuário do tipo tiver de saber que se trata de uma estrutura, o que normalmente é quando tem de manipular seus campos de forma individualizada, convém deixar (ou obrigar a) que ele escreva a forma “
struct nome_da_estrutura ”. Só defendo que se use
typedef quando se quiser evitar a manipulação de campos internos pelo usuário.
Acho que essa minha preferência pessoal reflete o que se encontra na biblioteca padrão do C. Por exemplo, é esperado que o usuário manipule separadamente os campos da
struct tm (em <time.h>, e que serve para se obter uma representação de data e hora) ou da
struct div_t (em <stdlib.h>, e que serve para representar o tipo de retorno da função
div (), contendo campos que trazem ao mesmo tempo o quociente e o resto de uma divisão entre dois inteiros). Em contraste com isso, os dados internos do tipo
FILE (definido em <stdio.h> geralmente como estrutura, mas uma implementação criativa poderia usar outra coisa, como array de bytes ou de inteiros) não são expostos para o usuário, a fim evitar a tentação de manipulá-los diretamente e, com isso, criar uma série de dependências de um ambiente específico, que dificulta reaproveitar o código num ambiente diferente (ou mesmo numa evolução futura do ambiente original).
Além da biblioteca padrão do C, várias outras bibliotecas de uso comum se alinham mais ou menos com essa prática de explicitar a estrutura do que deve ser tratado em nível de campos e esconder com
typedef s o que o programador pode ou deve tratar como objeto fechado, aos cuidados da biblioteca feita especificamente para manipulá-lo.
int main(){
int i, opcao;
Cliente ficha[2];
Aqui você disse que o array
ficha tem apenas dois elementos. Isso está em desacordo com a faixa que você usa, mais abaixo, para os índices, que você varia entre 0 e 2 (três valores).
do{
do{
printf("++++++++++++++++ Registro de clientes +++++++++++++++\n\n");
printf("[1] Registro de cliente\n");
printf("[2] Leitura de cliente\n");
printf("[0] Sair\n");
fflush(stdin);
printf("Opcao: ");
scanf("%d", &opcao);
}while(opcao!=1&&opcao!=2&&opcao!=0);
if(opcao==1){
FILE *ptarq = fopen("binario.bin", "wb"); //abrindo arquivo para escrita
for(i=0;i<=2;i++){
Viu?
i assume, a cada iteração, os valores 0, 1 e 2. Logo você usa três elementos, embora só tenha alocado espaço para dois.
system("pause");
system("cls");
fflush(stdin); //coletando informações
printf("Digite o nome do cliente: ");
Você poderia ajudar o usuário, dando uma ideia de quantos clientes ele já cadastrou. Veja o exemplo.
printf("Digite o nome do %dº cliente: ", i+1);
gets (ficha[i ].nome);
printf("Digite o numero de dias na pousada: ");
scanf("%d", &ficha[i ].dias);
fflush(stdin);
printf("Digite o seu endereco: ");
gets(ficha[i ].endereco);
if(ficha[i ].dias<10){
ficha[i ].conta=((ficha[i ].dias*100)+15);
Todos esses parênteses no cálculo da expressão podem ser omitidos.
Mas algo mais do que isso. Do ponto de vista de arquitetura de dados, se o valor da conta depende da quantidade de dias por uma fórmula fixa e conhecida, é redundante ter as duas como membros da estrutura e ou ocupando espaço em disco.
Mesmo que você tenha um componente variável e nem sempre presente, como um desconto ou uma multa, seria interessante você colocar essa informação explicitamente na estrutura (por exemplo, se o cliente pagou a menos do que seria de esperar, é mais útil saber que é porque ele teve um desconto, e não por uma arbitrariedade ou erro qualquer).
}
else{
ficha[i ].conta=((ficha[i ].dias*100)+8);
Mesma observação quanto aos parênteses e quanto à redundância.
}
printf("Total a ser pago: R$%.2f\n", ficha[i ].conta);
fwrite(&ficha, sizeof(Cliente), 1, ptarq);
Você simplesmente não corrigiu a chamada a
fwrite (), pois continua com um ponteiro para o array inteiro (cujo endereço coincide com o do primeiro elemento), mas com a especificação de tamanho de um elemento só. Como esse
fwrite () está dentro do laço de repetição, você grava o primeiro elemento três vezes.
}
fclose(ptarq);
}
else{
if(opcao==2){ //tentativa de leitura do arquivo binario
FILE *ptarq=fopen("binario.bin", "rb");
fread(&ficha, sizeof(Cliente), 1, ptarq);
Aqui você cometeu mesmo erro de não correspondência entre tipos dos argumentos, mas com um efeito final diferente. Como a chamada a
fread () está fora do laço de repetição, parece que você quis ler o array inteiro de uma só vez. No entanto, você informou um tamanho que corresponde a apenas um elemento do array. O efeito final é que você lê apenas o primeiro elemento, e os dois outros ficam inalterados (possivelmente indefinidos).
Um observação adicional, que vale tanto para a escrita quanto para a leitura é que você não testa os valores devolvidos pelas funções. Num mundo ideal, erros de leitura e de escrita nunca ocorrem, mas você deve se preparar para o mundo real, em que é possível que uma operação de I/O tenha sucesso parcial ou falhe completamente. Para saber se tudo correu bem, você deve comparar o valor retornado pela função com o valor do terceiro argumento. Veja detalhes na documentação das funções
fread () e
fwrite () (por exemplo, em
http://netbsd.gw.com/cgi-bin/man-cgi?fread++NetBSD-current ).
for(i=0;i<=2;i++){
puts(ficha[i ].nome);
printf("%d\n", ficha[i ].dias);
puts(ficha[i ].endereco);
printf("%.2f\n", ficha[i ].conta);
system("pause");
}
fclose(ptarq);
}
}
}while(opcao==5);
Veja como esse alinhamento de código está confuso.
Em C, você deve dizer explicitamente qual o valor de retorno da função
main () antes de ela chegar ao final. Isso pode ser feito por meio de um comando “
return valor; ” (minha preferência) ou por meio de uma chamada “
exit(valor); ”, mas tem de ser feito. E, por convenção, um valor de retorno igual a zero indica sucesso, e outro valor indica algum tipo de erro.
(Em C++, o comando
return antes do fim de
main () é opcional, e o compilador assume um
return 0; implícito se ele não estiver presente. Eu, particularmente, não gosto muito disso, pois cria uma inconsistência entre a função
main () e todas as outras funções que retornam algum valor, pois essa opção só existe para
main ().)
PS: Ajeitei a linha de código na estrutura de repetição, desci o "fread" e funcionou, aleluia, MUITO OBRIGADO, mesmo, agora o programa tá dando erro na hora de fechá-lo, vou garimpar aqui pra ver o que faço aheuihaids. Caso alguém veja no futuro, fiz o seguinte:
if(opcao==2){ //tentativa de leitura do arquivo binario
FILE *ptarq=fopen("binario.bin", "rb");
for(j=0;j<=2;j++){
fread(&ficha[j], sizeof(Cliente), 1, ptarq);
puts(ficha[j].nome);
printf("%d\n", ficha[j].dias);
puts(ficha[j].endereco);
printf("%.2f\n", ficha[j].conta); //tentei testar para ver se funcionava lendo o n de dias
system("pause");
(Troquei o
i no bloco acima por
j , mas apenas para evitar os problemas de formatação do fórum, que usa “[
i]” para indicar texto em itálicos.)
Ah, então o código anterior não era a versão final? Então não precisava tê-lo publicado.
Bom, o erro no final é fácil de explicar: quando você gravou o terceiro elemento num array declarado para conter apenas dois elementos, essa operação sobrescreveu memória pertencente a outras variáveis e estruturas de controle do programa, incluindo a informação de o que fazer (ou melhor, o endereço para onde o programa deveria retornar) após o termino da execução da função
main ().