Correção de Bug [RESOLVIDO]

1. Correção de Bug [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 14/02/2018 - 04:34h

Yaeh! Curtindo o carnaval? Pois eu não....só na IDE aqui :D

Então, estou ou estava escrevendo um programa em c++ para salvar nota de alunos (um clássico de programa) e essas notas elas ficam armazenadas em um arquivo .dat, onde os dados se encontra da seguinte maneira:

0 Matricula-Fantasma 0 0 0 0
1 Gordon-Freeman 10 9.5 0 8.7
2 Gman 0 0 0 0


Então, basicamente o programa funciona as mil maravilhas não opções de inserção de aluno para a atribuição de notas e na verificação de notas, porém a porcaria tem um problema que é sempre que vou alterar uma nota, na opção 'alterar notas', ele fica atribuindo a nota passada na nova inserida, assim:

Acabo de digitar a nota do 1 bimestre e estou a digitar a do segundo bimestre->
https://imgur.com/a/QyaXq

Taaarãããã....olha o que acontequando digita a do segundo bimestre->
https://imgur.com/a/OKs1C

A nota que tava no primeiro bimestre vai para o segundo e isso acontece também quando digito no 1 bimestre 8.5 e ai digito 9.5 no terceiro e a nota do primeiro bimestre simplesmente desaparece. Para contornar esse erro eu tinha que digitar a nota depois escolher a opção 5 e voltar para o menu ai sim voltar e modificar mais notas....

Porque isso acontece? Estou tentando entender e ainda não entendi :(

Link para o código -> https://github.com/IsaakGomes/Nota-de-Alunos/blob/master/Nota-de-Alunos/main.cpp

Ahh! Oque acharam do "programa", bom, ruim ou lixoso?








  


2. Re: Correção de Bug

Paulo
paulo1205

(usa Ubuntu)

Enviado em 14/02/2018 - 08:17h

Eu dei uma examinada por alto no seu programa.

Você cometeu alguns erros que são comuns com iniciantes. Se você for iniciante e ainda não tiver sido devidamente orientado, não tem muito do que se envergonhar.

A primeira coisa que salta ao olhos é a forma como você chama as funções especializadas de dentro de main() e depois volta delas para main(). Parece que você tentou ser bem explícito, mas o que você fez está errado, e é uma fonte de possível estouro do tamanho máximo da pilha de execução. Assim sendo, esse é um erro que você tem de corrigir, e aprender a fazer do jeito correto para não mais incorrer nele.

Quando uma função termina (i.e. chega ao final ou executa um comando return), o programa automaticamente retorna o fluxo de execução para logo depois do ponto em que a função foi chamada. Assim sendo, em vez de ter algo na forma abaixo (semelhante ao que você fez),
int main(){
// Código repetido a cada ciclo (ex.: imprimir menu de opções, ler opção etc.).
switch(opt){
case 1: func1(); break;
case 2: func2(); break;
// ...
}
// Código após decidir sair do programa.
}

void func1(){
// Faz o que tem de fazer.
main(); // Chama main() de novo, desde seu começo.
}

void func2(){
// Faz o que tem de fazer.
main(); // Chama main() de novo, desde seu começo.
}
, o certo seria ter o seguinte:
int main(){
// Código de preparação (executado só uma vez).
do {
// Código repetido a cada ciclo (ex.: imprimir menu de opções, ler opção etc.).
switch(opt){
case 1: func1(); break;
case 2: func2(); break;
// ...
}
} while(condicao_de_repeticao);
// Código após decidir sair do programa.
}

void func1(){
// Faz o que tem de fazer.
// Não chama main(): apenas retorna.
}

void func2(){
// Faz o que tem de fazer.
// Não chama main(): apenas retorna.
}



Outro problema com seu programa é o uso aparentemente inconsistente de arquivos.

A forma mais comum de trabalhar com algo semelhante a uma base de dados é abrir o arquivo uma única vez, no início do programa, com um modo que permita tanto leitura como escrita, e ir fazendo as consultas, inclusões e modificações à medida em que elas forem necessárias. Além disso, os registros costumam ter tamanhos fixos, de modo que você consiga posicionar operações de leitura individuais através de uma conta simples (nº do regsitro desejado vezes o tamanho de cada registro) e que não precise reescrever todo o banco de dados quando tiver de alterar um único registro.

O inconveniente de arquivos com registros de tamanho fixo é que eles dão um pouco mais de trabalho de manipular: em vez de usar std::strings para os campos de texto do registro, você teria de usar arrays de caracteres tradicionais.

Mas trabalhar com texto sequencial tem inconvenientes, a meu ver, maiores. Um deles é que o arquivo não é, como talvez possa parecer, um simples conjunto de linhas de texto, mas sim um conjunto de bytes em sequência. Se um arquivo tem dez linhas e você quiser modificar apenas o conteúdo da quinta linha, a única forma disso acontecer de modo seguro é garantindo que o novo conteúdo da linha tem exatamente o mesmo número de caracteres que a anterior: se o novo tamanho for diferente, você pode acabar ficando com uma quantidade diferente de linhas (se o conteúdo novo for menor), interferindo com o conteúdo das linhas seguintes (se for maior) ou mesmo corromper o arquivo (especialmente no Windows, que indica o fim de linha com dois bytes, em vez de com apenas um).



Se você quiser continuar trabalhando com múltiplas aberturas e arquivo texto sequencial, eu sugiro que você abra o arquivo para gravação com a opção ios::trunc atmbém na hora de regravar o arquivo (e não apenas ao apagar seu conteúdo), a fim de não deixar “restos” no arquivo caso um registro encolha de tamanho.


Ainda mais um problema é a repetição de código muito parecido. Tal repetição fica muito evidente na sua função AlterarNotas(). Facilitaria corrigir esse problema se você trocasse a forma da estrutura de dados de
struct Aluno{
std::string matricula, nomeAluno, resultadoFinal;
float nota_1, nota_2, nota_3, nota_4, media;
};
para
struct Aluno{
std::string matricula, nome /* nomeAluno é redundante, já que a struct já se chama Aluno */, resultadoFinal;
float notas[4], media;
};
. Com isso, sua função de alterar notas poderia ser bem reduzida (mostrando apenas um trecho e sem verificação de erros):
// ...
int n;
cout << "Qual nota deseja alterar (1 a 4)? ";
cin >> n;
// Como os arrays em C têm índices que começam em zero, diminuo 1 do que o usuário tiver digitado.
cout << "Digite a nota do " << (n--) << "º bimestre: ";
cin >> tabelaDeNotas[id].notas[n];
// ...
const auto &aluno=tabelaDeNotas[id];
arquivo << aluno.matricula << ' ' << aluno.nome;
for(const auto &nota: aluno.notas)
arquivo << ' ' << nota;
arquivo << '\n';




Isso ficaria melhor ainda se você criasse funções que fizessem a entrada e a saída do registro, e usasse essas funções para manipular o registro inteiro, em vez de trabalhar com cada campo em todas as operações de E/S posteriores.

ostream operator<<(ostream &os, const Aluno &al){
os << al.matricula << ' ' << al.nome;
for(const auto &nota: al.notas)
os << ' ' << nota;
return os << '\n';
}

istream operator>>(istream &is, Aluno &al){
is >> al.matricula >> al.nome;
for(auto &nota: al.notas)
is >> al.nota;
is.ignore(); // Ignora marca de linha de linha.
return is;
}

// ...

void ObterDados(...){
// ...
istream arquivo("caminho_do_arquivo.dat");
while(arquivo >> tabelaDeNotas[n])
n++;
// O bloco de três linhas acima é agora plenamente suficiente para ler o arquivo todo
// (desde, é claro, que ele tenha menos registros do que o tamanho máximo do array).
}

void GravarDados(...){
// ...
ostream arquivo("caminho_do_arquivo.dat", ios::trunc);
while(n<total_registros && (arquivo << tabelaDeNotas[n]))
n++;
// ...
}



3. Re: Correção de Bug [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 30/03/2018 - 12:36h

Thanks man, mas eu desistir dessa drogra de lingugem com POO!!!






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts