Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

1. Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

francielle moreira
franmoreira

(usa Outra)

Enviado em 21/05/2015 - 02:16h

estou iniciando agora o curso. Ja fiz o codigo do programa porem esta dando erro na parte de consulta e de descobrir as duas maiores notas, pesquisei muito mas n estou conseguindo. Agradeço mt se alguem puder me ajudar. O codigo que fiz e e este:
#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

void cadastro( int* nummatricula, string* nome, float * nota)
{
int i;
for(i= 0; i<=2;i++)
{
cout<< " digite o numero de matrícula "<< i+1<<":";
cin>> nummatricula[i];
cout<< " digite o nome do aluno"<< i+1<<":";
cin>> nome[i];
cout<<" digite a nota do aluno "<< i+1<<":";
cin>> nota[i];
}
}

void consulta(int* nummatricula, string * nome, float *nota )
{
string nomepesquisa;
int i, matriculapesquisa,opc;
do
{
cout<< "\ndigite 1 para pesquisar por matricula ou 2 para pesquisar por nome:";
cin>> opc;
switch (opc)
{
case 1:
{
cout<< "\ndigite o numero de matricula:";
cin>> matriculapesquisa;
for(i=0; i<=2;i++)
{
if(nummatricula[i] == matriculapesquisa)
{
cout<< "nummatricula: nome: nota:", nummatricula[i],nome[i],nota[i];
break;
}
}
}
case 2:
{
cout<< "\ndigite o nome de pesquisa:";
cin>> nomepesquisa;
for(i=0; i<=2;i++)
{
if(nome[i] == nomepesquisa)
{
cout<< "nummatricula: nome: nota:",nummatricula[i],nome[i],nota[i];
break;
}
}
}

default:
{
cout<< " opção invalida:";
break;
}
}
cout<< "digite 1 para continuar pesquisando:";
cin>> opc;
}
while (opc==1);
}
void relatorio (int* nummatricula, string * nome, float *nota )
{
int i;
for(i= 0; i<=2;i++)
{
cout<< "\n A matricula:"<< nummatricula[i]<<endl;
cout<< "\n O nome do aluno:" << nome[i]<<endl;
cout<< "\n A nota do aluno:" << nota[i]<<endl;
cout<<"________________________________";
}
system ("pause");
system ("cls");
}
float media( float *nota )
{
int i;
float soma = 0,media;

for (i =0; i <=2; i++)
{
soma = soma + nota[i];
}
media = soma/3;
return (media);
}
void maiores_notas (float*nota)
{
float maiornota=0, segundamaior=0;
int i;
for (i=0; i<=2;i++)
{
if(nota[i]>maiornota)
{
maiornota=segundamaior;
}
else
{
if(nota[i]>segundamaior)
{
segundamaior=nota[i];
}
}
}
cout<<" A maior nota é:"<<maiornota;
cout<<" A segunda maior nota é:"<<segundamaior;
system("pause");

}
void main( )
{
int nummatricula[3], opc;
string nome[3];
float nota[3];

do
{

cout<<"#############################################################:"<<endl;
cout<<" # \n\n============== Menu de opções=========== \a\n #:"<<endl;
cout<<"#############################################################:"<<endl;
cout<<"# 1 - Ficha de cadastro\n #:"<<endl;
cout<<"# 2 - Consultar notas\n #:"<<endl;
cout<<"# 3 - Relatorio\n #:"<<endl;
cout<<"# 4 - Media das Notas\n #:"<<endl;
cout<<"# 5 - Maiores notas\n #:"<<endl;
cout<<"# 6 - Sair\n #:"<<endl;
cout<<"#############################################################:"<<endl;
cout << "\nEscolha a opção desejada!\n : ";
cin >> opc;

switch (opc)
{
case 1:
{
cadastro(nummatricula, nome , nota);
break;
}

case 2:
{
consulta(nummatricula,nome, nota);
break;
}

case 3:
{
relatorio(nummatricula,nome,nota);
break;
}

case 4:
{
cout<< " A media das notas e:"<<media(nota);
break;
}

case 5:
{
maiores_notas(nota);
break;
}

case 6:
{
cout<< "Obrigada! Volte sempre:";
break;

}

default:
{
cout<< "opcao invalida:";
break;
}
}
}
while(opc!=6);
system ("pause");
}




  


2. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 21/05/2015 - 09:10h

OIá, vejo que fez seu código no Windows, pois você incluiu a biblioteca stdfx que só é presente no sistema da M$.

Eu dei uma olhada no seu código e ví vários erros.

1° Vetores começam em [0] e não em [1], ou seja, você não utiliza o <= nos for's
2° Não precisa utilizar o system("pause"), utilize cin.get!
3° Você "tentou" portar esse código de C para C++? Pois ví erros como:
cout<< "nummatricula: nome: nota:",nummatricula[i],nome[i],nota[i];

O correto seria:
cout<< "nummatricula:" << nummatricula[ i ] << "nome: " << nome[ i ] << "nota: " << nota[ i ];

4° O retorno do main DEVE ser int! Compiladores da M$ acho que permitem isso, mas está errado. O retorno deve ser 0 se ocorreu tudo bem ou diferente de 0 se ocorreu algum erro!

5° Revise sua verificação de média!

Acho que são coisas fáceis de você resolver. Qualquer coisa tô com seu source aqui.

Espero ter ajudado

[]'s

T+

--


Dr. quanto tempo de vida eu tenho?
- 15 minutos.
E o que o Sr. pode fazer por mim?
- No máximo, um miojo. = )




3. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

francielle moreira
franmoreira

(usa Outra)

Enviado em 21/05/2015 - 13:34h


Primeiramente gostaria de agradecer pelo seu interesse em ajudar!
Bom eu estou iniciando agora esse curso que e apenas um tecnico em informatiaca, tenho pouquissimo conhecimento, e o codigo que fiz foi de acordo com a orientação do professor, fiz em C++, essa e a linguagem de programação que ele ensina. Muitas coias tenho pesquisado na internet, porem tenho dificuldade pois os codigos que vejam sao bem diferentes.

O for sempre faço assim pq foi a forma que o professor explicou no curso. Ele sempre inicio com i=0, e depois <=. Nao consegui entender bem a forma correta, e agradeço muito se vc puder me dar um exemplo.

O system ("pause") e o comando que ele sempre pede para usar, esse que vc citou ele nunca utilizou.
Quanto media, ele sempre retorna dessa forma. Um exemplo se e um programa para ver se o numero e par ou impar, ai ele faz return ("par"). Porem como vou retornar a palavra media eu devo faz a função string media? nao sei.

Eu utilizo o visual studio para testar. O cadastro, o relatorio o menu e a media deram certo. Mas a consulta e o procedimento para saber quais sao as duas maiores notas nao.
sera que vc poderia me dizer se tem algo errado na logica que utilizei?
Desde ja agradeço!


4. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 21/05/2015 - 17:45h

franmoreira escreveu:


Primeiramente gostaria de agradecer pelo seu interesse em ajudar!
Bom eu estou iniciando agora esse curso que e apenas um tecnico em informatiaca, tenho pouquissimo conhecimento, e o codigo que fiz foi de acordo com a orientação do professor, fiz em C++, essa e a linguagem de programação que ele ensina. Muitas coias tenho pesquisado na internet, porem tenho dificuldade pois os codigos que vejam sao bem diferentes.

O for sempre faço assim pq foi a forma que o professor explicou no curso. Ele sempre inicio com i=0, e depois <=. Nao consegui entender bem a forma correta, e agradeço muito se vc puder me dar um exemplo.

O system ("pause") e o comando que ele sempre pede para usar, esse que vc citou ele nunca utilizou.
Quanto media, ele sempre retorna dessa forma. Um exemplo se e um programa para ver se o numero e par ou impar, ai ele faz return ("par"). Porem como vou retornar a palavra media eu devo faz a função string media? nao sei.

Eu utilizo o visual studio para testar. O cadastro, o relatorio o menu e a media deram certo. Mas a consulta e o procedimento para saber quais sao as duas maiores notas nao.
sera que vc poderia me dizer se tem algo errado na logica que utilizei?
Desde ja agradeço!


Bom, na internet nunca haverá o mesmo código, vc sempre terá que adaptar ao seu uso!

Um Array tem a seguinte forma:


int array[2];
array[0] // Posicao 1
array[1] // Posicao 2
array[2] // ERRO! Fora de memoria


Agora um exemplo de For:


int i;
for(i = 0;i<10;i++){
cout << "Numero " << i << endl;
}


Saida:
Numero 0
Numero 1
Numero 2
Numero 3
Numero 4
Numero 5
Numero 6
Numero 7
Numero 8
Numero 9





int i;
for(i = 0;i<=10;i++){
cout << "Numero " << i << endl;
}


Saida:
Numero 0
Numero 1
Numero 2
Numero 3
Numero 4
Numero 5
Numero 6
Numero 7
Numero 8
Numero 9
Numero 10


Ou seja, se fizer o For igual a esse num vetor dará erro.

System("pause"); Chama uma função exclusiva do DOS: pause, que não faz nada além de mostrar uma mensagem na tela e aguardar o enter.
Se fizer algo assim:

cout << "Pressione enter para continuar...\n";
cin.get()


Terá o mesmo efeito, sem falar que o código funcionará em todas as plataformas e será mais leve, pois é uma função interna do C++ e não precisa chamar outro programa só para imprimir uma mensagem! Fuja do SYSTEM!

O que você deseja fazer com a média? Por que não faz algo assim:


int *media(){
/// código de verificacao de media
int medias[2] = {media1, media2};
return media;
}


E funcionará assim.


int *array = media();
cout << array[0] << " " << array[1] << endl;


Já na verificação do maior ou menor, segue um exemplo em portugues:

// Dentro do for
se vetor[i] > vetor[i + 1] então
maior = vetor[i]
senao
maior = vetor[i + 1]
fim-se

se maior == vetor[i] então
menor = vetor[i+1]
fim-se


Espero ter ajudado

[]'s

T+

--

Dr. quanto tempo de vida eu tenho?
- 15 minutos.
E o que o Sr. pode fazer por mim?
- No máximo, um miojo. = )




5. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

francielle moreira
franmoreira

(usa Outra)

Enviado em 22/05/2015 - 00:19h

Obrigada! Ajudou muito, me esclareceu muitas coisas principalmente sobre o for. Se possivel so queria que vc me ajudasse a entender a logica do procedimento em que ele pede os dois numeros maiores. O programa vai ter um vetor com 50notas cadastradas. Ele quer que eu informe quais sao as duas maiores notas. A primeira nota eu vou fazer assim como vc fez se a nota[i] for maior q a maior nota, ela vai receber a maior nota. Mas n sei como faço para descobrir a segunda maior nota..
desde ja agradeço!


6. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 22/05/2015 - 10:40h

Olá!

Desculpa qualquer mal entendido.

Para sortear recomendo fazer algo assim:


#include <stdio.h>
int main(void) {

int vetor[10] = {10,9,8,7,6,5,4,3,2,1};

int tamanho = 10;
int aux;

for(int i=tamanho-1; i >= 1; i--) {

for( int j=0; j < i ; j++) {

if(vetor[j]>vetor[j+1]) {

aux = vetor[j];
vetor[j] = vetor[j+1];
vetor[j+1] = aux;

}
}
}

for( int r = 0; r < 10; ++r) {

printf("%d\n",vetor[r]);
}

return 0;
}



E pesquise a respeito de "Bubble sort"

Espero ter ajudado

[]'s

T+

--

Dr. quanto tempo de vida eu tenho?
- 15 minutos.
E o que o Sr. pode fazer por mim?
- No máximo, um miojo. = )




7. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

francielle moreira
franmoreira

(usa Outra)

Enviado em 22/05/2015 - 12:19h

Ok. Mt obrigada mesmo. Vc ajudou mt e eu e quem peço dsculpas por nao ter explicado bem!


8. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as duas m

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 22/05/2015 - 12:24h

franmoreira escreveu:

Ok. Mt obrigada mesmo. Vc ajudou mt e eu e quem peço dsculpas por nao ter explicado bem!


Denada =D

Marque o tópico como resolvido e clique em melhor resposta =D

Espero ter ajudado

[]'s

T+

--

Dr. quanto tempo de vida eu tenho?
- 15 minutos.
E o que o Sr. pode fazer por mim?
- No máximo, um miojo. = )




9. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as du

Paulo
paulo1205

(usa Ubuntu)

Enviado em 22/05/2015 - 12:37h

Mexi um pouco (ou nem tão pouco) no seu programa. Faço alguns comentários no próximo post.

#include <iostream>
#include <string>
#include <limits>

#ifdef __unix__ // Se for um sistema UNIX...
# include <term.h> // ... usa rotinas para manipular terminal...
# include <unistd.h> // ... junto com a constante STDOUT_FILENO.
#else
# include <cstdlib> // Para usar system("cls") (DOS/Windows).
#endif


using namespace std;


const int MAX_ELEMENTS=3;


inline void ignore_till_eol(istream &is){
is.ignore(numeric_limits<streamsize>::max(), '\n');
}

inline void my_pause(){
cout << "\nPressione ENTER para continuar... ";
ignore_till_eol(cin);
}

inline void my_cls(){
#ifdef __unix__
if(!cur_term)
setupterm(nullptr, STDOUT_FILENO, nullptr);
putp(clear_screen);
#else
// O Windows certamente tem uma forma de evitar chamar comando
// externo também. Só que eu não conheço Windows...
system("cls");
#endif
}


void cadastro(int *nummatricula, string *nome, float *nota){
int i;
for(i= 0; i<MAX_ELEMENTS;i++){
cout << "Digite os dados do " << i+1 << "º aluno:\n";
cout << " Matrícula: ";
cin >> nummatricula[i];
ignore_till_eol(cin);
cout << " Nome: ";
getline(cin, nome[i]);
cout << " Nota: ";
cin >> nota[i];
ignore_till_eol(cin);
cout << '\n';
}
}

void consulta(int *nummatricula, string *nome, float *nota){
string nomepesquisa;
int i, matriculapesquisa,opc;
bool encontrado;
do {
encontrado=false;
cout << "Digite 1 para pesquisar por matricula ou 2 para pesquisar por nome: ";
cin >> opc;
ignore_till_eol(cin);
switch (opc) {
case 1:
cout << "\nDigite o numero de matricula: ";
cin >> matriculapesquisa;
ignore_till_eol(cin);
for(i=0; !encontrado && i<MAX_ELEMENTS; i++){
if(nummatricula[i] == matriculapesquisa){
encontrado=true;
cout <<
"nummatricula: " << nummatricula[i] << "\n"
"nome: " << nome[i] << "\n"
"nota: " << nota[i] << "\n"
;
}
}
if(!encontrado)
cout << "\nNenhum registro encontrado.\n\n";
break;

case 2:
cout << "\nDigite o nome de pesquisa: ";
getline(cin, nomepesquisa);
for(i=0; !encontrado && i<MAX_ELEMENTS; i++){
if(nome[i] == nomepesquisa){
encontrado=true;
cout <<
"nummatricula: " << nummatricula[i] << "\n"
"nome: " << nome[i] << "\n"
"nota: " << nota[i] << "\n"
;
}
}
if(!encontrado)
cout << "\nNenhum registro encontrado.\n\n";
break;

default:
cout << "Opção invalida.\n\n";
}
cout << "Digite 1 para continuar pesquisando, 2 para sair: ";
cin >> opc;
ignore_till_eol(cin);
}
while (opc==1);
}

void relatorio (int *nummatricula, string *nome, float *nota){
int i;
for(i= 0; i<MAX_ELEMENTS;i++){
cout << "\n A matricula: "<< nummatricula[i]<<endl;
cout << "\n O nome do aluno: " << nome[i]<<endl;
cout << "\n A nota do aluno: " << nota[i]<<endl;
cout <<"________________________________";
}
my_pause();
my_cls();
}

float media(float *nota){
int i;
float soma = 0,media;

for (i =0; i <MAX_ELEMENTS; i++)
soma = soma + nota[i];
media = soma/3;
return (media);
}

void maiores_notas(float *nota){
float maiornota, segundamaior;
int i;
segundamaior=maiornota=nota[0];
for (i=1; i<MAX_ELEMENTS;i++){
if(nota[i]>maiornota){
segundamaior=maiornota;
maiornota=nota[i];
}
else if(nota[i]>segundamaior)
segundamaior=nota[i];
}
cout <<" A maior nota é: "<<maiornota;
cout <<" A segunda maior nota é: "<<segundamaior;
my_pause();
}

int main(){
int nummatricula[MAX_ELEMENTS], opc;
string nome[MAX_ELEMENTS];
float nota[MAX_ELEMENTS];
bool cadastro_ok=false;

do {
my_cls();
cout <<
"\n"
"#############################################################\n"
"# ============== Menu de opções ============== #\n"
"#############################################################\n"
"# 1 - Ficha de cadastro #\n"
"# 2 - Consultar notas #\n"
"# 3 - Relatorio #\n"
"# 4 - Media das Notas #\n"
"# 5 - Maiores notas #\n"
"# 6 - Sair #\n"
"#############################################################\n"
"\n"
"Escolha a opção desejada: "
;
cin >> opc;
ignore_till_eol(cin);

my_cls();

if(opc==6){
cout << "Obrigada! Volte sempre.";
break; // Sai do loop do-while.
}
if(opc==1){
cadastro(nummatricula, nome , nota);
cadastro_ok=true;
}
else if(opc<1 || opc>6){
cout << "\nOpcao invalida.\n";
my_pause();
}
else if(cadastro_ok){
switch(opc){
case 2:
consulta(nummatricula,nome, nota);
break;

case 3:
relatorio(nummatricula,nome,nota);
break;

case 4:
cout << "A media das notas e: "<<media(nota);
my_pause();
break;

case 5:
maiores_notas(nota);
}
}
else{
cout << "\nFavor fazer primeiro o cadastro (opção 1).\n";
my_pause();
}
} while(cin);
my_pause();
}



10. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as du

Paulo
paulo1205

(usa Ubuntu)

Enviado em 22/05/2015 - 18:41h

Alguns dos comentários já foram feitos pelo Thihup. De qualquer forma, vou tentar comentar sobre todas as mexidas que eu fiz no seu programa. Vou também apontar alguns erros que permaneceram e pontos que podem ser melhorados.

1) Declaração de main() (abordado pelo Thiago, mas trago algumas observações a mais)

Em C++ para sistemas operacionais de uso geral, como Windows ou Linux, main() tem sempre de devolver um valor inteiro e, portanto, tem de ser declarada com o tipo de retorno int. Esse valor inteiro é usado para informar ao sistema, e eventualmente também a outros programas, quão bem-sucedida foi a execução do programa.

Essa é uma comunicação do seu programa para quem o chamou. Existe também comunicação no sentido do chamador para o seu programa. Quem chama o programa pode modificar o comportamento desejado durante a execução através de argumentos passados na forma de texto no momento em que a execução inicia.

Seu programa pode ou não ter interesse em receber e tratar argumentos, e você indica isso na hora em que declara a função main(), escolhendo uma entre duas opções de parâmetros da função permitidas pelo padrão do C++:

* Se não quiser receber argumentos, deve declarar main() sem parâmetros, deixando os parênteses vazios (ou, por compatibilidade com C, contendo apenas a palavra-chave void).

* Se quiser receber argumentos, deve especificar dois parâmetros na declaração de main(). O primeiro deve ter o tipo int e funciona como um contador da quantidade de argumentos (esse argumento geralmente é chamado de argc, que é uma abreviação de argument counter, mas a rigor pode ter qualquer nome, desde que o tipo de dados esteja correto). O segundo parâmetro é um array de ponteiros para char (esse argumento tipicamente é chamado de argv, que é uma abreviação de agument vector, mas também pode ter qualquer nome, desde que você respeite o tipo de dados), sendo que cada elemento desse array é uma string contendo a representação textual do argumento correspondente.

Por ter tipo de retorno int, o programa deve devolver um valor inteiro antes de chegar ao fim da execução de main(). Por uma convenção compartilhada com a linguagem C, normalmente se assume que a execução bem-sucedida do programa produz um valor de retorno igual a zero. C++ levou essa convenção ao ponto extremo de não exigir que se coloque explicitamente o comando “return 0;” na função main(), assumindo-o implicitamente caso o programador omita um comando return antes do final da definição da função. (Mas atenção que isso só vale para main() -- outras funções que você vier a declarar têm sempre de ser explícitas quanto ao valor de retorno.)


2) É bom evitar o uso de system() (especialmente para coisas triviais, como limpar tela ou fazer pausa, e o Thihup também mencionou isso)

A principal razão é que system() custa caro. Cada chamada a system()

1) cria um processo novo;
2) bloqueia a execução do programa original, esperando o processo novo acabar;
3) no processo novo, executa o interpretador de comandos (comumente chamado de shell) padrão do sistema (/bin/sh no mundo UNIX, CMD.EXE no Windows, COMMAND.COM no DOS, etc.), passando como argumento desse shell a string especificada como argumento de system(), para que ele interprete o comando da forma apropriada;
4) o shell, então, interpreta o comando informado;
5) se o comando puder ser totalmente resolvido pelo próprio shell (melhor hipótese), ele o executa e termina; no entanto, num caso geral, esse comando faz uso de utilitários externos ao shell, o que pode demandar a criação de novos subprocessos e a execução de outros programas externos (cada um deles com um fluxo parecido com este que estou descrevendo);
6) quando o shell termina, termina também o processo que havia sido criado no passo 2;
7) o sistema operacional e/ou ambiente de execução informa sobre o final do processo ao programa original, que retoma execução;
8) ainda dentro de system(), o programa tenta recuperar o código informado como resultado da execução do comando (tal código é justamente aquele que corresponde ao valor de retorno de main(), discutido acima);
9) dependendo do tipo do sistema operacional, a função system() interpreta e possivelmente manipula o valor recebido, e retorna para quem a chamou um valor inteiro dependente dessa interpretação/manipulação;
10) você, que chamou system(), deveria também tratar o valor que ela lhe devolveu.

Além do custo computacional, esse fluxo todo tem várias possibilidades de erro e até mesmo a chance de causar surpresas, executando alguma coisa completamente inesperada. Para ter uma ideia de sobre que surpresas estou falando, imagine que seu programa chame um comando externo, como o NET do Windows (um utilitário que reside no programa executável C:\Windows\System32\NET.EXE) e que serve para um monte de coisas diferentes, como sincronizar relógio, mapear unidades de rede, iniciar e parar serviços do sistema etc.). Se um dia alguém criar um script chamado NET.CMD, que sirva para fazer outra coisa, e o colocar no mesmo diretório que a sua aplicação (ou num diretório que venha antes de C:\Windows\System32 na lista especificada pela variável de ambiente PATH), o seu programa vai parar de chamar o NET.EXE e passar a usar o NET.CMD, sabe-se lá com quais efeitos.

Por fim, além do custo e da maior propensão a erros, ainda existe o fator compatibilidade. Quando você usa system(), não está só se expondo a ter um programa que funciona no sistema operacional X e n]ao funciona no Y. Na verdade, você só sabe que ele vai realizar a função desejada no seu sistema e sob condições as que você usou durante a fase de desenvolvimento e testes. Dependendo da função solicitada, pode não funcionar numa máquina praticamente idêntica à sua, ou até mesmo na sua própria máquina, se você alterar algumas condições (por exemplo: usuário diferente, novos valores em variáveis de ambiente, aplicação de atualizações, mudança de permissões de arquivos etc.).

No programa alterado, eu criei as funções my_pause() e my_cls(), para substituir, respectivamente, system("pause") e system("cls"). No caso de my_pause(), eu construí uma versão que só usa funções padronizadas e que, portanto, vai funcionar em qualquer máquina, com qualquer sistema operacional (mas que depende de não haver marcas de fim de linha sobrando no buffer de std::cin, razão pela qual eu espalhei chamadas à função ignore_till_eol() após vários pontos em que você usava leitura formata; além disso minha função espera que você tecle ENTER, e não qualquer tecla, como o comando PAUSE do MS-DOS ou do Windows).

Já em my_cls(), eu fiz com que ela não dependa de comandos externos quando compilada num ambiente UNIX ou Unix-like. Não fiz o mesmo para o Windows, mantendo dentro dela uma invocação de system("cls"), porque não conheço Windows, e preferi gastar tempo com outras coisas em lugar de procurar a função certa na MSDN, Além do mais, system("cls") serve também para o MS-DOS, cuja solução nativa é diferente da do Windows). Mas você (ou o Thiago, se ele quiser) pode fazê-lo, transformando a solução em algo mais ou menos como segue.

#ifdef __unix__
putp(clear_screen);
#elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) // Não sei se são esses símbolos!
/* solução nativa do Windows */
#elif defined(__DOS__)
/* solução nativa do MS-DOS, ou a própria system("cls") */
#else
/* para outros sistemas, talvez seja melhor só pular uma meia dúzia de linhas em cout. */
#endif



3) Arrays e acesso a seus elementos

Como lembrou o Thiago, quando você declara arrays (ou vetores) em C e C++, você informa uma quantidade inteira de elementos. Se essa quantidade for N, os índices inteiros que você pode usar para acesso aos elementos são aqueles que vão de 0 (zero) a N-1.

No entanto, o Thiago errou no diagnóstico de que o seu programa original extrapolava o maior valor válido para o índice. Não extrapolava, pois você declarava arrays com três elementos (N=3) e fazia um for variando de 0 a 2 (N-1==2, logo OK).

O problema é que a forma padrão entre os programadores C e C++ de construir um laço de repetição que varia a variável de controle inteira ‘i’ entre 0 e N-1 é usando a condição de controle “i<N”, e não explicitamente “i<=N-1”, especialmente quando tal variável é usada como índice de um array de tamanho N.

Essa familiaridade é desejável quando você leva em consideração que, no mundo real e ao longo de sua carreira como programadora ou analista, outros programadores vão interagir com programas que você fizer e vice-versa.

O próprio fato de o Thiago ter sido induzido ao erro de interpretação só pela forma como você escreveu já é um sinal de como é importante falar a mesma língua que a maioria dos seus colegas programadores.

Além da questão da familiaridade, existe um motivo excelente para preferir “i<N” em vez de “i<=N-1”: da primeira forma, você prontamente pega o valor de N e o usa na comparação; da segunda, você tem de realizar uma subtração antes de comparar. Pior ainda: essa subtração é repetida para cada iteração do laço de repetição. [Eu sei que você não fez essas subtrações no seu programa, porque usou direto uma constante numérica. Mas antes a tivesse feito! (Veja o porquê na próxima observação).]

No programa alterado, eu mudei os laços de repetição de modo a usar a forma mais comum de construir laços de repetição.


4) Uso de contantes numéricas

Lá em main(), você declarou três arrays distintos (porém relacionados -- falo mais sobre isso depois), dizendo que eles tinham três elementos cada. Em outras funções, você usou laços de repetição com a variável de controle variando de 0 a 2, e neles percorria os elementos desses desses arrays, passados como argumentos para essas funções através de parâmetros ponteiros.

Quem olha para o código, vê o 3 dentro dos colchetes nas declarações dos arrays e consegue saber o número de elementos em cada array. Só que não sabe por que 3, e não 1, 2, 10 ou 1000000.

Aí a mesma pessoa olha para as outras funções e encontra um 2 numa condição de parada de um laço de repetição. A pessoa precisa ir a fundo no código (tudo bem que, no programa simples em questão, nem é tão fundo assim, mas procure entender a questão de princípio) para compreender que esse 2 é índice de arrays declarados noutra parte do programa, e ainda mais fundo para entender que ele é 2, e não 1, 3, 20 ou 400, porque está ligado àquele 3 que foi usado como tamanho nessas declarações feitas alhures (se você tivesse usado a forma comum de expressar laços de repetição, indicada na observação anterior, haveria pelo menos a coincidência do 3 da declaração com o 3 da condição de controle).

Pior: imagine que você testou e gostou do programa, e agora quer trabalhar com 3000 alunos, em lugar de três. Responda rápido: quantos pontos do seu programa original teriam de ser alterados? E com quais valores?

No programa alterado, eu criei um símbolo constante MAX_ELEMENTS e o empreguei tanto na declaração quanto nos laços de repetição. A escolha do nome tem o componente “max”, abreviação de maximum, indicando que se trata de um limite superior, e também “elements”, que indica que a constante está relacionada a objetos que contêm mais de um elemento. Supostamente, essa combinação é suficientemente clara para que alguém, ao ler tão-somente a primeira linha de um laço de repetição na forma

for(i=0; i<MAX_ELEMENTS; i++) 


não precise de muito esforço (ou seja, ler todo o interior do bloco controlado, e potencialmente declarações feitas em outras partes do programa) para entender que o intuito desse laço de repetição é varrer sucessivos elementos de um array. Além disso, se eu quiser mudar a quantidade de alunos que o programa é capaz de tratar, eu só preciso mexer num ponto do programa, e todo o restante, que depende do valor alterado, vai permanecer totalmente consistente.


5) Acoplamento (ou falta dele) de informações

Não fiz nenhuma alteração dessa categoria no programa porque você provavelmente ainda não aprendeu alguns dos recursos que seriam envolvidos. Mesmo assim, eu exponho aqui um problema, e muito rapidamente comento sobre caminhos evolutivos para uma solução.

Os arrays matricula, nome e nota, declarados dentro de main(), guardam informações relacionadas, mas o fato de eles serem declarados de forma independente a obriga a manualmente garantir a coesão entre eles em em várias partes diferentes do programa.

Se você usasse uma estrutura (struct), poderia agregar todos os dados de um aluno num objeto só. Veja o exemplo abaixo.

struct Aluno_t {
int matricula;
string nome;
float nota;
};


Você poderia ter apenas um array com elementos do tipo Aluno_t (“_t” é um sufixo comumente usado em nomes de tipos definidos pelo usuário). As funções cadastro() e consulta() receberiam apenas um ponteiro, em vez de três. O mesmo elemento de um único array teria todas as informações de um dado aluno, e o acesso a cada peça de informação desse aluno pode ser feito por meio da seleção do campo da estrutura de interesse naquele momento.

Mas ainda há mais informação possível de agregar. Seu programa trata o tamanho do(s) array(s) como uma informação global, não como uma propriedade do array. Você poderia criar uma estrutura agregando o array e todas as suas propriedades. Veja um exemplo.

struct DadosEscolares_t {
Aluno_t alunos[MAX_ELEMENTS];
int qt_alunos_cadastrados; // Inicialmente deve valer 0.
};


Você poderia evoluir bastante seu tipo DadosEscolares_t, especialmente usando ideias e recursos de programação orientada a objetos e programação genérica. Espero que você venha a aprender sobre isso em breve. Na minha opinião, é isso que faz o C++ grandemente superior ao C.

No entanto, boa parte da biblioteca padrão do C++ é dedicada justamente a implementar tipos de dados para trabalhar com coleções de dados. Possivelmente tudo o que você gostaria de fazer com DadosEscolares_t já está pronto, e você provavelmente teria muito pouco a fazer além de aplicar uma versão melhorada de Aluno_t a um dos tipos de coleções já existentes na biblioteca padrão.


6) Formatação de strings

Seu programa original estava desenhando as coisas na tela de uma forma meio bagunçada, e usando mais operações de escrita do que necessário.

No programa alterado, eu uso uma só operação de saída para desenhar uma versão rearrumada do menu de opções.

Nunca vi professor que mostrasse isso, então eu mesmo mostro a você que em C e C++ você pode concatenar constantes string (texto entre aspas), fazendo com que elas formem uma só string. No código abaixo, as duas strings contêm o mesmo texto.

char str1[]="P"  "a"
"u""lo";
char str2[]="Paulo";


Usei isso também na nora de imprimir os resultados das consultas (uma linha acaba com "\n" e a linha seguinte continua essa mesma string). Não é erro nem acidente, mas uma forma de representar visualmente no código do programa uma coisa parecida com o que vai sair na tela, mas economizando em número de operações (cout<<"Pa" "u" "lo" só tem uma string e uma operação de saída; cout<<"Pa"<<"u"<<"lo" produz a mesma saída, mas com três operações de saída com três strings diferentes).


7) Operações de leitura

A leitura de dados formatada, através do operador >> aplicado a um stream de entrada de dados (no caso, sempre std::cin), muitas vezes é conveniente, mas tem algumas características que você precisa conhecer.

Uma delas é que, quando usado para ler strings, ele não permite o uso de espaços. Você não conseguiria, por exemplo, cadastrar um aluno com nome "José da Silva": a operação de leitura leria somente "José", e passaria adiante, deixando " da Silva" (com um espaço antes da palavra “da”) no buffer de leitura. A operação seguinte tentaria ler um valor de ponto flutuante (a nota), pulando o espaço em branco, mas encontrando o caráter 'd', o que causaria um erro na operação de leitura, e deixaria std::cin num estado de falha, provocando falha de todas as operações de leitura posteriores (mesmo as de strings e as chamadas de ignore que eu coloquei no programa).

Eu não tratei todos os possíveis erros de leitura. Fazê-lo adequadamente ou deixaria o código muito poluído ou dependeria de usar exceções, que você provavelmente ainda não aprendeu. Se você deliberadamente digitar uma letra num ponto do programa que espere ler um número, pode acabar com um loop infinito ou outro efeito deletério qualquer.

No entanto, eu troquei a leitura de nomes para usar std::getline(), que lê uma linha inteira, permitindo digitar nomes contendo espaços. Junto com isso, eu criei uma função (ignore_till_eol() (que na verdade é só um apelido para uma chamada a std::istream::ignore() com argumentos adequados) para remover do buffer de entrada a marca de fim de linha (e todos os caracteres que porventura venham antes dela) que fica retida nesse buffer após a execução da operação de leitura formatada com std::istream::operator>>(), e espalhei várias dessa funções pelo programa.

“Para não dizer que eu não falei de flores”, eu coloquei um teste explícito do estado da entrada como condição do final do laço de repetição em main(). Não é grande coisa como garantia de consistência de I/O, pois ainda permite a geração de saída meio espúria como resultado de entrada inadequada, mas é um começo. Coisa parecida poderia ser usada em outros pontos do programa.


8) Uso de switch

Você deve ter visto que eu mudei um bocado os testes feitos dentro de main(). A principal razão foi colocar uma garantia de que você só poderia realizar operações que dependessem dos valores dos arrays depois de ter lido tais valores.

Em consulta(), eu não sei se você errou o local onde queria ter usado break, que você usou para interromper o for, ou se acertou na interrupção do for (porque faz mesmo sentido interrompê-lo naquele ponto) mas simplesmente esqueceu o break do switch. Mas o fato de não ter interrompido o caso do valor 1 fazia com que o fluxo continuasse por dentro do bloco do caso do valor 2 (ou seja: se você pedisse para procurar por matrícula, ele procurava por matrícula e depois procurava também por nome).

Os casos de um switch são análogos a labels usados com o comando goto. Por isso mesmo, vários livros de C falam em “case labels” e “default label”. Se você deixar de usar break, é esperado que o fluxo continue pelos comandos do próximo label. Essa continuação é chamada de fall-through, e algumas vezes é desejável.

Por causa desse modo de funcionamento, o último label dispensa o uso de break.

default não precisa necessariamente ser o último label. Se for útil, especialmente se você desejar fall-through do caso default, ele pode até ser o primeiro da lista.

Não é necessário colocar o código correspondente a cada caso dentro de um bloco delimitado por chaves. Isso só seria útil se você quisesse declarar alguma variável local, sem que ela aparecesse para outros casos dentro do mesmo switch.


9) Seleção dos dois maiores valores

Eu dei a solução pronta. Seu código para a segunda maior nota estava certo, mas você errou no para a maior.

Eu acho que o ponto mais importante é a inicialização. Zero não é o menor valor possível de um dado do tipo float. Dado que existem contextos em que existem notas negativas (por exemplo: concursos em que respostas erradas tiram pontos, para desestimular o cara a chutar a resposta -- se ele errar tentando honestamente acertar, pode ficar com nota negativa), começar com zero pode fazer com que se produza um resultado errado. Por isso mesmo, o melhor é você já começar com um valor que faça parte da sua coleção.


11. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as du

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 23/05/2015 - 10:41h

+1

Boa Paulo!

Gostei bastante da explicação, foi útil para mim também.

Mas gostaria de pedir uma coisinha: eu poderia utilizar aquele trecho explicando o que a função system faz? Com os devidos créditos, claro.

E posso utilizar o "my_cls" também para ajudar os outros?

E dando uma breve procurada na internet, achei esse trecho de código usando a WinAPI que limpa a tela:


#include <windows.h>

void cls(void)
{
COORD coordScreen = {0, 0};
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsole, &csbi);
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;

FillConsoleOutputCharacter(hConsole, TEXT(' '),
dwConSize, coordScreen, &cCharsWritten);

GetConsoleScreenBufferInfo(hConsole, &csbi);

FillConsoleOutputAttribute(hConsole, csbi.wAttributes,
dwConSize, coordScreen, &cCharsWritten);

SetConsoleCursorPosition(hConsole, coordScreen);
}



Grande Abraço!

[]'s

T+

--

Dr. quanto tempo de vida eu tenho?
- 15 minutos.
E o que o Sr. pode fazer por mim?
- No máximo, um miojo. = )




12. Re: Preciso fazer um programa em C para cadastra alunos, consultar, gerar relatorio, descobrir as du

Paulo
paulo1205

(usa Ubuntu)

Enviado em 24/05/2015 - 04:14h

Thihup escreveu:

+1

Boa Paulo!

Gostei bastante da explicação, foi útil para mim também.

Mas gostaria de pedir uma coisinha: eu poderia utilizar aquele trecho explicando o que a função system faz? Com os devidos créditos, claro.


Pode. Mas, se quiser, há uma outra postagem minha aqui no VoL que é mais específica a respeito de quais problemas que podem acontecer, sobretudo quando o comando solicitado não pode ser resolvido diretamente pelo shell. Ela é a terceira mensagem no tópico http://www.vivaolinux.com.br/topico/C-C++/Duvida-com-realloc-em-C.

Eu comecei a escrever um artigo no meu blog sobre o abuso de system() há quase um ano, mas nunca o concluí, e por isso não o publiquei.

E posso utilizar o "my_cls" também para ajudar os outros?

E dando uma breve procurada na internet, achei esse trecho de código usando a WinAPI que limpa a tela:

( ... )


É... Eu vi também. Fiquei um pouco surpreso com o tamanho da coisa mas, pensando bem, é isso mesmo que tem de ser feito lá no fundo, e a simplicidade de putp(clear_screen); (Unix com termcap/terminfo), initscr() (Unix com curses) ou clrscr() (DOS ou Windows com ConIO) é, na verdade, apenas aparente. Em última instância, alguém tem de ir lá na tela e colocar um espaço em branco em cada posição endereçável, e depois deslocar o cursor para o canto superior esquerdo. A WinAPI só deixa explícito esse custo (que, contudo, ainda é bem menor do que chamar system("cls"), até porque o comando CLS vai ter de executar esses mesmos passos) -- o que, no fim das contas, é até didático.

Então, a nossa my_cls() (e as respectivas inclusões de cabeçalhos) vai evoluindo assim.

#ifdef __unix__
# include <unistd.h>
# include <term.h>
#elif defined(_WIN32)
# include <windows.h>
#elif defined(__DOS__) || defined(__MSDOS__)
# include <conio.h>
#endif

void my_cls(void){
#ifdef __unix__
static bool bad_term=false;

if(!bad_term){
// Caso o terminal ainda não tenha sido inicializado, Inicializa-o.
if(!cur_term){
int err_code;
if(setupterm(NULL, STDOUT_FILENO, &err_code)==ERR){
bad_term=true;
goto term_is_bad;
}
}
putp(clear_screen);
return;
}
#elif defined(_WIN32)
static bool bad_console=false;
static HANDLE hConsole=INVALID_HANDLE_VALUE;

if(!bad_console){
if(hConsole==INVALID_HANDLE_VALUE){
hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
if(hConsole==INVALID_HANDLE_VALUE){
bad_console=true;
goto term_is_bad;
}
}

static COORD top_left_coord = { 0, 0 };

CONSOLE_SCREEN_BUFFER_INFO screen_info;
DWORD screen_size, n_chars_affected;
WORD current_output_attributes;

// Pega tamanho atual da tela (executa toda vez, pois pode mudar).
if(!GetConsoleScreenBufferInfo(hConsole, &screen_info)){
bad_console=true;
goto term_is_bad;
}
screen_size=screen_info.dwSize.X*screen_info.dwSize.Y;
current_output_attributes=screen_info.wAttributes;

// Enche a tela de espaços e depois muda os atributos.
if(
!FillConsoleOutputCharacter(
hConsole, static_cast<TCHAR>(' '),
screen_size, top_left_coord, &n_chars_affected
) ||
!FillConsoleOutputAttribute(
hConsole, current_output_attributes,
screen_size, top_left_coord, &n_chars_affected
) ||
!SetConsoleCursorPosition(hConsole, top_left_coord)
){
bad_console=true;
goto term_is_bad;
}
return;
}
#elif defined(__DOS__) || defined(__MSDOS__)
clrscr();
#endif

#if !defined(__DOS__) && !defined(__MSDOS__)
term_is_bad:
/*
Tipo de terminal desconhecido ou com erro. "Limpa" a tela
somente pulando uma quantidade razoável de linhas, quiçá
arrastando o texto anterior para fora.
*/
for(int i=0; i<100; i++)
putchar('\n');
#endif
}


Sobre esse código, alguns comentários:

* Eu mudei os nomes de variáveis para coisas que me parecem mais descritivas do papel de cada uma. Também deixei de lado aquela notação que usa prefixos indicativos do tipo, pois ela é redundante com uma boa escolha de nomes.

* Eu suprimi a dupla chamada a GetConsoleScreenBufferInfo(), pegando a informação necessária de atributo no mesmo momento em que pego a informação de tamanho para calcular a quantidade de caracteres a substituir.

* Eu suspeito que o cálculo desses caracteres esteja superestimando a quantidade de caracteres a alterar. Os consoles do Windows frequentemente são configurados de modo a ter um buffer que é maior do que a área exibida na tela, permitindo que se faça a rolagem para baixo ou para cima, o que às vezes é útil para recuperar informação que já deixou a tela. Do jeito como está o programa, parece-me que ele apaga todo o buffer, e não apenas a parte visível na janela, o que talvez não seja desejável. Possivelmente seria mais interessante preservar parte do conteúdo histórico, calculando top_left_coord em função dos campos srWindow e/ou dwMaximumWindowSize de screen_info. Mas eu não tenho como testar essa hipótese.

* Não testei nada do código para Windows pois tenho nenhuma máquina com Windows com compilador instalado.

* A coisa começou a ficar maior.(e mais feia). O melhor dos mundos seria se todos os sistemas tivessem a mesma interface de programação. Existe, por exemplo, uma implementação nativa de Curses para Windows, chamda pdcurses. Se ela fosse instalada, também no Windows se poderia usar a solução com setupterm() e putp().



01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts