Funções para Editar, Excluir e Buscar em uma pilha em c

1. Funções para Editar, Excluir e Buscar em uma pilha em c

Ricardo Carvalho
ricardorstc

(usa Outra)

Enviado em 17/11/2018 - 16:40h

E aí, pessoa tudo certo?
Estou fazendo um programa que imita uma loja de informática com structs, alocação dinâmica e pilha em c e preciso da ajuda de vcs em algumas funções que não consegui fazer. Preciso que me ajudem fazer uma função para Editar um bloco especifico da pilha, outra função para Apagar um bloco especifico na pilha e uma função para Buscar valores a cima do informado pelo usuário, por exemplo: se o usuário digitar o preço 5, é pra mostrar os produtos com valores acima de 5. Vou mandar o código do que fiz aqui em baixo, mas a função excluir está errada, desconsiderem ela. AGRADEÇO DESDE JÁ A TODOS QUE PUDERAM RESPONDER!


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

typedef struct kabum
{
char desc [30];
int cod;
float valor;

struct kabum *ant;
} p;
p *novo, *topo=NULL;


int i=0;
int quant;
FILE *arquivo;

int inserir ()
{
printf("\n\n QUANTOS PRODUTOS QUER CADASTRAR?: ");
scanf("%d",&quant);

for (i=0; i<quant; i++)
{
novo=(p *) malloc (2*sizeof(p));

fflush(stdin);
printf("\n\n PRODUTO: ");
gets(novo->desc);
printf(" CODIGO: ");
scanf("%d",&novo->cod);
printf(" VALOR: R$");
scanf("%f",&novo->valor);
novo->ant=NULL;
if(topo==NULL) //pilha vazia?
{
topo=novo;

}
else
{
novo->ant=topo;
topo=novo;
}
}
return (0);
}

int mostrar ()
{
p *tmp;
tmp=topo;
if (tmp==NULL) //aqui ele testa p/ saber se a pilha está fazia
{
printf ("\n\n PILHA VAZIA! INSIRA UM DADO E TENTE NOVAMENTE.\n");
}
else
{
while (tmp!=NULL)
{
printf("\n\n PRODUTO: %s\n", tmp->desc);
printf(" CODIGO: %d\n",tmp->cod);
printf(" VALOR: R$%0.2f\n\n",tmp->valor);
tmp=tmp->ant;
}

}

return (0);
}

int deletar ()
{
char alerta;
p *extra;
extra = topo;
if (topo == NULL)
{
printf ("\n\n PILHA VAZIA!\n");
}
else
{
printf("\n\n TEM CERTEZA DE DESEJA EXCLUIR A PILHA? (S/N): ");
scanf("%c",&alerta);
alerta=tolower(alerta);
if (alerta=='s')
{
topo = topo -> ant;
free(extra);
printf("\n PILHA DELETADA! \n");
}
if (alerta=='n')
{
printf("\n PILHA NAO DELETADA! \n");
}
if (alerta!='s' && alerta!='n')
{
printf("\n\n OPCAO INVALIDA\n");
}
}

return(0);
}

int salvar()
{
p *tmp;
tmp=topo;

arquivo=fopen("text.txt","wb");

/* if (tmp==NULL)
{
printf("\n\n ERRO AO SALVAR, PILHA ESTA VAZIA. INSIRA UM DADO E TENTE NOVAMENTE! \n");
}*/
while (tmp!=NULL)
{
fprintf(arquivo,"\n\n PRODUTO: %s \n",tmp->desc);
fprintf(arquivo," CODIGO: %d \n",tmp->cod);
fprintf(arquivo," VALOR: %f ",tmp->valor);
tmp=tmp->ant;
}

fclose(arquivo);
printf("\n\n ARQUIVO SALVO COM SUCESSO!\n");


return 0;
}

int abrir()
{
p *tmp;
tmp=topo;

arquivo=fopen("text.txt","rt");

if (arquivo == NULL)
{
printf("\n\n NAO FOI POSSIVEL ABRIR O ARQUIVO. SALVE E TENTE NOVAMENTE! \n");
}

char frase [10000];
while (fgets(frase, 1000,arquivo)!=NULL)
{
printf("%s\n",frase);
}
fclose(arquivo);

return 0;
}


void sobre ()
{
printf("\n\n");
printf(" *================================ SOBRE ================================*\n");
printf(" | |\n");
printf(" |PROGRAMA CRIADO UTILIZANDO ALOCACAO DINAMICA, STRUCTS E PONTEIROS, COMO|\n");
printf(" |FORMA DE AVALIACAO DA DISCIPLINA DE ESTRUTURA DE DADOS, DO PROFESSOR: |\n");
printf(" |GUSTAVO QUIRINO E APRESENTADO EM SALA DE AULA, COM A FINALIDADE DE |\n");
printf(" |OBTER UMA DAS NOTAS DA SEGUNDA UNIDADE. |\n");
printf(" | |\n");
printf(" | IDENTIFICACAO |\n");
printf(" | |\n");
printf(" |IFBA - CAMPUS BARREIRAS |\n");
printf(" |TURMA: 732 |\n");
printf(" |CURSO: INFORMATICA |\n");
printf(" |PROFESSOR: GUSTAVO QUIRINO |\n");
printf(" |COMPONENTES: LUCAS GOMES, MATEUS SENE E RICARDO CARVALHO |\n");
printf(" | |\n");
printf(" =========================================================================\n");
}

int main ()
{
char op_menu=0, op_menu_interno=0; /*variaveis zeradas porque pode ser que o programa seja exacutado mais de uma vez
por isso eu zerei todas, para não pegar os valores das vezes anteriores. A cada nova execução
todas são zeradas*/

do
{
printf("\n\n");
printf(" *======================= MENU PRINCIPAL =========================*\n");
printf(" | |\n");
printf(" | |\n");
printf(" | A - INSERIR B - MOSTRAR |\n");
printf(" | |\n");
printf(" | C - DELETAR D - EDITAR |\n");
printf(" | |\n");
printf(" | E - BUSCAR F - SALVAR |\n");
printf(" | |\n");
printf(" | G - ABRIR H - SOBRE |\n");
printf(" | |\n");
printf(" | S - SAIR |\n");
printf(" | |\n");
printf(" | |\n");
printf(" ==================================================================\n");
fflush(stdin);
printf("\n Digite...: ");
scanf("%c", &op_menu);
op_menu=tolower(op_menu);
fflush(stdin); //limpa o buffer do teclado
switch(op_menu)
{
case 'a' :
fflush(stdin);
system("cls"); //limpa a tela
inserir();
printf("\n");
do
{
printf(" 0 - Menu principal: ");
scanf("%s",&op_menu_interno);
}
while (op_menu_interno!='0');
system("cls");
break;
fflush(stdin);
case 'b':
system("cls");
mostrar();
printf("\n");
do
{
printf(" 0 - Menu principal: ");
scanf("%s",&op_menu_interno);
}
while (op_menu_interno!='0');
system("cls");
break;


case 'c':
fflush(stdin);
system("cls");
deletar();
printf("\n");
do
{
printf(" 0 - Menu principal: ");
scanf("%s",&op_menu_interno);
}
while (op_menu_interno!='0');
system("cls");
break;
fflush(stdin);
case 'd':
fflush(stdin);
system("cls");
printf("você digitou: 4");
printf("\n");
do
{
printf(" 0 - Menu principal: ");
scanf("%s",&op_menu_interno);
}
while (op_menu_interno!='0');
system("cls");
break;
fflush(stdin);
case 'e':

case 'f':
fflush(stdin);
system("cls");
salvar();
printf("\n");
do
{
printf(" 0 - Menu principal: ");
scanf("%s",&op_menu_interno);
}
while (op_menu_interno!='0');
system("cls");
break;
case 'g':
fflush(stdin);
system("cls");
abrir();
printf("\n");
do
{
printf(" 0 - Menu principal: ");
scanf("%s",&op_menu_interno);
}
while (op_menu_interno!='0');
system("cls");
break;
case 'h':
system("cls");
sobre();
printf("\n");
do
{
printf(" 0 - Menu principal: ");
scanf("%s",&op_menu_interno);
}
while (op_menu_interno!='0');
system("cls");
break;
case 's':
system("cls");
printf("\n");
printf("Finalizando programa...");
printf("\n");
exit(0);
break;
default:
system("cls");
printf("Codigo invalido! Digite novamente");
break;
}

}
while (op_menu!=5);
return (0);
}



  


2. Re: Funções para Editar, Excluir e Buscar em uma pilha em c

Paulo
paulo1205

(usa Ubuntu)

Enviado em 18/11/2018 - 09:39h

Prezado Ricardo,

Eu acho estranho que, num mesmo programa, se fale em pilha e, ao mesmo tempo, editar ou apagar “um bloco específico”, ou buscar certos valores, ou mesmo exibir o conteúdo. Porque, quando se fala em pilha, normalmente se espera que todas as operações sejam feitas no topo da pilha: as operações necessárias são apenas empilhar e desempilhar. Mesmo testar se está vazia (ou saber o tamanho) ou manipular o elemento do topo sem o desempilhar já são extensões da ideia básica. Manipular elementos fora do topo, então, é mais atípico ainda, porque acaba desfazendo o encapsulamento de dados, permitindo que a pilha seja usada como se fosse uma lista, apenas com a peculiaridade de ter elementos dispostos, para fins de pesquisa, na ordem inversa à ordem de inserção.

Tem certeza de que você está mesmo falando de pilha, ou você quis dizer lista?

Isso à parte, eu dei uma olhada no seu código e encontrei algumas coisas que você deveria rever:

  • Um typedef que usa um nome ruim: Eu não sou muito fã de typedefs de um modo geral, mas o que você fez usa um nome particularmente ruim. “p” não apenas não descreve suficientemente o tipo de dados, mas ainda por cima é o nome quase universalmente usado para designar um ponteiro temporário, usado num trecho curto de código. Assim sendo, seu typedef mais confunde do que esclarece.

Minha sugestão é eliminar completamente o typedef, usando literalmente a designação “struct kabum” onde necessário, porque, ao longo do seu programa, você usa o conhecimento de que o tipo é uma estrutura, assim indo contra uma das principais funções de typedef(†). Se, no entanto, você quiser realmente usar typedef (ou se for obrigado a fazê-lo por seu professor(††)), escolha pelo menos um nome mais adequado para o apelido da estrutura.

  • Variáveis globais: A variável novo é usada em apenas uma função do programa, e para representar apenas por pouco tempo uma informação de duração mais longa. Por que então declará-la como variável global? A mesma pergunta se aplica a outras variáveis. No caso de topo, seu uso como global implica que seu programa só pode manipular uma pilha. Se você a transformasse num parâmetro a ser recebido pelas funções que manipulam a pilha, receberia instantaneamente a possibilidade de ter múltiplas pilhas diferentes no mesmo programa.

Minha sugestão, que vale como princípio geral para qualquer programa, é que você procure reduzir ao máximo a quantidade de variáveis globais, limitando seu uso ao caso em que se mostre como a melhor alternativa (ou única possibilidade, em casos extremos).

  • Funções sem protótipos adequados: Em todas as suas declarações de funções, inclusive na de main(), você colocou uma lista de argumentos vazia. Talvez você pense que isso significa que sua função não vai receber argumentos(†††), mas o que você realmente está dizendo é que tais funções podem receber qualquer quantidade de argumentos de quaisquer tipos, efetivamente impedindo o compilador de detectar se as funções foram usadas corretamente.

Minha sugestão é que você dê às funções protótipos que informem que ela não aceita parâmetros, de modo a impedir o compilador de aceitar eventuais argumentos (no caso de inserir(), por exemplo, você a declararia como “int inserir(void)”. Alternativamente, caso você decida acatar a recomendação do item anterior, você terá de ter um parâmetro que indique qual das pilhas deve ser manipulada, e isso poderia requerer algo como “int inserir(struct kabum **topo)”, “int mostrar(const struct kabum *topo)” etc.

  • Forma de usar malloc(): Em C, é desnecessário (e você pode até entender isso como errado, pois efetivamente cria problemas para a manutenção de código) converter o tipo do valor retornado por malloc(), pois um dado do tipo void *, que é o tipo do valor retornado, é automaticamente conversível em qualquer outro tipo de ponteiro para dados. Além disso, eu não entendi a razão de você dobrar o valor de sizeof(p). Por acaso você pensou em alocar dois registros de uma vez, em lugar de um só? Se sim, onde está o uso do segundo registro?

Aliás, o próprio uso de sizeof(p) é um outro problema de manutenção de código. Note que a única linha
novo=(p *) malloc (2*sizeof(p)); 
faz referência ao tipo p três vezes: uma implícita, no tipo declarado de novo, uma explícita, na conversão de tipo do valor de retorno, e outra também explícita, para obter o tamanho da área a ser alocada. Isso é uma redundância e, como tal, deve ser eliminada.

Minha sugestão é que você elimine essas redundâncias, transferindo para o compilador o máximo possível de trabalho. Eu mesmo sugeri, acima, que você trocasse o tipo “p” para outra coisa mais clara e explícita: se você acatar essa sugestão, só nessa linha requereria duas intervenções, além da que já teria sido necessária para a declaração da variável. Por isso, eu recomendo a você que prefira a seguinte forma de fazer a alocação.
novo = malloc(sizeof *novo);
// Isso se lê do seguinte modo: o ponteiro “novo” recebe a área alocada com tamanho (“sizeof”)
// do conteúdo (que será) apontado pelo próprio ponteiro (“*novo”).

Isso ainda tem uma redundância (o ponteiro aparece duas vezes), mas com vantagens: não apenas dois é menor do que três, mas também há ligação diretamente visível, com absolutamente o mesmo nome, entre o que está de um lado e o do outro da atribuição. Além do mais, você pode trocar à vontade o tipo de novo em sua declaração, e a alocação sempre vai dar certo, sem que você tenha de mexer em qualquer outra parte do programa além da declaração da variável.

  • gets(): A função gets() foi removida da biblioteca padrão do C, por ser insegura (não oferecer nenhuma forma de limitar a quantidade máxima de caracteres a ser guardada no vetor) e inadequada para uso geral (não permitir o diagnóstico de se a linha originalmente lida tinha a marca de fim de linha ou se tal marca estaria ausente, o que pode acontecer ao se chegar ao fim do arquivo ou por uma interrupção na fonte dos dados). Para poder usá-la, você tem de usar algum compilador antigo ou habilitar opções para geração de código obsoleto.

Minha sugestão, que mais uma vez serve como princípio geral, é de que você substitua todas as ocorrências de gets() por fgets() ou, se estiver trabalhando apenas com Linux, por getline().

  • fflush(stdin): O padrão do C só define o comportamento de fflush() para streams de saída de dados, não para streams de entrada. Uma implementação de fflush() que descarte dados de um stream de entrada é tão válida quanto outra que faça dar pau no programa, ou uma que formate seu HD, ou ainda uma que dispare mísseis nucleares contra sua casa, desde que todas elas obedeçam ao que o padrão determina para streams de saída.

O comportamento que você deseja era comum no mundo Microsoft, cujos primeiros compiladores C (para MS-DOS) apareceram antes da primeira padronização do C (que ocorreu apenas em 1989), e acabaram sendo transportados também para compiladores do Windows. No mundo UNIX, muitos sistemas não o implementam (alguns trazem uma função separada, chamada fpurge(), para descartar dados na entrada, mas tal função não é padronizada). No Linux, embora ele tenha sido incorporado ao longo da última década na GNU libc, usada por muitas (mas não todas!) distribuições de Linux, a documentação da GNU deixou de indicar que esse descarte funciona, e a documentação on-line (manpage) que vem com o Ubuntu 18.04 diz explicitamente que o descarte pode ser feito com arquivos nos quais seja possível deslocar o ponteiro de leitura, mas não com terminais. Logo, ao menos de acordo com a documentação, é bem possível que a GNU libc tenha voltado a trás em relação a ter adotado o mesmo comportamento do MS-DOS pré-padrão de 1989

Minha sugestão é que você nunca use fflush(stdin) em sistema nenhum. Se quiser ignorar parte do conteúdo do buffer de entrada, faça isso usando funções de leitura padronizadas, descartando o que não lhe interessar e retendo o que precisar. No caso do seu programa, um bom substituto poderia ser simplesmente “scanf(" ")” (com um espaço no meio das aspas) imediatamente antes da próxima operação de leitura (ou seja: sem intercalar o descarte, chamadas a outros tipos de funções, e finalmente a leitura do dado que lhe interessa, como você tinha feito, por exemplo, ao ler a opção em main()).

  • Função mostrar(): Exemplo de função que trata a pilha como lista.

  • Função deletar(): Está incoerente, pois fala em excluir a pilha, o que dá a entender que apagaria a pilha inteira, mas só apaga realmente o nó que está no topo. Além disso, nos testes que você faz sobre a resposta dada pelo usuário ao pedido de confirmação, você repete o mesmo testes várias vezes.

Minha sugestão é que você altere ou a mensagem de alerta, ou o comportamento da função, para que ambos fiquem coerentes. Quanto aos testes redundantes, use else de modo mais efetivo. No seu caso, você poderia fazer um encadeamento if/else if/else, do seguinte modo.
if(alerta=='s'){  // <--- Aqui se testa se é 's'.
/* bla bla bla */
}
else if(alerta=='n'){ // <--- Aqui se testa se é 'n'.
/* ble ble ble */
}
else { // <--- Aqui já se sabe que não é nem 's' nem 'n', logo não precisa testar de novo.
/* bli bli bli */
}


  • Função salvar(): Além de tratar a pilha como lista, você imprime ao final uma mensagem de que a gravação foi bem sucedida, mesmo sem ter testado o resultado de nenhuma das operações de escrita (nem mesmo de fopen()!).

Minha sugestão é de que você não assuma que o simples fato de ter mandado abrir o arquivo é garantia de que ele realmente será criado, nem que todas as chamadas a fprintf() funcionarão, e nem mesmo que a chamada final a fclose() vai conseguir garantir o sucesso das operações anteriores. Em lugar disso, teste todas as operações, e diga que a garvação foi bem -sucedida apenas se nenhuma delas tiver falhado.

  • Invocação de system("cls"): Invocar um comando externo (ainda mais para uma operação simples e supérflua como limpar a tela) é custoso e propenso a erros (veja comentários meus nestes tópicos: https://www.vivaolinux.com.br/topico/C-C++/Duvida-com-realloc-em-C e https://www.vivaolinux.com.br/topico/C-C++/Preciso-fazer-um-programa-em-C-para-cadastra-alunos-consu.... Além do mais, eu fiquei com a impressão de que, em algumas situações, você acaba limpando a tela logo depois de ter enviado uma mensagem de diagnóstico para o usuário, que vai acabar sumindo da tela antes de ele ter tido tempo de ler.

Minha sugestão é que você não perca tempo com essas firulas, a não ser que seu professor o obrigue. Se precisar delas, considere trazer a funcionalidade para dentro do seu programa, para evitar custos e riscos de chamar programa externo.


No mais, as funções para as quais você disse que precisava de ajuda não foram nem mesmo começadas. Seria bom que você pelo menos começasse a tentar fazer, para que pudéssemos ajudar sem ter de fazer tudo por você.

----
 Eu considero que os três principais usos de typedef são os seguintes:

  (1) esconder detalhes de implementação de tipos de dados potencialmente complexos, que só são manipulados internamente por alguma biblioteca especializada (por exemplo: o tipo FILE da biblioteca padrão, que é sempre manipulado internamente pela biblioteca, sem jamais revelar o que vai ali dentro; mesmo se você examinar o arquivo que implementa <stdio.h>, não vai encontrar esses detalhes ali);
  (2) representar de forma não-ambígua tipos de dados que representem conceitos bem definidos, mesmo que os tipos nativos (ou mesmo compostos!) usados para implementá-los varie de arquitetura para arquitetura (por exemplo: size_t, time_t, int32_t ou socket_t, respectivamente em <stddef.h>, <time.h>, <stdint.h> e <socket.h>); e
  (3) para tipos de dados de declaração realmente complexa (principalmente envolvendo ponteiros de funções), criar um apelido que permita simplificar operações que envolvam tal tipo (principalmente operações de conversões tipo).

O uso que você faz não se encaixa em nenhum dos três casos anteriores.


†† Eu suspeito que a insistência em usar typedef em cursos introdutórios de programação em C se deva a pouca familiaridade com C por parte dos professores ou, para aqueles que realmente lidam bem com a linguagem, de superar hábitos adquiridos em outras linguagens (como Pascal e assemelhadas, e possivelmente, mais modernamente, Visual Basic), em que era obrigatório usar definições de tipo para poder usar estruturas de dados semelhantes à struct do C (que em Pascal se chama RECORD).


††† Em C++, até seria o caso. Contudo, você não parece estar usando C++, mas sim C.


3. Re: Funções para Editar, Excluir e Buscar em uma pilha em c

Steve
Steve

(usa Slackware)

Enviado em 18/11/2018 - 10:59h

paulo1205 escreveu: Prezado Ricardo....

É incrível como se aprende com suas respostas kkkkkkkkk O texto que vc respondeu a ele, também me ajudou MUITO a entender mais alguns conceitos...


4. Re: Funções para Editar, Excluir e Buscar em uma pilha em c

Paulo
paulo1205

(usa Ubuntu)

Enviado em 18/11/2018 - 19:43h

Steve escreveu:

O texto que vc respondeu a ele, também me ajudou MUITO a entender mais alguns conceitos...


Fico feliz com isso: como eu gasto um tempo grande escrevendo essas coisas, é bom saber que elas realmente ajudam alguém.

Eu fico, porém, curioso acerca de quais assuntos foram mais úteis. Se você puder dizer o que foi mais útil para você, eu agradeço.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts