Controlador de ganho automático baseado numa plataforma FPGA
Estruturas de dados dinâmicos em linguagem C
1. Instituto Politécnico do Cávado e do Ave
Escola Superior de Tecnologia
Engenharia Eletrotécnica e de Computadores
Estruturas de dados dinâmicos em
linguagem C
Paulo Lima
Junho de 2013
2. ii
Resumo
Este trabalho, teve como objetivo o desenvolvimento de aplicações em
linguagem C, utilizando estruturas de dados dinâmicas.
O trabalho foi dividido em três partes, que correspondentes a diferentes
aplicações a desenvolver: gestão de um arquivo de exames de diagnóstico médico,
gestão de um simposium de medicamentos genéricos e gestão de uma prova
desportiva.
Em cada aplicação desenvolvida, foi aplicado um tipo de estrutura de dados,
sendo o objetivo utilizar listas ligadas simples, listas duplamente ligadas e finalmente
as árvores binárias de procura.
Foram analisados os problemas propostos, estruturados os dados, desenvolvidas
as funções e procedimentos de modo a criar as aplicações e no final aplicaram-se
testes ao sistema de modo a precaver possíveis erros.
Todas as aplicações foram construídas de modo a evitar situações problemáticas
para o utilizador e foram inseridos mecanismos de proteção na inserção de dados de
modo a evitar bugs e possíveis situações de conflito do sistema.
Palavras-Chave (Tema): Estruturas de dados, Listas ligadas simples, Listas
duplamente ligadas, Árvores binárias de procura.
Palavras-Chave (Tecnologias): Visual Studio, Linguagem C.
“Andar sobre as águas e fazer software a partir de uma especificação, é simples se
ambas estiverem congeladas.”
Edward V. Berard
3. iii
Índice
Resumo..............................................................................................................................ii
Índice ................................................................................................................................iii
Introdução ........................................................................................................................ 1
1 Linguagem C ............................................................................................................. 2
2 Estruturas de dados.................................................................................................. 3
2.1 Listas ligadas simples ......................................................................................... 3
2.2 Listas duplamente ligadas.................................................................................. 4
2.3 Árvores binárias de procura............................................................................... 5
Capítulo I........................................................................................................................... 6
1 Contexto ................................................................................................................... 7
2 Descrição Técnica ..................................................................................................... 8
2.1 Variáveis............................................................................................................. 8
2.2 Funções e procedimentos.................................................................................. 9
2.2.1 Inserir registo no início da lista ligada........................................................ 9
2.2.2 Inserir registo no fim da lista ligada ........................................................... 9
2.2.3 Introduzir dados num registo pelo utilizador........................................... 10
2.2.4 Listar registos da lista ligada..................................................................... 10
2.2.5 Gravar lista ligada em ficheiro de texto ................................................... 10
2.2.6 Carregar lista ligada do ficheiro de texto ................................................. 10
2.2.7 Pesquisar exame por paciente ................................................................. 10
2.2.8 Pesquisar exames por período de tempo ................................................ 10
2.2.9 Alterar dados de um exame ..................................................................... 11
2.2.10 Validar um código de exame .................................................................... 11
4. iv
2.2.11 Validar operações com a lista ligada ........................................................ 11
2.2.12 Apresentar primeira página...................................................................... 11
2.2.13 Apresentar menu...................................................................................... 11
2.2.14 Correr o menu .......................................................................................... 11
3 Desenvolvimento.................................................................................................... 12
3.1 Bibliotecas........................................................................................................ 12
3.2 Declarações...................................................................................................... 12
3.3 Métodos de inserção de dados........................................................................ 13
3.4 Restrições......................................................................................................... 14
3.5 Listar exames ................................................................................................... 15
3.6 Gravar exames ................................................................................................. 15
3.7 Carregar exames .............................................................................................. 16
3.8 Pesquisa de exames por paciente.................................................................... 16
3.9 Pesquisa de exames por data .......................................................................... 17
3.10 Alterar exames................................................................................................. 18
3.11 Validar código .................................................................................................. 19
3.12 Validar operação.............................................................................................. 20
3.13 Função main..................................................................................................... 20
4 Testes e Resultados ................................................................................................ 21
4.1 Página inicial .................................................................................................... 21
4.2 Menu................................................................................................................ 21
4.3 Listar todos os exames..................................................................................... 22
4.4 Pesquisa por paciente...................................................................................... 22
4.5 Pesquisa por data............................................................................................. 23
4.6 Inserir novo exame .......................................................................................... 23
5. v
5 Código fonte ........................................................................................................... 24
Capítulo II........................................................................................................................ 39
1 Contexto ................................................................................................................. 40
2 Descrição Técnica ................................................................................................... 41
2.1 Variáveis........................................................................................................... 41
2.2 Funções e procedimentos................................................................................ 43
2.2.1 Inserir registo no início da lista ligada (Med) ........................................... 43
2.2.2 Inserir registo no início da lista ligada (Principios)................................... 43
2.2.3 Inserir novo medicamento ....................................................................... 43
2.2.4 Inserir novo princípio ativo....................................................................... 44
2.2.5 Listar medicamentos ................................................................................ 44
2.2.6 Listar princípios ativos.............................................................................. 44
2.2.7 Gravar estrutura de medicamentos ......................................................... 44
2.2.8 Gravar estrutura de princípios ativos....................................................... 44
2.2.9 Carregar dados de medicamentos ........................................................... 44
2.2.10 Carregar dados de princípios ativos ......................................................... 44
2.2.11 Menu de princípios ativos ........................................................................ 44
2.2.12 Pesquisa de princípios ativos.................................................................... 45
2.2.13 Remover medicamento............................................................................ 45
2.2.14 Remover princípio ativo ........................................................................... 45
2.2.15 Pesquisar medicamentos por laboratório................................................ 45
2.2.16 Pesquisa por múltiplos critérios ............................................................... 45
2.2.17 Alterar dados de medicamento................................................................ 46
2.2.18 Alterar dados de princípios ativos............................................................ 46
2.2.19 Validar código........................................................................................... 46
6. vi
2.2.20 Validar número......................................................................................... 46
2.2.21 Validar laboratório.................................................................................... 46
2.2.22 Validar operações com estrutura medicamentos.................................... 46
2.2.23 Validar operações com estrutura princípios ativos.................................. 46
2.2.24 Selecionar critérios................................................................................... 47
2.2.25 Menu alterar medicamentos.................................................................... 47
2.2.26 Menu alterar princípios ativos.................................................................. 47
2.2.27 Inexistência de medicamentos................................................................. 47
2.2.28 Inexistência de princípios ativos............................................................... 47
2.2.29 Continuar.................................................................................................. 47
2.2.30 Navegação nos menus.............................................................................. 47
3 Desenvolvimento.................................................................................................... 48
3.1 Declarações...................................................................................................... 48
3.2 Inserir novo medicamento............................................................................... 48
3.3 Selecionar princípios ativos ............................................................................. 49
3.4 Pesquisa de princípios ativos........................................................................... 50
3.5 Remover medicamentos.................................................................................. 51
3.6 Pesquisa por múltiplos critérios ...................................................................... 52
3.7 Validar laboratório........................................................................................... 54
4 Testes e Resultados ................................................................................................ 55
4.1 Menu de opções principal ............................................................................... 55
4.2 Inserir novo medicamento............................................................................... 55
4.3 Alterar dados de medicamentos ..................................................................... 56
4.4 Pesquisa por laboratório.................................................................................. 56
4.5 Pesquisa por múltiplos critérios ...................................................................... 57
7. vii
4.6 Listar princípios ativos ..................................................................................... 57
5 Código fonte ........................................................................................................... 58
Capítulo III....................................................................................................................... 88
1 Contexto ................................................................................................................. 89
2 Descrição Técnica ................................................................................................... 90
2.1 Variáveis........................................................................................................... 90
2.2 Funções e procedimentos................................................................................ 92
2.2.1 Inserir atleta ............................................................................................. 92
2.2.2 Inserir tempos........................................................................................... 92
2.2.3 Introduzir dados do atleta........................................................................ 92
2.2.4 Gravar dados da estrutura (Atletas)......................................................... 92
2.2.5 Carregar dados para a estrutura (Atletas)................................................ 93
2.2.6 Carregar dados para a estrutura (ListaTempos)....................................... 93
2.2.7 Listar atletas inscritos............................................................................... 93
2.2.8 Consultar atletas por número .................................................................. 93
2.2.9 Consultar atletas por percurso................................................................. 93
2.2.10 Listar atletas por tempo de prova............................................................ 94
2.2.11 Listar atletas que terminaram a prova..................................................... 94
2.2.12 Validar número......................................................................................... 94
2.2.13 Validar operação....................................................................................... 94
2.2.14 Navegação nos menus e apresentação.................................................... 94
3 Desenvolvimento.................................................................................................... 95
3.1 Declarações...................................................................................................... 95
3.2 Introduzir atleta............................................................................................... 95
3.3 Carregar dados................................................................................................. 96
8. viii
3.4 Listar atletas..................................................................................................... 97
3.5 Validar número ................................................................................................ 97
3.6 Validar percurso............................................................................................... 97
3.7 Outros .............................................................................................................. 98
4 Testes e Resultados ................................................................................................ 99
4.1 Menu de opções .............................................................................................. 99
4.2 Inserir novo atleta............................................................................................ 99
4.3 Listar atletas inscritos .................................................................................... 100
4.4 Consultar atleta por número ......................................................................... 100
4.5 Consultar atleta por percurso........................................................................ 101
4.6 Listar atletas que terminaram a prova .......................................................... 101
5 Código fonte ......................................................................................................... 102
Conclusões.................................................................................................................... 117
Referências Bibliográficas............................................................................................. 118
9. 1
Introdução
As linguagens de programação são uma forma de enviar instruções para um
computador ou processador, usando métodos padronizados.
Quando um programa de computador é criado, são usadas todo um conjunto de
regras sintáticas e semânticas que são caraterísticas da linguagem de programação
previamente selecionada para essa aplicação. Essas regras permitem ao programador
definir quais os dados onde o computador vai atuar, como serão armazenados esses
mesmos dados e todas as ações que o sistema deve tomar conforme as circunstâncias
o exigirem.
A um conjunto de regras sintáticas e semânticas designa-se de algoritmo. O
conjunto das palavras compostos de acordo com as regras referidas anteriormente,
designa-se de código fonte. Para um Software poder ser executado, esse código fonte
necessita ser traduzido para código máquina (binário) e posteriormente executado
pelo processador.
A principal vantagem do uso de linguagens de programação é o aumento de
produtividade para o programador, uma vez que lhe permite utilizar linguagens de alto
nível. Os processadores apenas trabalham com código máquina e se as aplicações
tivessem de ser desenvolvidas a este nível, os processos seriam demasiado morosos e
em alguns casos praticamente impossíveis de realizar. Ao utilizarem-se linguagens de
muito baixo nível também se torna extremamente complicado detetar os erros nos
algoritmos de implementação, devido à extrema complexidade dos códigos
associados. Utilizando a linguagem de alto nível, para além de se acelerar o processo
de criação de algoritmos porque a forma assemelha-se à linguagem humana,
facilmente se deteta os erros de implementação nos algoritmos com ajuda do
compilador que transforma o código fonte em código máquina antes do
processamento.
10. Introdução
2
1 Linguagem C
Uma das linguagens de programação mais utilizadas no mundo é a linguagem C.
Existem, outras ainda utilizadas atualmente tais como o Pascal, Fortran e Cobol. No
entanto será focada a linguagem C que é a linguagem de desenvolvimento deste
trabalho prático.
A linguagem C foi desenvolvida inicialmente entre 1969 e 1973 no AT&T Bell Lab
por Dennis Ritchie e Ken Thompson para a implementação do sistema UNIX (que era
originalmente escrito em Assembly).
A partir de 1973 com a adição do tipo de dados struct à linguagem C, esta
tornou-se suficientemente poderosa para a maioria dos núcleos do UNIX poderem ser
escritos em C. Assim, este foi um dos primeiros três sistemas a não serem
implementados em linguagem Assembly, abrindo as portas à linguagem C como
linguagem de programação preferencial.
Ao longo dos anos, a linguagem C foi sofrendo várias adaptações e melhorias,
sendo que atualmente se pode definir como uma linguagem de programação de alto
nível, compilada, estruturada, imperativa, procedural e padronizada. As suas
características mais importantes são:
Linguagem extremamente simples.
Bibliotecas de rotinas padronizadas, tais como funções matemáticas ou
manipulação de strings.
Paradigma de programação procedural.
Linguagem de pré-processamento que permite compilar vários ficheiros
de código fonte.
Uso de apontadores flexibiliza a linguagem.
Permite a inclusão de código Assembly no programa escrito em C.
O uso de estruturas (structs) permite que dados relacionados possam ser
manipulados como um todo.
Os tipos de dados da linguagem C podem ser do tipo inteiro (com ou sem sinal),
reais, carateres, estruturas e ainda muito importante do tipo apontadores.
11. Introdução
3
Os apontadores são um tipo de variável que guarda o endereço de memória de
outra variável. Na linguagem C, os apontadores possuem um valor reservado
designado NULL que significa que estes não estão a guardar no momento nenhum
endereço.
Os dados do tipo inteiro (int) servem para guardar números inteiros de valor
finito. Podem ser com ou sem sinal e o máximo e mínimo valor que se pode inserir é
limitado pela capacidade do processador de execução.
Os dados do tipo carater (char) ocupam 1 byte em memória independentemente
do sistema operativo e podem guardar carateres ou números inteiros. Através de
vetores de carateres é possível criar strings que são cadeias de carateres.
Os dados do tipo real (float) servem para guardar números de vírgula flutuante.
Estes dados podem ser ainda do tipo double float que apresentam o dobro da
capacidade de armazenamento.
2 Estruturas de dados
Na linguagem C são largamente usadas estruturas, que são grupos de variáveis
organizadas pelo programador e definidas através da criação de um novo tipo de
variável pela palavra reservada typedef.
Quando se desenvolve uma aplicação em qualquer linguagem de programação é
necessário desenvolver uma estrutura de dados que solucione o problema que se
pretende resolver.
Uma estrutura de dados é uma forma de armazenar e organizar os dados de
modo a que estes possam ser utilizados o mais eficazmente possível. O tipo de
estrutura selecionada pode depender da aplicação pretendida, sendo que as
estruturas mais comuns são as listas ligadas, as árvores binárias de procura, grafos, etc.
2.1 Listas ligadas simples
Uma lista ligada simples (LLS) é uma estrutura de dados dinâmicos, composta de
células que apontam para o próximo elemento da lista. Para criar uma lista ligada é
necessário saber o endereço do seu primeiro elemento, sendo que o ultimo elemento
12. Introdução
4
terá endereço NULL. A figura seguinte representa o esquema das ligações necessárias
para desenvolver uma lista ligada simples.
Figura 1 – Esquema de uma LLS.
Vantagens das LLS:
A inserção ou remoção de um elemento da lista ligada não implica mudar
outros elementos de lugar.
Quando se cria a lista, não é necessário definir o número máximo de
elementos que esta poderá ter, pois a memória pode ser alocada
dinamicamente.
Desvantagens das LLS:
Manipular a lista ligada pode ser perigoso, pois se a ligação entre os
elementos estiver mal feita, toda a lista ligada pode ser perdida.
Para aceder a um elemento nas posições finais da lista, é necessário
percorrer todas as posições iniciais.
2.2 Listas duplamente ligadas
Uma lista duplamente ligada (LDL) é uma derivação da lista ligada simples. O que
diferencia a lista duplamente ligada da lista ligada simples é que a lista duplamente
ligada apresenta dois apontadores, um para o endereço de memória do próximo
elemento da cadeia e outro apontador para o elemento anterior da cadeia. Como foi
visto anteriormente, na lista ligada simples apenas existe um apontador para o
endereço de memória do próximo elemento da cadeia.
A figura seguinte representa o esquema das ligações necessárias para
desenvolver uma lista duplamente ligada.
13. Introdução
5
Figura 2 – Esquema de uma LDL.
Vantagens das LDL:
Maior controlo dos dados presentes na lista ligada.
Existe menor risco de perda acidental dos dados.
Desvantagens das LDL:
Maior complexidade dos algoritmos.
Necessita de mais apontadores.
2.3 Árvores binárias de procura
Uma árvore binária de procura (ABP) é uma estrutura de dados que se baseada
em nós. Esta estrutura deve respeitar duas regras: os nós da subárvore esquerda
devem possuir uma chave com valor inferior ao nó raiz e todos os nós da subárvore
direita devem possuir uma chave com valor superior ao nó raiz. As árvores binárias de
procura possuem alguma terminologia associada que convém salientar:
Nós: representam os registos guardados na árvore.
Raiz: é o nó que fica no topo da árvore.
Filhos: são os nós que precedem os nós anteriores.
Pais: são os nós que antecedem os outros nós.
Folhas: são os nós terminais que não possuem ligação com outro nó.
Figura 3 – Esquema de uma ABP.
15. Cap. I
7
1 Contexto
Uma clínica médica, ao dispor de múltiplos serviços, departamentos e
especialidades, tem a necessidade de gerir todos os exames de diagnóstico que são
feitos para cada doente.
É necessário arquivar os exames realizados, numa determinada data, por um
determinado técnico. Entre outros serviços, é importante:
Consultar os exames realizados num determinado período de tempo;
Consultar os exames de um determinado doente;
Arquivar os exames realizados.
Pretende-se o desenvolvimento de uma aplicação em linguagem C, que efetue a
gestão desse arquivo de exames, através da utilização de listas ligadas simples.
A solução desenvolvida deverá possuir uma componente de interação com o
utilizador, bem como a capacidade de armazenar e carregar toda a informação através
de ficheiros.
16. Cap. I
8
2 Descrição Técnica
Para desenvolver o problema proposto, foi utilizada uma estrutura de dados
baseada numa lista ligada simples com nove variáveis para definir o registo Exames e
uma variável do tipo apontador para definir o endereço do registo seguinte na lista
ligada:
typedef struct Arquivo
{
int codigo, dia, mes, ano, identificacao;
char departamento[50], paciente[50], tecnico[50], resultados[100];
struct Arquivo *seguinte;
}
Exames;
2.1 Variáveis
A variável codigo deve receber um inteiro e guarda a identificação numérica do
exame inserido. A inserção deste código será limitada entre 1 e 9999 por opção. O
programa foi escrito de modo a inserir os registos no início da lista ligada. Assim o
utilizador deve tentar manter este código o mais ordenado possível, não fazendo
saltos entre valores de inserção. Através de restrições ao nível do código desenvolvido,
não será possível a introdução de códigos repetidos.
A variável departamento deve receber uma string de carateres (50 no máximo) e
vai descrever o exame ao dizer a que departamento pertence. Não foi considerado
relevante adicionar mais uma variável com o nome do exame realizado.
A variável paciente deve receber uma string de carateres (50 no máximo) e vai
guardar o nome do paciente que realizou o exame.
A variável identificacao deve receber um inteiro, e guarda o número do paciente
que realizou o exame. Este número pode ser um qualquer, sendo que foi pensado
inicialmente um conjunto de 9 algarismos referentes ao número do cartão de cidadão.
Será através deste número que serão efetuadas as pesquisas por paciente, como tal,
este número deverá ser o mais individualizado possível e o operador não deverá
utilizar o mesmo número em mais de um paciente.
17. Cap. I – Descrição Técnica
9
A variável tecnico deve receber uma string de carateres (50 no máximo) e vai
guardar o nome do funcionário que realizou o exame.
As variáveis dia, mes e ano devem receber inteiros e em conjunto vão guardar a
data de realização do exame. Estas variáveis terão restrições de inserção, para o
utilizador por engano não inserir dias ou meses incoerentes. Por opção, a inserção do
ano também foi limitada. Só serão considerados exames realizados a partir do ano
2000 e só será possível inserir até ao ano 2099, altura que o programa estará
obviamente obsoleto.
A variável resultados deve receber uma string de carateres (100 no máximo) e
vai guardar os resultados do paciente no exame realizado.
Finalmente a variável seguinte do tipo apontador para a estrutura Exames, será
o apontador para o endereço de memória do registo seguinte na lista ligada.
2.2 Funções e procedimentos
Para a implementação do programa, foi necessário desenvolver uma série de
funções e procedimentos. De seguida apresenta-se um pequeno resumo das rotinas
desenvolvidas pela ordem em que aparecem no código fonte.
2.2.1 Inserir registo no início da lista ligada
Convencionou-se que o último exame inserido devia ficar no topo da lista. Assim
optou-se pelo método de inserir no início da lista ligada. No entanto, quando se
carrega o ficheiro de texto, usando este método, os números dos registos acabariam
por ficar baralhados, como se vai explicar mais à frente. Assim é também utilizado o
inserir no fim da lista ligada mas somente após o carregar do ficheiro de texto. Este
método é usado quando os dados são inseridos manualmente pelo utilizador.
2.2.2 Inserir registo no fim da lista ligada
Como referido anteriormente, este método é utilizado após serem carregados os
dados do ficheiro de texto, de modo a ser mantida a ordem numérica (código) dos
registos. Caso fosse utilizado a opção inserir no final da lista ligada, não havia esta
necessidade como se vai explicar mais à frente. Assim esta função é complementar e é
chamada numa altura diferente da anterior.
18. Cap. I – Descrição Técnica
10
2.2.3 Introduzir dados num registo pelo utilizador
Este é um dos procedimentos principais do programa e permite que o utilizador
introduza novos registos na lista ligada. Além de pedir os dados a inserir ao utilizador,
possui limitações de introdução de dados e invoca outras funções e procedimentos
para permitir inserir na lista ligada e gravar.
2.2.4 Listar registos da lista ligada
Este procedimento a qualquer altura, percorre a lista ligada, uma vez que esta
está permanentemente atualizada, quer seja com dados inseridos pelo utilizador quer
com dados carregados do ficheiro de texto e imprime no monitor o conteúdo dos seus
diferentes registos.
2.2.5 Gravar lista ligada em ficheiro de texto
Este procedimento grava a lista ligada atualizada em formato de ficheiro de texto
sempre que é introduzido mais um registo pelo utilizador.
2.2.6 Carregar lista ligada do ficheiro de texto
Esta função é responsável por carregar os dados presentes no ficheiro de texto
para a lista ligada ao iniciar o programa. Os dados são inseridos na lista ligada através
da função inserir no fim da lista, como referido anteriormente.
2.2.7 Pesquisar exame por paciente
Este procedimento percorre a lista ligada, comparando o número de pesquisa ao
código dos registos até encontrar uma correspondência. Se esta existir, imprime na
consola os dados do registo. Se não encontrar qualquer correspondência devolve
mensagem informando que registo não existe no sistema. Este procedimento tal como
todos os que dependem de a lista ligada não estar vazia, será complementado com
outros procedimentos de controlo que impedirão que o procedimento seja executado
caso a lista ligada esteja vazia.
2.2.8 Pesquisar exames por período de tempo
Este procedimento é mais complexo e será demonstrado numa fase posterior.
Permite devolver os registos num período de tempo estabelecido pelo utilizador e tal
19. Cap. I – Descrição Técnica
11
como o procedimento anterior, caso a lista ligada se encontre vazia não vai ser
executado.
2.2.9 Alterar dados de um exame
Este procedimento que só será executado caso a lista ligada não se encontre
vazia, permite ao utilizador alterar os dados dos registos inseridos na lista ligada
diretamente a partir do programa. É pedido ao utilizador que selecione o código do
exame a alterar e posteriormente o dado pretendido.
2.2.10 Validar um código de exame
Esta função permite verificar se determinado código de exame já existe no
sistema. Assim será usada com duas funções: nas pesquisas por código de exame
permite rejeitar imediatamente caso ele não exista e controla que se possam inserir
códigos repetidos quando se está a criar um novo exame.
2.2.11 Validar operações com a lista ligada
Esta função é extremamente simples, mas altamente vantajosa, uma vez que
verifica se a lista ligada está vazia e serve como sistema de controlo para não deixar
correr outros procedimentos sobre uma lista ligada vazia.
2.2.12 Apresentar primeira página
Este é um procedimento simples que é invocado na função main que
simplesmente apresenta informações ao utilizador, nomeadamente o nome do
programa, alunos que o desenvolveram, o docente e outras informações úteis.
2.2.13 Apresentar menu
Esta função apresenta duas funcionalidades. A primeira é dar uma imagem
gráfica do menu, bem como as suas opções. A segunda é fazer a escolha da opção do
utilizador e retornar esse valor.
2.2.14 Correr o menu
Permite navegar por entre as opções do programa. Recebe a opção selecionada a
partir da função referenciada anteriormente e possui 5 seleções básicas para além da
opção de poder abandonar a aplicação.
20. Cap. I
12
3 Desenvolvimento
Para desenvolver o programa e para que este funcione corretamente, foi
necessário que todos os procedimentos e funções criados interagissem de forma
eficaz, sem erros e evitando situações do tipo “beco sem saída” que ocorrem quando
as aplicações bloqueiam ou ficam presas em estados sem retorno. O resultado final é o
código fonte que se encontra anexado a este relatório, sendo que nesta seção serão
explicadas algumas técnicas implementadas.
3.1 Bibliotecas
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <locale.h>
Estas instruções permitem incluir bibliotecas ao programa. Além das
necessidades normais de input/output (stdio.h), alocação de memória (stdlib.h),
manipulação de strings (string.h) e operações com diferentes tipos de variáveis
(ctype.h), foi necessário recorrer à biblioteca locale.h para poder incluir carateres
especiais em língua portuguesa, que serão largamente utilizados ao longo do
programa.
3.2 Declarações
Foi declarada globalmente uma variável ListaLigada do tipo apontador para a
estrutura Exames com o endereço inicial nulo. Esta declaração global torna possível o
seu uso em qualquer procedimento ou função sem a necessidade de voltar a declarar.
E desta forma sempre que uma operação for executada sobre a lista ligada esta estará
automaticamente atualizada.
Exames *ListaLigada = NULL;
21. Cap. I – Desenvolvimento
13
3.3 Métodos de inserção de dados
Foi definido que os dados inseridos deviam ficar no início da lista ligada e que era
importante manter ordenados os códigos. Se os dados fossem carregados do ficheiro
de texto através duma função inserir no início da lista, ia-se perder essa ordenação.
Exames *inserirFim (Exames *apt, int cod, char *dep, char *pac, int id,
char *tec, int d, int m, int a, char *res)
{
Exames *aux = apt;
Exames *novo;
novo = (Exames*) malloc (sizeof (Exames));
novo -> codigo = cod;
strcpy (novo -> departamento, dep);
strcpy (novo -> paciente, pac);
novo -> identificacao = id;
strcpy (novo -> tecnico, tec);
novo -> dia = d;
novo -> mes = m;
novo -> ano = a;
strcpy (novo -> resultados, res);
novo -> seguinte = NULL;
if (apt == NULL)
return (novo);
else
{
while (aux -> seguinte != NULL)
aux = aux -> seguinte;
aux -> seguinte = novo;
return (apt);
}
}
Assim foi criada uma função auxiliar de inserir no fim da lista ligada que só é
utilizada quando os dados são carregados do ficheiro de texto. De referir que este
problema de ordenação não ocorreria caso fosse utilizado sempre o inserir no fim da
lista ligada. No entanto não foi essa a opção adotada. O que se pretende evitar nesta
situação é:
Se o utilizador inserir 3 registos consecutivos: 1000, 1001, 1002…
Na lista ligada a ordem ficaria: 1002, 1001, 1000…
Ao gravar em ficheiro de texto: 1002, 1001, 1000…
Ao carregar usando inserir no início: 1000, 1001, 1002…
Se fosse inserido mais um registo: 1004, 1000, 1001, 1002…
22. Cap. I – Desenvolvimento
14
3.4 Restrições
Dentro do procedimento inserir exame vão existir algumas restrições como se
pode observar.
while (manter == 1)
{
printf ("CÓDIGO DO EXAME: ");
scanf ("%d", &cod);
fflush (stdin);
teste = validarCodigo (ListaLigada, cod);
if ((cod < 1) || (cod > 9999))
{
manter = 1;
printf ("nATENÇÃO! Introduza um código entre 1 e 9999nn");
}
else if (teste == 1)
{
manter = 1;
printf ("nATENÇÃO! Código já foi introduzidonn");
}
else
manter = 0;
}
Ao inserir o código do exame, o programa entra num ciclo e só sai se forem
verificadas as restrições: o código introduzido não ser repetido, não ser inferior a 1,
nem superior a 9999. Para verificar se o código já existe é usado o procedimento
validarCodigo que aparecerá posteriormente no corpo do programa.
while (manter == 1)
{
printf ("DIA: ");
scanf ("%d", &d);
fflush (stdin);
if ((d < 1) || (d > 31))
{
manter = 1;
printf ("nATENÇÃO! Introduza um dia entre 1 e 31nn");
}
else
manter = 0;
}
Este é outro tipo de restrição encontrada dentro do procedimento inserir exame,
que neste caso limita a introdução da variável dia. Mais uma vez o programa entra
num ciclo e só sairá se uma opção válida for introduzida que neste caso é um inteiro
entre 1 e 31. De notar que a variável que permite manter os ciclos é sempre a mesma,
uma vez que saindo destes, ela é reinicializada com o valor 1.
23. Cap. I – Desenvolvimento
15
3.5 Listar exames
O procedimento listar exames é extremamente simples. Uma vez que ele já
recebe a lista ligada pela definição da sua assinatura, existe uma condição que testa se
esta é ou não nula. Se for nula, invoca o procedimento naoExiste que apresenta uma
mensagem informando que não existem exames gravados. Se a lista ligada não estiver
vazia serão impressos no monitor, o conteúdo de todos os exames.
if (apt == NULL)
{
naoExiste ();
menu ();
}
else
{
printf ("n");
printf ("*****************************************************n");
printf ("* *n");
printf ("* EXAMES ARQUIVADOS *n");
printf ("* *n");
printf ("*****************************************************n");
3.6 Gravar exames
O procedimento gravar exames, permite a gravação em formato de ficheiro de
texto, de todos os dados presentes na lista ligada, impedindo estes de se perderem
quando o programa é terminado.
void gravar (Exames *apt)
{
FILE *ficheiro;
ficheiro = fopen ("Arquivo.txt", "w+");
if (ficheiro != NULL)
{
while (apt != NULL)
{
fprintf (ficheiro, "n%dn", apt -> codigo);
fprintf (ficheiro, "%sn", apt -> departamento);
fprintf (ficheiro, "%sn", apt -> paciente);
fprintf (ficheiro, "%dn", apt -> identificacao);
fprintf (ficheiro, "%sn", apt -> tecnico);
fprintf (ficheiro, "%dn", apt -> dia);
fprintf (ficheiro, "%dn", apt -> mes);
fprintf (ficheiro, "%dn", apt -> ano);
fprintf (ficheiro, "%s", apt -> resultados);
apt = apt -> seguinte;
}
}
fclose (ficheiro);
}
24. Cap. I – Desenvolvimento
16
3.7 Carregar exames
O procedimento carregar exames, vai permitir inserir os dados presentes no
ficheiro de texto na lista ligada, ao iniciar o programa. Se ao for executado o
procedimento ele não encontrar nenhum ficheiro de texto compatível, imprimirá uma
mensagem por cima do menu de opções durante a primeira utilização informando que
não existem exames guardados.
void carregar ()
{
FILE *ficheiro;
int cod, id, d, m, a;
char dep[50], pac[50], tec[50], res[100];
ficheiro = fopen ("Arquivo.txt", "r");
if (ficheiro != NULL)
{
while (!feof (ficheiro))
{
fscanf (ficheiro, "n%dn", &cod);
fscanf (ficheiro, " %[^n]", dep);
fscanf (ficheiro, " %[^n]", pac);
fscanf (ficheiro, "%dn", &id);
fscanf (ficheiro, " %[^n]", tec);
fscanf (ficheiro, "%dn", &d);
fscanf (ficheiro, "%dn", &m);
fscanf (ficheiro, "%dn", &a);
fscanf (ficheiro, " %[^n]", res);
ListaLigada=inserirFim(ListaLigada,cod,dep,pac,id,tec,d,m,a,res);
}
fclose (ficheiro);
}
else
printf ("nNão foram carregados exames!n");
}
3.8 Pesquisa de exames por paciente
O procedimento pesquisar exame por paciente é um dos vários que é controlado
através da função validarOperacao. Neste caso não era fundamental recorrer a este
método, uma vez que ele recebe a lista ligada na sua assinatura. No entanto, este
passou a ser o método de testar se o procedimento devia ser ou não executado, sendo
que noutros procedimentos ele é mesmo necessário, uma vez que não recebem a lista
ligada na assinatura. Assim, se a função retornar -1 significa que a lista ligada se
encontra vazia e o procedimento imprime uma mensagem informando que não
existem exames. Se a função retornar 1 executa o procedimento.
25. Cap. I – Desenvolvimento
17
void pesquisaPaciente (Exames *apt)
{
int chave, teste2 = 1;
char escolha2;
system ("cls");
teste2 = validarOperacao (ListaLigada);
if (teste2 == -1)
{
naoExiste ();
menu ();
}
else
{
Continuando no procedimento pesquisar exame por paciente, é solicitado uma
chave de pesquisa ao utilizador. Seguidamente o programa vai correr a lista ligada
enquanto essa chave for diferente das identificações dos registos. Quando encontrar
uma correspondência, se o registo não estiver vazio imprime o seu conteúdo.
printf ("Identificação: ");
scanf ("%d", &chave);
fflush (stdin);
while ((apt != NULL) && (apt -> identificacao != chave))
apt = apt -> seguinte;
if (apt != NULL)
{
printf ("nn");
printf ("CÓDIGO DO EXAME: %dn", apt -> codigo);
printf ("DEPARTAMENTO: %sn", apt -> departamento);
printf ("NOME DO PACIENTE: %sn", apt -> paciente);
printf ("IDENTIFICAÇÃO DO PACIENTE: %dn", apt -> identificacao);
printf ("TÉCNICO RESPONSÁVEL: %sn", apt -> tecnico);
printf ("DATA: %d-%d-%dn", apt -> dia, apt -> mes, apt -> ano);
printf ("RESULTADOS: %snn", apt -> resultados);
}
else
printf ("nnEXAME INEXISTENTEnn");
3.9 Pesquisa de exames por data
Na pesquisa por data, pretende-se listar os exames realizados entre determinado
período de tempo.
void pesquisaData (Exames *apt)
{
int chaveDiaInf, chaveMesInf, chaveAnoInf, inf;
int chaveDiaSup, chaveMesSup, chaveAnoSup, sup;
int manter2 = 1, teste3 = 0, cnt = 0;
char escolha3;
26. Cap. I – Desenvolvimento
18
A solução encontrada foi limitar esse período através de duas chaves: uma
inferior e outra superior. Assim tem de ser definidos os dias, meses e anos tanto da
chave inferior como da superior.
inf = (chaveAnoInf*365) + (chaveMesInf*31) + chaveDiaInf;
sup = (chaveAnoSup*365) + (chaveMesSup*31) + chaveDiaSup;
Após o utilizador inserir os dados para completar as chaves, que são compostas
por limitações de inserção para garantir que não se inserem datas irreais, o programa
vai transformar as datas numa chave. Para a obtenção dessa chave a melhor solução
encontrada e que provou estar imune a falhas foi transformar toda a data em número
de dias. Para isso multiplica-se o ano por 365, o mês por 31, e a estas duas variáveis
soma-se o valor do dia. Assim garante-se que nenhuma chave possa ser repetida.
while (apt != NULL)
{
if (((((apt->ano)*365) + ((apt->mes)*31) + (apt->dia)) >= inf) &&
((((apt->ano)*365) + ((apt->mes)*31) + (apt->dia)) <= sup))
{
printf ("nn");
printf ("CÓDIGO DO EXAME: %dn", apt -> codigo);
printf ("DEPARTAMENTO: %sn", apt -> departamento);
printf ("NOME DO PACIENTE: %sn", apt -> paciente);
printf ("IDENTIFICAÇÃO DO PACIENTE: %dn", apt -> identificacao);
printf ("TÉCNICO RESPONSÁVEL: %sn", apt -> tecnico);
printf ("DATA: %d-%d-%dn", apt -> dia, apt -> mes, apt -> ano);
printf ("RESULTADOS: %snn", apt -> resultados);
cnt = cnt++;
}
apt = apt -> seguinte;
}
if (cnt == 0)
printf ("nnNÃO EXISTEM EXAMES NESTE PERÍODO DE TEMPOnn");
De seguida, enquanto a lista ligada não chegar ao fim, vai ser comparada a chave
de cada registo para ver se fica enquadrada entre as duas chaves criadas. Por cada
equivalência encontrada é impresso no monitor o conteúdo do registo.
3.10 Alterar exames
O procedimento alterar vai permitir substituir dados nos registos da lista ligada,
se esta não for vazia.
Este procedimento contém várias das restrições anteriores, não apresentando
nada de novo, sendo que a mais importante é que se o código do exame a alterar não
27. Cap. I – Desenvolvimento
19
for validado o utilizador tem duas opções: recomeçar, procurando um código válido ou
abandonar o procedimento.
void alterar (Exames *apt)
{
int _cod, _id, _d, _m, _a;
char _dep[50], _pac[50], _tec[50], _res[100];
int manter3 = 1, modificar = 0, teste4 = 0, teste5 = 0;
char escolha4;
teste4 = validarOperacao (ListaLigada);
if (teste4 == -1)
{
naoExiste ();
menu ();
}
else
{
printf ("*****************************************************n");
printf ("* *n");
printf ("* ALTERAR DADOS DE EXAME *n");
printf ("* *n");
printf ("*****************************************************n");
while (manter3 == 1)
{
printf ("n");
printf ("Código: ");
scanf ("%d", &_cod);
fflush (stdin);
teste5 = validarCodigo (ListaLigada, _cod);
if (teste5 == 0)
{
printf ("nnCódigo não se encontra registadonn");
printf ("Deseja continuar? [S/N]? ");
scanf ("%s", &escolha4);
3.11 Validar código
Como já antes referido esta função que é várias vezes invocada, permite verificar
se um determinado código já se encontra utilizado na lista ligada. Percorre a lista
ligada e se encontrar uma equivalência retorna 1. Caso contrário, retorna 0.
int validarCodigo (Exames *apt, int test)
{
while (apt != NULL)
{
if (apt -> codigo == test)
return (1);
apt = apt -> seguinte;
}
return (0);
}
28. Cap. I – Desenvolvimento
20
3.12 Validar operação
Este procedimento que é largamente utilizado, testa se a lista ligada é ou não
vazia. Se for nula, devolve -1. Caso contrário devolve 1.
int validarOperacao (Exames *apt)
{
if (apt == NULL)
return (-1);
else
return (1);
}
3.13 Função main
Na função main foi necessário incluir a instrução setlocale configurada para
“Portuguese” de modo a permitir a inclusão de carateres especiais em língua
portuguesa na escrita.
int main (void)
{
setlocale (LC_ALL,"Portuguese");
capa ();
carregar ();
menu ();
return (0);
}
29. Cap. I
21
4 Testes e Resultados
4.1 Página inicial
A página inicial apresenta variadas informações, tais como o nome de quem
desenvolveu o trabalho, o nome da aplicação, docente e unidade curricular para a qual
esta foi desenvolvida.
Figura 4 – Página inicial.
4.2 Menu
O menu de opções é um dos aspetos mais importantes do programa, uma vez
que permite navegar entre as diversas funcionalidades e ao mesmo tempo apresentar
um ambiente gráfico agradável ao utilizador.
Figura 5 – Menu de opções.
30. Cap. I – Testes e Resultados
22
4.3 Listar todos os exames
A opção listar todos os exames, imprime no monitor os dados de todos os
exames arquivados, sendo que o último exame inserido aparecerá sempre no topo da
lista. É a forma mais rápida de ter acesso aos exames guardados no programa.
Figura 6 – Listar todos os exames.
4.4 Pesquisa por paciente
A pesquisa por paciente é utilizada quando se pretende procurar um exame
associado a um paciente para o qual previamente se conhece o número de
identificação. No final de cada pesquisa é solicitado ao utilizador se pretende efetuar
uma nova pesquisa.
Figura 7 – Pesquisa por paciente.
31. Cap. I – Testes e Resultados
23
4.5 Pesquisa por data
A pesquisa por período de tempo permite encontrar exames realizados num
determinado período de tempo definido pelo utilizador. Caso não seja encontrado o
pretendido, no final é solicitado ao utilizador se deseja continuar.
Figura 8 – Pesquisa por período de tempo.
4.6 Inserir novo exame
Os dados de um novo exame são inseridos consecutivamente, com a opção de
introduzir os dados de vários pacientes.
Figura 9 – Inserir novo exame.
32. Cap. I
24
5 Código fonte
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <locale.h>
typedef struct Arquivo
{
int codigo, dia, mes, ano, identificacao;
char departamento[50], paciente[50], tecnico[50], resultados[100];
struct Arquivo *seguinte;
}
Exames;
Exames *ListaLigada = NULL;
void inserirExame ();
void listar (Exames *apt);
void gravar (Exames *apt);
void carregar ();
void pesquisaPaciente (Exames *apt);
void pesquisaData (Exames *apt);
void alterar (Exames *apt);
int validarCodigo (Exames *apt, int test);
int validarOperacao (Exames *apt);
void menuAlterar ();
void naoExiste ();
void continuar ();
void capa ();
int fmenu ();
void menu ();
Exames *inserirInicio (Exames *apt, int cod, char *dep, char *pac, int id,
char *tec, int d, int m, int a, char *res)
{
Exames *novo;
novo = (Exames*) malloc (sizeof (Exames));
novo -> codigo = cod;
strcpy (novo -> departamento, dep);
strcpy (novo -> paciente, pac);
novo -> identificacao = id;
strcpy (novo -> tecnico, tec);
novo -> dia = d;
novo -> mes = m;
novo -> ano = a;
strcpy (novo -> resultados, res);
novo -> seguinte = apt;
return (novo);
}
33. Cap. I – Código fonte
25
Exames *inserirFim (Exames *apt, int cod, char *dep, char *pac, int id,
char *tec, int d, int m, int a, char *res)
{
Exames *aux = apt;
Exames *novo;
novo = (Exames*) malloc (sizeof (Exames));
novo -> codigo = cod;
strcpy (novo -> departamento, dep);
strcpy (novo -> paciente, pac);
novo -> identificacao = id;
strcpy (novo -> tecnico, tec);
novo -> dia = d;
novo -> mes = m;
novo -> ano = a;
strcpy (novo -> resultados, res);
novo -> seguinte = NULL;
if (apt == NULL)
return (novo);
else
{
while (aux -> seguinte != NULL)
aux = aux -> seguinte;
aux -> seguinte = novo;
return (apt);
}
}
void inserirExame ()
{
int cod, id, d, m, a;
char dep[50], pac[50], tec[50], res[100];
int manter = 1, teste = 0;
char escolha;
system ("cls");
printf ("n");
printf ("*******************************************************n");
printf ("* *n");
printf ("* INSERIR DADOS DO EXAME *n");
printf ("* *n");
printf ("*******************************************************n");
printf ("n");
while (manter == 1)
{
printf ("CÓDIGO DO EXAME: ");
scanf ("%d", &cod);
fflush (stdin);
teste = validarCodigo (ListaLigada, cod);
if ((cod < 1) || (cod > 9999))
{
manter = 1;
printf ("nATENÇÃO! Introduza um código entre 1 e 9999nn");
}
else if (teste == 1)
34. Cap. I – Código fonte
26
{
manter = 1;
printf ("nATENÇÃO! Código já foi introduzidonn");
}
else
manter = 0;
}
manter = 1;
printf ("nDEPARTAMENTO: ");
gets (dep);
fflush (stdin);
printf ("nNOME DO PACIENTE: ");
gets (pac);
fflush (stdin);
printf ("nIDENTIFICAÇÃO DO PACIENTE: ");
scanf ("%d", &id);
fflush (stdin);
printf ("nTÉCNICO RESPONSÁVEL: ");
gets (tec);
fflush (stdin);
printf ("nDATA DO EXAMEn");
while (manter == 1)
{
printf ("DIA: ");
scanf ("%d", &d);
fflush (stdin);
if ((d < 1) || (d > 31))
{
manter = 1;
printf ("nATENÇÃO! Introduza um dia entre 1 e 31nn");
}
else
manter = 0;
}
manter = 1;
while (manter == 1)
{
printf ("MÊS: ");
scanf ("%d", &m);
fflush (stdin);
if ((m < 1) || (m > 12))
{
manter = 1;
printf ("nATENÇÃO! Introduza um mês entre 1 e 12nn");
}
else
manter = 0;
}
manter = 1;
while (manter == 1)
{
printf ("ANO: ");
scanf ("%d", &a);
35. Cap. I – Código fonte
27
fflush (stdin);
if ((a < 2000) || (a > 2099))
{
manter = 1;
printf ("nATENÇÃO! Introduza um ano entre 2000 e 2099nn");
}
else
manter = 0;
}
manter = 1;
printf ("nRESULTADOS: ");
gets (res);
fflush (stdin);
ListaLigada = inserirInicio (ListaLigada,cod,dep,pac,id,tec,d,m,a,res);
gravar (ListaLigada);
printf ("nnDeseja continuar? [S/N] ");
scanf ("%s", &escolha);
fflush (stdin);
if ((escolha == 'S') || (escolha == 's'))
{
system ("cls");
inserirExame ();
}
else
{
system ("cls");
menu ();
}
}
void listar (Exames *apt)
{
system ("cls");
if (apt == NULL)
{
naoExiste ();
menu ();
}
else
{
printf ("n");
printf ("*****************************************************n");
printf ("* *n");
printf ("* EXAMES ARQUIVADOS *n");
printf ("* *n");
printf ("*****************************************************n");
printf ("n");
while (apt != NULL)
{
printf ("n");
printf ("CÓDIGO DO EXAME: %dn", apt -> codigo);
printf ("DEPARTAMENTO: %sn", apt -> departamento);
48. Cap. II
40
1 Contexto
Uma empresa farmacêutica pretende desenvolver um simpósio de
medicamentos genéricos para fornecer a médicos de várias especialidades. Este
simpósio deverá conter toda a informação que o médico considere útil e necessária,
tais como indicações, contraindicações, posologia, princípio ativo, composição, outros
medicamentos com o mesmo princípio ativo, etc.
Os medicamentos devem estar agrupados por laboratórios, sendo possível:
Registar novo medicamento.
Registar novo princípio ativo.
Consultar por múltiplos critérios.
Remoção de medicamento.
Pretende-se o desenvolvimento de uma aplicação em linguagem C, que efetue a
gestão desse simpósio, através da utilização de listas duplamente ligadas.
A solução desenvolvida deverá possuir uma componente de interação com o
utilizador, bem como a capacidade de armazenar e carregar toda a informação através
de ficheiros.
49. Cap. II
41
2 Descrição Técnica
Para desenvolver esta segunda aplicação proposta e de modo a dar uma solução
o mais eficaz possível, foram utilizadas duas estruturas de dados baseadas em listas
duplamente ligadas. A primeira estrutura foi denominada de Med e desenvolvida para
guardar os dados associados aos medicamentos genéricos.
typedef struct Simposium
{
int codigo;
char nome[50], principioAtivo[50], outrosMedicamentos[100], laboratorio[50];
char composicao[50], posologia[50], indicacoes[50], contraIndicacoes[50];
struct Simposium *anterior;
struct Simposium *seguinte;
}
Med;
A segunda estrutura foi denominada de Principios e foi desenvolvida com o
intuito de guardar os princípios ativos autonomamente, de modo que estes possam
sempre que requeridos ser chamados a ser inseridos na estrutura principal.
typedef struct Generico
{
int numero;
char designacao[50], descricao[100];
struct Generico *ant;
struct Generico *seg;
}
Principios;
2.1 Variáveis
A variável codigo deve receber um inteiro e guarda a identificação numérica do
medicamento inserido. A inserção deste código será limitada entre 1 e 9999 por opção
e não poderão ser introduzidos códigos repetidos. O programa foi escrito de modo a
inserir os registos no início da lista ligada, sendo que para este caso a ordenação não é
importante.
A variável nome deve receber uma string de carateres (50 no máximo) e vai
guardar o nome do medicamento inserido no sistema.
A variável principioAtivo deve receber uma string de carateres (50 no máximo) e
vai indicar o princípio ativo que está associado ao medicamento. Este princípio ativo
pode ser introduzido diretamente pelo utilizador, ou escolhido através de um menu
caso a segunda estrutura (Princípios Ativos) não se encontre vazia.
50. Cap. II – Descrição Técnica
42
A variável outrosMedicamentos deve receber uma string de carateres (100 no
máximo) e vai guardar os nomes de outros medicamentos que possam ter o mesmo
princípio ativo.
A variável laboratorio deve receber uma string de carateres (50 no máximo) e vai
guardar o nome do laboratório que produziu o medicamento. Esta variável é
importante para satisfazer o critério de agrupar os medicamentos por laboratório ao
futuramente permitir fazer pesquisas agrupadas por nome de laboratório.
A variável composicao deve receber uma string de carateres (50 no máximo) e
vai guardar a fórmula do medicamento caso seja conhecida.
A variável posologia deve receber uma string de carateres (50 no máximo) e vai
guardar a quantidade diária recomendada quando se utiliza o medicamento.
A variável indicacoes deve receber uma string de carateres (50 no máximo) e vai
guardar as aplicações conhecidas para o uso do medicamento caso sejam conhecidas.
A variável contraIndicacoes deve receber uma string de carateres (50 no
máximo) e vai guardar os efeitos indesejáveis conhecidos do medicamento caso estes
sejam conhecidos.
A variável anterior do tipo apontador para a estrutura Med, será o apontador
para o endereço de memória do registo anterior na lista duplamente ligada.
A variável seguinte do tipo apontador para a estrutura Med, será o apontador
para o endereço de memória do registo seguinte na lista duplamente ligada.
A variável numero deve receber um inteiro e guarda a identificação numérica do
princípio ativo inserido. A inserção deste código será limitada entre 1 e 999 por opção
e tal como na estrutura anterior não poderão ser introduzidos códigos repetidos.
A variável designacao deve receber uma string de carateres (50 no máximo) e vai
guardar o nome do princípio ativo inserido no sistema. Estes princípios podem ser já
ou não existentes na estrutura Med, pois as duas estruturas são independentes.
A variável descricao deve receber uma string de carateres (100 no máximo) e vai
guardar uma breve descrição sobre o princípio ativo. Esta descrição deve ser a mais
51. Cap. II – Descrição Técnica
43
breve e esclarecedora possível, uma vez que aparecerá no menu de seleção quando se
introduz um novo medicamento.
A variável ant do tipo apontador para a estrutura Principios, será o apontador
para o endereço de memória do registo anterior na lista duplamente ligada.
A variável seg do tipo apontador para a estrutura Principios, será o apontador
para o endereço de memória do registo seguinte na lista duplamente ligada.
2.2 Funções e procedimentos
Para a implementação do programa, foi necessário desenvolver uma série de
funções e procedimentos, tanto para a estrutura Med como para a estrutura
Principios. Apresenta-se um resumo das rotinas desenvolvidas pela ordem que
aparecem no código fonte.
2.2.1 Inserir registo no início da lista ligada (Med)
Este procedimento permite inserir um novo registo no início da lista duplamente
ligada como foi previamente definido, ao mesmo tempo que atualiza os apontadores
da cabeça da lista (início) e da cauda da lista (fim).
2.2.2 Inserir registo no início da lista ligada (Principios)
Este procedimento é similar ao anterior mas para a estrutura dos princípios
ativos. Grande parte das rotinas estará duplicada devido ao uso de duas estruturas
diferentes.
2.2.3 Inserir novo medicamento
Este procedimento permite ao utilizador introduzir dados num novo registo na
estrutura Med. São solicitados os dados ao utilizador, que irão apresentar restrições
de introdução relativamente ao código, e caso a lista ligada Principios não se encontre
vazia o utilizador pode selecionar o princípio ativo de entre um menu que será
apresentado. Após cada inserção, a lista é gravada em ficheiro de texto e é solicitado
ao utilizador se deseja introduzir consecutivamente mais registos.
52. Cap. II – Descrição Técnica
44
2.2.4 Inserir novo princípio ativo
Este procedimento permite ao utilizador introduzir dados num novo registo da
estrutura Principios. Os dados introduzidos terão restrições relativamente ao número
que terá de ser limitado entre 1 e 999 e não poderá ser repetido. Após cada inserção é
solicitado ao utilizador se pretende introduzir mais registos.
2.2.5 Listar medicamentos
Este procedimento imprime na consola todos os registos presentes na estrutura
Med caso a lista ligada não se encontre vazia.
2.2.6 Listar princípios ativos
Este procedimento imprime na consola todos os registos presentes na estrutura
Principios caso a lista ligada não se encontre vazia.
2.2.7 Gravar estrutura de medicamentos
Este procedimento grava em formato de ficheiro de texto o conteúdo da lista
ligada correspondente à estrutura Med.
2.2.8 Gravar estrutura de princípios ativos
Este procedimento grava em formato de ficheiro de texto o conteúdo da lista
ligada correspondente à estrutura Principios.
2.2.9 Carregar dados de medicamentos
Este procedimento caso tenha um ficheiro de texto válido disponível, carrega os
dados presentes para a lista ligada da estrutura Med através do procedimento inserir
registo no início da lista ligada.
2.2.10 Carregar dados de princípios ativos
Tal como o procedimento anterior, este procedimento carrega os dados
presentes para a lista ligada da estrutura Principios caso exista um ficheiro de texto
válido.
2.2.11 Menu de princípios ativos
Esta função apresenta inicialmente um menu, em que as opções são a listagem
da lista ligada Princípios. Assim, o utilizador deve escolher um dos princípios ativos do
53. Cap. II – Descrição Técnica
45
menu que será referenciado através do seu número. Após esta escolha ser realizada, a
função retorna o valor dessa escolha.
2.2.12 Pesquisa de princípios ativos
Passando o valor retornado da função anterior por parâmetro a esta função,
consegue-se extrair o nome do princípio ativo que tinha sido selecionado
anteriormente. A função de pesquisa vai assim retornar o nome do princípio ativo
selecionado.
2.2.13 Remover medicamento
Este procedimento permite remover um medicamento através do seu código. Se
não houver medicamentos o procedimento não será executado. No final de cada
remoção é solicitado ao utilizador se deseja continuar e se este for removendo
medicamentos até a lista ligada ficar vazia irá aparecer uma mensagem informando
que não existem mais medicamentos.
2.2.14 Remover princípio ativo
Este procedimento é similar ao anterior, mas funciona para a lista ligada de
princípios ativos.
2.2.15 Pesquisar medicamentos por laboratório
Este procedimento foi criado para satisfazer o critério de agrupar os
medicamentos por laboratórios. É pedido ao utilizador que insira o nome do
laboratório e serão mostrados todos os medicamentos do mesmo laboratório. Caso
não exista o laboratório selecionado será indicado que não existem medicamentos
pertencentes a esse laboratório.
2.2.16 Pesquisa por múltiplos critérios
Este procedimento permite ao utilizador efetuar pesquisas através da conjunção
de dois critérios previamente definidos. Estes critérios podem ser escolhidos pelo
utilizador e caso a conjunção de busca seja verdadeira ele vai mostrar o medicamento
ou medicamentos encontrados. O modo de funcionamento deste procedimento será
explicado mais detalhadamente posteriormente.
54. Cap. II – Descrição Técnica
46
2.2.17 Alterar dados de medicamento
Este procedimento permite alterar um dado do medicamento previamente
definido pelo utilizador através do seu código. Esta funcionalidade é criada de modo ao
utilizador poder alterar os dados diretamente a partir da aplicação sem ter de recorrer
ao ficheiro de texto.
2.2.18 Alterar dados de princípios ativos
Este procedimento é similar ao anterior, mas funciona para a estrutura
Principios.
2.2.19 Validar código
Esta função permite verificar se o código introduzido na estrutura Med já se
encontra utilizada. Esta função tanto será usada para verificar se o código já existe,
como para impedir que códigos repetidos sejam inseridos.
2.2.20 Validar número
Esta função é muito similar à anterior mas neste caso verifica os números
introduzidos na estrutura Principios.
2.2.21 Validar laboratório
Esta função permite verificar se o laboratório introduzido pelo utilizador existe
dentro da estrutura Med e serve para validar pesquisas que envolvam o nome do
laboratório.
2.2.22 Validar operações com estrutura medicamentos
Esta função é extramente simples, mas ao mesmo tempo importante, pois
permite verificar se a lista ligada Med se encontra ou não vazia dentro de
procedimentos em que a lista ligada não consta na sua assinatura.
2.2.23 Validar operações com estrutura princípios ativos
Esta função é similar à anterior, mas verifica a lista ligada da estrutura Principios.
55. Cap. II – Descrição Técnica
47
2.2.24 Selecionar critérios
Este procedimento apenas mostra a parte gráfica do menu de opções quando o
utilizador escolhe os critérios para a pesquisa. Foi desenvolvido para manter o código
principal mais pequeno.
2.2.25 Menu alterar medicamentos
Mais uma vez este procedimento apenas mostra a parte gráfica do menu de
opções quando o utilizador escolhe a opção alterar dados de medicamentos. O motivo
da criação deste procedimento foi o mesmo: manter o código principal mais pequeno.
2.2.26 Menu alterar princípios ativos
Este procedimento é muito similar ao anterior mas apresenta os critérios a
alterar para a estrutura Principios.
2.2.27 Inexistência de medicamentos
Este procedimento foi criado para apresentar um cabeçalho que ia ser utilizado
em diversas ocasiões, sempre que não houvesse medicamentos para listar. Para não
estar constantemente a repetir o código foi criado o procedimento.
2.2.28 Inexistência de princípios ativos
Este procedimento é similar ao anterior, mas o cabeçalho é referente à estrutura
Principios, que surge sempre que não há princípios ativos a listar.
2.2.29 Continuar
Este procedimento foi criado para evitar uma repetição excessiva do mesmo
código ao longo do programa. O procedimento faz uma espera, pede ao utilizador que
pressione uma tecla e a seguir limpa a consola.
2.2.30 Navegação nos menus
Estes procedimentos e funções são rigorosamente semelhantes aos utilizados na
primeira aplicação desenvolvida. A diferença comparativamente com esta aplicação é
que aqui estão a ser utilizados três menus encadeados em que é possível navegar
entre eles. Só é possível abandonar a aplicação a partir do primeiro menu devido à
instrução exit selecionando a opção 0.
56. Cap. II
48
3 Desenvolvimento
Ao se desenvolver o programa, foi garantido que as estruturas de dados eram
independentes e que a sua manipulação não entraria em conflito. A razão de uma
segunda estrutura de dados é carregar diretamente o princípio ativo para a primeira
estrutura, quando a segunda não for vazia. Ao mesmo tempo as duas estruturas
podem ser consultadas e geridas de forma independente.
O resultado final deste programa é o código fonte que se encontra no final do
capítulo. As técnicas de programação foram baseadas na primeira aplicação, sendo
que nesta secção apenas serão demonstradas as inovações desenvolvidas.
3.1 Declarações
Foram declaradas 4 variáveis globais para as listas ligadas do tipo apontador: 2
para a estrutura Med com o endereço inicial nulo e 2 para a estrutura Principios.
Med *aptInicio = NULL;
Med *aptFim = NULL;
Principios *aptInicio2 = NULL;
Principios *aptFim2 = NULL;
Estas declarações permitem manter atualizados os apontadores início e fim da
lista para as duas estruturas sempre que é introduzido um novo registo. Isto é
fundamental para quando se pretender remover um registo em qualquer das listas
ligadas.
3.2 Inserir novo medicamento
No procedimento inserir novo medicamento é declarada uma variável copia do
tipo apontador para carater com endereço inicial nulo.
void inserirMedicamento ()
{
int cod;
char nom[50], pa[50], om[100], lab[50];
char comp[50], pos[50], ind[50], ci[50];
char *copia = NULL;
int chave, manter = 1, teste = 0, teste3 = 0;
char escolha;
57. Cap. II – Desenvolvimento
49
Este apontador irá receber o endereço de memória do princípio ativo
selecionado, caso a lista ligada Principios não seja vazia quando o procedimento for
executado.
teste3 = validarOperacoesPrincipios (aptInicio2);
if (teste3 == 1)
{
chave = menuPrincipios (aptInicio2);
copia = pesquisaPrincipios (aptInicio2, chave);
strcpy (pa, copia);
}
else
{
printf ("PRINCÍPIO ATIVO: ");
gets (pa);
fflush (stdin);
}
Esta é a parte realmente diferente no programa. Quando o utilizador for inserir o
princípio ativo, é testado se a lista ligada Principios se encontra ou não vazia. Se esta
não estiver vazia é invocada a função menuPrincipios que retorna a escolha do
utilizador para a variável chave.
Seguidamente, através da função pesquisaPrincipios, o endereço de memória do
princípio ativo selecionado é guardado na variável copia.
Finalmente o princípio ativo é copiado para a variável pa através da instrução
strcpy e o programa prossegue.
Caso a lista ligada de princípios esteja vazia é solicitado ao utilizador que
introduza o princípio ativo manualmente.
3.3 Selecionar princípios ativos
A função menuPrincipios lista todos os registos da lista ligada Principios como se
fosse um menu permitindo ao utilizador fazendo uma escolha através do seu número.
No final a função retorna o número selecionado.
58. Cap. II – Desenvolvimento
50
int menuPrincipios (Principios *aptInicio2)
{
int escolhaNumero = 0;
if (aptInicio2 != NULL)
{
printf ("n");
printf ("*****************************************************n");
printf ("* *n");
printf ("* SELECIONAR PRINCÍPIO ATIVO *n");
printf ("* *n");
printf ("*****************************************************n");
printf ("n");
while (aptInicio2 != NULL)
{
printf ("%d - %s: %sn", aptInicio2 -> numero, aptInicio2 -> designacao,
aptInicio2 -> descricao);
printf ("n");
aptInicio2 = aptInicio2 -> seg;
}
}
printf ("nIntroduzir número: ");
scanf ("%d", &escolhaNumero);
fflush (stdin);
printf ("n");
return (escolhaNumero);
}
3.4 Pesquisa de princípios ativos
A função pesquisaPrincipios percorre a lista ligada Principios enquanto a chave
que recebe por parâmetro for diferente do número de cada um dos seus registos.
Quando encontra uma equivalência se o registo não for nulo retorna a designação do
princípio ativo. Caso contrário retornaria 0, que não irá acontecer neste programa pois
apenas irá pesquisar chaves confirmadas.
char *pesquisaPrincipios (Principios *aptInicio2, int chave)
{
while (aptInicio2 -> numero != chave)
aptInicio2 = aptInicio2 -> seg;
if (aptInicio2 != NULL)
return (aptInicio2 -> designacao);
return (0);
}
59. Cap. II – Desenvolvimento
51
3.5 Remover medicamentos
Os procedimentos para remover (neste caso remover medicamento) terão dois
testes: um para testar se a operação se justifica, caso contrário é sinal que não existem
medicamentos e outro para verificar se o código de medicamento que se pretende
remover existe.
void remover (Med **endInicio, Med **endFim)
{
Med *aux = *endInicio;
int manter3 = 1, apagar = 0, teste4 = 0, teste5 = 0;
char escolha3;
system ("cls");
teste4 = validarOperacoesMedicamentos (aptInicio);
if (teste4 == -1)
{
naoExiste ();
menu ();
}
else
{
No primeiro caso, se não existirem medicamentos, uma mensagem é impressa e
o programa retorna ao menu de opções. No segundo caso, se o código não existir, é
solicitado ao utilizador se pretende continuar ou retroceder.
while ((aux != NULL) && (aux -> codigo != apagar))
aux = aux -> seguinte;
if ((aux != NULL) && (aux -> anterior == NULL))
{
*endInicio = aux -> seguinte;
free (aux);
}
else if ((aux != NULL) && (aux -> seguinte == NULL))
{
*endFim = aux -> anterior;
aux -> anterior -> seguinte = NULL;
free (aux);
}
else if ((aux!=NULL)&&(aux->seguinte==NULL)&&(aux->anterior==NULL))
{
*endInicio = NULL;
*endFim = NULL;
free (aux);
}
else if (aux != NULL)
{
aux -> anterior -> seguinte = aux -> seguinte;
aux -> seguinte -> anterior = aux -> anterior;
free (aux);
}
60. Cap. II – Desenvolvimento
52
Se o código for válido, o programa corre a lista ligada enquanto o código a
apagar for diferente do código do registo. Quando aparece uma equivalência existem 4
possibilidades: o registo estar no início da lista ligada (1ª condição), o registo estar no
fim da lista ligada (2ª condição), só existir um registo (3ª condição) ou o registo estar
numa posição algures a meio da lista ligada. Verificada essa condição, são executadas
as instruções para religar a nova lista ligada sem o registo removido e o registo é
apagado através da instrução free.
3.6 Pesquisa por múltiplos critérios
O procedimento pesquisaMultiplos vai permitir efetuar pesquisas pela
conjunção de dois critérios selecionados pelo utilizador.
void pesquisaMultiplos (Med *aptInicio)
{
int criterio1=0, criterio2=0, caminho=0, cnt=0, teste9=0, teste10=0;
teste9 = validarOperacoesMedicamentos (aptInicio);
if (teste9 == -1)
{
naoExiste ();
menu2 ();
}
else
{
selecionarCriterios ();
printf ("nA: ");
scanf ("%d", &criterio1);
fflush (stdin);
printf ("B: ");
scanf ("%d", &criterio2);
fflush (stdin);
caminho = criterio1 * criterio2;
Estes são dados a escolher ao utilizador através da invocação do procedimento
selecionarCriterios. Após isto o utilizador deve introduzir duas escolhas que serão dois
valores de 1 a 4.
printf ("-----------------------------------------------------------n");
printf ("| 1 | Código |n");
printf ("-----------------------------------------------------------n");
printf ("| 2 | Nome |n");
printf ("-----------------------------------------------------------n");
printf ("| 3 | Princípio ativo |n");
printf ("-----------------------------------------------------------n");
printf ("| 4 | Laboratório |n");
printf ("-----------------------------------------------------------n");
61. Cap. II – Desenvolvimento
53
Existem várias combinações que podem ser simplificadas através de um caminho
de pesquisa. Para isso multiplica-se o valor das duas escolhas:
Código * Nome 2 Nome * Princípio ativo 6
Código * Princípio ativo 3 Nome * Laboratório 8
Código * Laboratório 4 Princípio ativo * Laboratório 12
Como o código por definição é único, qualquer pesquisa envolvendo o código e o
nome, apenas vai encontrar uma ocorrência.
if (caminho == 2) // Pesquisa por código
{
int _cod;
printf ("Código: ");
scanf ("%d", &_cod);
Se o caminho encontrado for 3, a pesquisa será por código e princípio ativo.
else if (caminho == 3) // pesquisa por código/principio
{
int _ccod;
char _ppat[50];
printf ("Código: ");
scanf ("%d", &_ccod);
fflush (stdin);
printf ("Princípio ativo: ");
gets (_ppat);
Se o caminho encontrado for 4, a pesquisa será por código e laboratório.
else if (caminho == 4) // pesquisa por código/laboratório
{
int _codd;
char _labb[50];
printf ("Código: ");
scanf ("%d", &_codd);
fflush (stdin);
printf ("Laboratório: ");
gets (_labb);
Se o caminho encontrado for 6, a pesquisa será por nome e princípio ativo.
else if (caminho == 6) // pesquisa por nome/principio
{
char _nome[50], _pat[50];
printf ("Nome: ");
gets (_nome);
fflush (stdin);
printf ("Princípio ativo: ");
gets (_pat);
62. Cap. II – Desenvolvimento
54
Se o caminho encontrado for 8, a pesquisa será por nome e laboratório.
else if (caminho == 8) // pesquisa por nome/laboratório
{
char _name[50], _labot[50];
printf ("Nome: ");
gets (_name);
fflush (stdin);
printf ("Laboratório: ");
gets (_labot);
Finalmente, se o caminho encontrado for 12, a pesquisa será por princípio ativo
e laboratório.
else if (caminho == 12) // pesquisa por princípio/laboratório
{
char _pat[50], _laborat[50];
printf ("Princício ativo: ");
gets (_pat);
fflush (stdin);
printf ("Laboratório: ");
gets (_laborat);
3.7 Validar laboratório
As funções de validar operações e validar códigos/números já foram
demonstradas no primeiro trabalho. No entanto esta é a primeira em que a validação é
feita através da instrução strcmp, que é possível realizar através da inclusão da
biblioteca string.h.
int validarLaboratorio (Med *aptInicio, char *test3)
{
while (aptInicio != NULL)
{
if ((strcmp (aptInicio -> laboratorio, test3)) == 0)
return (1);
aptInicio = aptInicio -> seguinte;
}
return (0);
}
A instrução strcmp devolve 0 se ao comparar duas strings elas forem iguais e
devolve 1 se elas forem diferentes. Assim, a lista ligada será percorrida enquanto a
comparação for diferente de 0.
Quando a strcmp devolver 0 significa que encontrou uma equivalência e nesse
caso a função retorna 1. Caso não encontre nenhuma equivalência enquanto houver
registos na lista ligada, a função retorna 0.
63. Cap. II
55
4 Testes e Resultados
4.1 Menu de opções principal
O menu de opções principal apresenta três funcionalidades fundamentais
(inserir, remover ou alterar medicamento) e capacidade de navegação para outros dois
menus secundários (consultas e gestão de princípios ativos).
Figura 10 – Menu de opções principal.
4.2 Inserir novo medicamento
Esta funcionalidade permite inserir dados de um novo medicamento, escolhendo
neste caso o principio ativo a partir de um menu.
Figura 11 – Inserir novo medicamento.
64. Cap. II – Testes e Resultados
56
4.3 Alterar dados de medicamentos
Esta funcionalidade permite alterar os dados de um medicamento inserido.
Neste exemplo o código do exame que se pretendia alterar não existia no sistema,
logo é requerido ao utilizador se deseja continuar.
Figura 12 – Alterar dados de medicamentos.
4.4 Pesquisa por laboratório
Esta funcionalidade permite agrupar os medicamentos registados por
laboratório, que é introduzido pelo utilizador. Este é um dos requisitos deste trabalho.
Figura 13 – Pesquisa por laboratório.
65. Cap. II – Testes e Resultados
57
4.5 Pesquisa por múltiplos critérios
Esta funcionalidade permite pesquisar medicamentos por mais de um critério
selecionados pelo utilizador. Neste caso seria o nome e o laboratório. Este era outro
dos requisitos do trabalho.
Figura 14 – Pesquisa por múltiplos critérios.
4.6 Listar princípios ativos
Esta funcionalidade permite listar os princípios ativos registados na estrutura
auxiliar do sistema. Não é um requisito obrigatório do trabalho.
Figura 15 – Listar princípios ativos.