Você está na página 1de 46

apêndice 2

Metodologia da Programação e
Desenvolvimento de Software

Sumário

2.1 Fases na resolução de problemas 2.7 O ciclo de vida do software


2.2 Programação modular 2.8 Métodos formais de
2.3 Programação estruturada verificação de programas
2.4 Conceito e características de
2.9 Resumo
algoritmos
2.5 Escrita de algoritmos 2.10 Exercícios
2.6 Representação gráfica dos algoritmos 2.11 Exercícios resolvidos

1
INTRODUÇÃO
Este capítulo introduz a metodologia a ser se- Um dos objetivos fundamentais deste li-
guida para a resolução de problemas com vro é a aprendizagem e o projeto dos algorit-
computadores e com uma linguagem de pro- mos. Este capítulo introduz ao leitor o concei-
gramação como C. to de algoritmo e de programa, bem como as
A resolução de um problema com um com- ferramentas que permitem ao usuário «dialo-
putador se faz pela escrita de um programa, gar» com a máquina: as linguagens de pro-
que exige no mínimo os seguintes passos: gramação.

1.  Definição ou análise do problema.


2.  Projeto do algoritmo.
3.  Transformação do algoritmo em um
programa.
4.  Execução e validação do programa.

CONCEITOS-CHAVE
• Algoritmo • Métodos formais
• Ciclo de vida • Pós-condições
• Projeto descendente • Precondições
• Diagrama Nassi Schneiderman • Programação modular
• Diagramas de fluxo • Programação estruturada
• Projeto • Testes
• Domínio do problema • Pseudocódigo
• Fatores de qualidade • Verificação
• Invariantes

2
Metodologia da programação e desenvolvimento de software   3

2.1 FASES NA RESOLUÇÃO DE PROBLEMAS


O processo de resolução de um problema por meio de um computador leva à escrita de um programa e à sua
execução no computador. Ainda que o processo de projetar programas seja — essencialmente — um processo
criativo, pode-se considerar uma série de fases ou passos comuns que em geral devem ser seguidos por todos
os programadores.
As fases de resolução de um problema com computador são:
•  Análise do problema.
•  Projeto do algoritmo.
•  Codificação.
•  Compilação e execução.
•  Verificação.
•  Depuração.
•  Manutenção.
•  Documentação.
Constituem o ciclo de vida do software e suas características mais importantes:
•  Análise. O problema é analisado levando-se em conta a especificação dos requisitos dados pelo cliente da
empresa ou pela pessoa que encomenda o programa.
•  Projeto. Uma vez analisado o problema, projeta-se uma solução que conduzirá a um algoritmo que resolva
o problema.
•  Codificação (implementação). A solução é escrita na sintaxe da linguagem de alto nível (por exemplo, C)
e se obtém um programa-fonte que em seguida é compilado.
•  Execução, verificação e depuração. O programa é executado e rigorosamente comprovado e são eliminados
todos os erros (em inglês, denominados bugs) que possam aparecer.
•  Manutenção. O programa é atualizado e modificado cada vez que é necessário, de modo que todas as ne-
cessidades de mudança sejam efetuadas.
•  Documentação. Escrita das diferentes fases do ciclo de vida do software, essencialmente a análise, o pro-
jeto e a codificação, unidos a manuais de usuário e de referência, bem como normas para a manutenção.
As duas primeiras fases conduzem a um projeto detalhado escrito em forma de algoritmo. Durante a terceira
etapa (codificação), implementa-se o algoritmo em um código escrito em uma linguagem de programação, refle-
tindo as idéias desenvolvidas nas fases de análise e projeto.
As fases de compilação e execução traduzem e executam o programa. Nas fases de verificação e depuração,
o programador busca erros das etapas anteriores e os elimina. É comprovado que, quanto mais tempo se gasta na
fase de análise e projeto, menos se gastará na depuração do programa. Por último, deve-se desenvolver a docu-
mentação do programa.
Antes de conhecer as tarefas a serem desenvolvidas em cada fase, vamos considerar o conceito e o significado
da palavra algoritmo. A palavra algoritmo é derivada da tradução para o latim da palavra Al-Khowarizmi,1 nome
de um matemático e astrônomo árabe no século IX que escreveu um tratado sobre manipulação de números e
equações. Um algoritmo é um método para resolver um problema por meio de uma série de passos precisos, de-
finidos e finitos.

Características de um algoritmo
•  preciso (indica a ordem de realização em cada passo);
•  definido (o mesmo resultado é obtido a cada vez que é seguido);
•  finito (tem fim; um número determinado de passos).

1
  Escreveu um tratado matemático famoso sobre manipulação de números e equações, intitulado Kitab al-jabr w’almugabala. A palavra álgebra é derivada, por sua
semelhança sonora, de al-jabr.
4   Programação em C: Metodologia, algoritmos e estruturas de dados

Um algoritmo deve produzir um resultado em um tempo finito. Os métodos que utilizam algoritmos são deno-
minados métodos algorítmicos e levam esse nome em oposição aos métodos que implicam algum juízo ou interpre-
tação denominados métodos heurísticos. Os métodos algorítmicos podem ser implementados em computadores,
entretanto, os processos heurísticos não foram facilmente convertidos para os computadores. Nos últimos anos, as
técnicas de inteligência artificial tornaram possível a implementação do processo heurístico em computadores.
Exemplos de algoritmos são: instruções para montar em uma bicicleta, fazer uma receita de comida, obter o
máximo divisor comum de dois números etc. Os algoritmos podem ser expressos por fórmulas, diagramas de
fluxo ou N-S e pseudocódigos. Essa última representação é a mais utilizada para seu uso com linguagens estrutu-
radas como C.

2.1.1 Análise do problema


A primeira fase da resolução de um problema com computador é a análise do problema. Essa fase requer uma cla-
ra definição, na qual se contemple exatamente o que o programador deve fazer e o resultado ou solução desejada.
Dado que se busca uma solução por computador, são necessárias especificações detalhadas de entrada e de
saída. A Figura 2.1 mostra os requisitos que devem ser definidos na análise.

Resolução
de um
problema

Análise Projeto Resolução do


do do problema com
problema algoritmo computador

Figura 2.1 Análise do problema.

Para poder identificar e definir bem um problema, é conveniente responder às seguintes perguntas:
•  Que entradas são necessárias? (tipo e quantidade de dados com os quais se trabalha).
•  Qual é a saída desejada? (tipo e quantidade de dados dos resultados).
•  Que método produz a saída desejada?
•  Requisitos ou necessidades adicionais e restrições para a solução.

Problema 2.1
Obter uma tabela com as depreciações acumuladas e os valores reais de cada ano de um automóvel comprado
em 1996 por 1.800.000 pesetas, durante os seis anos seguintes, supondo um valor de recuperação ou resgate de
120.000. Desenvolver a análise do problema, conhecendo a fórmula da depreciação anual constante D para cada
ano de vida útil.

custo 2 valor de recuperação


D5
vida útil

1.800.000 2 120.000 1.680.000


D5 5 5 280.000
6 6

Entrada custo original


vida útil
valor de recuperação
Metodologia da programação e desenvolvimento de software   5

Saída depreciação anual por ano


depreciação acumulada em cada ano
valor do automóvel em cada ano

Processo depreciação acumulada


cálculo da depreciação acumulada cada ano
cálculo do valor do automóvel em cada ano

A tabela a seguir mostra a saída solicitada.

Ano Depreciação Depreciação Valor anual


acumulada
1 (1996) 280.000 280.000 1.520.000
2 (1997) 280.000 560.000 1.240.000
3 (1998) 280.000 840.000 960.000
4 (1999) 280.000 1.120.000 680.000
5 (2000) 280.000 1.400.000 400.000
6 (2001) 280.000 2.180.000 120.000

2.1.2 Projeto do algoritmo


Na etapa de análise do processo de programação define-se o que o programa deve fazer. Na etapa de projeto, de-
termina-se como o programa realiza a tarefa solicitada. Os métodos mais eficazes para o processo de projeto se
baseiam no conhecido ditado divida e vencerás, isto é, a resolução de um problema complexo realiza-se dividindo
o problema em subproblemas e, em seguida, dividindo esses subproblemas em outros de nível mais baixo até que
possa ser implementada uma solução no computador. Esse método é tecnicamente conhecido como projeto des-
cendente (top-down) ou modular. O processo de quebrar o problema em cada etapa e expressar cada passo da
maneira mais detalhada é denominado refinamento sucessivo.
Cada subprograma é resolvido por meio de um módulo (subprograma) que tem um só ponto de entrada e um
só ponto de saída.
Qualquer programa bem projetado é constituído por um programa principal (o módulo de nível mais alto) que
chama a subprogramas (módulos de nível mais baixo), que, por sua vez, podem chamar a outros subprogramas.
Os programas estruturados dessa maneira são designados como projeto modular e o método de quebrar o progra-
ma em módulos menores é denominado programação modular. Os módulos podem ser planejados, codificados,
comprovados e depurados de modo independente (inclusive, por diferentes programadores) e, em seguida, combi-
nados entre si. O processo implica a execução dos seguintes passos até que o programa termine:
1.  Programar um módulo.
2.  Comprovar o módulo.
3.  Se for necessário, depurar o módulo.
4.  Combinar o módulo com os módulos anteriores.
O processo que converte os resultados da análise do problema em um projeto modular com sucessivos refina-
mentos que permitem uma posterior tradução para uma linguagem é denominado projeto do algoritmo.
O projeto do algoritmo independe da linguagem de programação que será utilizada para a posterior codificação.

2.1.3 Ferramentas de programação


As duas ferramentas mais comumente utilizadas para projetar algoritmos são: diagramas de fluxo e pseudocódigos.
Um diagrama de fluxo (flowchart) é uma representação gráfica de um algoritmo. Os símbolos utilizados
foram normatizados pelo American National Standard Institute (Ansi — Instituto Nacional Americano de Padro-
6   Programação em C: Metodologia, algoritmos e estruturas de dados

nização) e os empregados com mais freqüência são mostrados na Figura 2.2, juntamente com um modelo utiliza-
do para o projeto dos diagramas de fluxo (Figura 2.3). Na Figura 2.4 está representado o diagrama de fluxo que
resolve o Problema 2.1.

Entrada/
Terminal Subprograma
Saída

Decisão Processo
Não

Sim Conectores

Figura 2.2 Símbolos mais utilizados nos diagramas de fluxo.

Figura 2.3 Modelo para projeto de diagramas de fluxo.

O pseudocódigo é uma ferramenta de programação na qual as instruções são escritas em palavras similares
ao inglês ou ao português, que facilitam tanto a escrita como a leitura de programas. Em essência, o pseudocódigo
pode ser definido como uma linguagem de especificações de algoritmos.
Ainda que não existam regras para a escrita do pseudocódigo em português, elegeu-se uma notação-padrão
que será utilizada neste livro e que já é bastante empregada em livros de programação.2
As palavras reservadas básicas serão representadas por letras minúsculas em negrito. Essas palavras são tra-
dução livre de palavras reservadas de linguagens como C, Pascal etc. Mais à frente serão indicados os pseudocó-
digos fundamentais a ser utilizados nesta obra.

2
  Para maiores esclarecimentos sobre o pseudocódigo, pode-se consultar, entre outras, algumas dessas obras em espanhol: JOYANES,
Luis. Fundamentos de programación, 2. ed., 1997; Id. Metodologia de la programación, 1986; Id. Problemas de Metodología de la pro-
gramación, 1991 (todas elas publicadas por McGraw-Hill, Madri); bem como CLAVEL; BIONDI. Introducción a la programación. Bar-
celona: Masson, 1987; ou também BRAUNSTEIN; GROIA. Introducción a la programación y a las estructuras de datos. Buenos Aires:
Editorial Eudeba, 1986. Para uma formação prática, pode-se consultar: JOYANES, Luis et al. Fundamentos de programación: Libro de
problemas. Madri: McGraw-Hill, 1998.
Metodologia da programação e desenvolvimento de software   7

O pseudocódigo que resolve o Problema 2.1 é:


Previsões de depreciação
Introduzir custo
vida útil
valor final de resgate (recuperação)
imprimir cabeçalhos
Estabelecer o valor inicial do Ano
Calcular depreciação
enquanto valor ano =< vida útil fazer
calcular depreciação acumulada
calcular valor atual
imprimir uma linha na tabela
incrementar o valor do ano
fim de enquanto

Exemplo 2.1
Calcular o pagamento final de um trabalhador conhecendo o número de horas trabalhadas, a taxa horária e a
alíquota de impostos.
Algoritmo
1. Ler Horas, Taxa, alíquota
2. Calcular PagamentoBruto = Horas * Taxa
3. Calcular Impostos = PagamentoBruto * Alíquota
4. Calcular PagamentoFinal = PagamentoBruto — Impostos
5. Visualizar PagamentoBruto, Impostos, PagamentoFinal

Início

Não
Ano < Vida_Útil
Ler
Custo, Vida
Útil, ValorResgate Sim

Acumulada ←
Acumulada + Fim
Ler Ano Depreciação

Valor atual ← Custo Valor atual ←


Depreciação ← Valor atual +
(Custo-Valor-Resgate)/ Depreciação
VidaÚtil
Acumulada ← 0
Ano ← Ano + 1

Figura 2.4 Diagrama de fluxo (Exemplo 2.1).

Exemplo 2.2
Calcular o valor da soma 1+2+3+...+100.
8   Programação em C: Metodologia, algoritmos e estruturas de dados

Algoritmo
Uma variável Contador é utilizada como um contador que gere os sucessivos números inteiros e Soma é utiliza-
da para armazenar as somas parciais 1, 1+2, 1+2+3…
1. Estabelecer Contador a 1
2. Estabelecer Soma a 0
3. enquanto Contador < = 100 fazer
Somar Contador a Soma
Incrementar Contador em 1
fim_enquanto
4. Visualizar Soma

2.1.4 Codificação de um programa


Codificação é a escrita em uma linguagem de programação da representação do algoritmo desenvolvida nas etapas
precedentes. Dado que o projeto de um algoritmo é independente da linguagem de programação utilizada para sua
implementação, o código pode ser escrito com igual facilidade em qualquer linguagem.
Para realizar a conversão do algoritmo em programa, as palavras reservadas escritas em português devem ser
substituídas por palavras homônimas em inglês e as operações/instruções indicadas em linguagem natural devem
ser transformadas na linguagem de programação correspondente.
/*
Este programa obtém uma tabela das depreciações acumuladas e
valores reais de cada ano de determinado produto
*/
#include <stdio.h>
void main()
{
double Custo, Depreciação,
Valor_Recuperação,
Valor_Atual,
Acumulado,
Valor_Anual;
int Ano, Vida_Útil;
puts(“Introduza custo, valor recuperação e vida útil”);
scanf(“%lf %lf %d”,&Custo,&Valor_Recuperação,&Vida_Útil);
puts(“Introduza ano atual”);
scanf(“%d”,&Ano);
Valor_Atual = Custo;
Depreciação = (Custo-Valor_Recuperação)/Vida_Útil; Acumulado = 0;
puts(“Ano Depreciação Dep. Acumulada”);
while (Ano < Vida_Útil)
{
Acumulado = Acumulado + Depreciação;
Valor_Atual = Valor_Atual – Depreciação;
printf(“Ano: %d, Depreciação:%.2lf, %.2lf Acumulada”,
Ano,Depreciação,Acumulado);
Ano = Ano + 1;
}
}

Documentação interna
Como se verá adiante, a documentação de um programa é classificada em interna e externa. A documentação
interna é aquela que é incluída dentro do código do programa fonte por meio de comentários que ajudam na
Metodologia da programação e desenvolvimento de software   9

compreensão do código. Todas as linhas de programas que comecem com um símbolo / * são comentários. O pro-
grama não necessita deles e o computador os ignora. Essas linhas de comentários servem somente para tornar os
programas mais fáceis de compreender. O programador deve ter como objetivo escrever códigos simples e limpos.
Como as máquinas atuais suportam grandes memórias (512 Mb ou 1.024 Mb de memória central mínima em
computadores pessoais), não é necessário recorrer a técnicas de economia de memória, sendo assim é recomendá-
vel que se inclua o maior número de possível de comentários desde que sejam significativos.

2.1.5 Compilação e execução de um programa


Uma vez que o algoritmo já tenha sido convertido em um programa-fonte, será preciso introduzi-lo na memória por
meio do teclado e posteriormente armazená-lo em um disco. Essa operação é realizada com um programa editor.
Logo após, o programa-fonte é convertido em um arquivo de programa que deve ser guardado (gravado) em disco.
O programa-fonte deve ser traduzido para linguagem de máquina. Esse processo é realizado com o compila-
dor e é o sistema operacional que se encarrega da compilação.
Se depois da compilação são encontrados erros (erros de compilação) no programa-fonte, é preciso editar
o programa outra vez, corrigir os erros e compilar novamente. Esse processo deve ser repetido até que erros não
sejam produzidos, obtendo-se então o programa-objeto que ainda assim não é diretamente executável. Supon-
do que não existam erros no programa-fonte, deve-se instruir o sistema operacional para que realize a fase de
montagem ou vinculação (link), carga, do programa-objeto com as bibliotecas do programa do compilador.

CPU Memória Memória


externa externa
EIDCIDE CPU

Editor Programa Programa-


Teclado de textos editor objeto
Enlace do Programa
programa de carga

a)
c)

Memória
externa
CPU

Programa
editor
Compilador Programa-
objeto

b)

Figura 2.5 Fases da compilação/execução de um programa: a) edição; b) compilação; c) montagem ou vinculação.

O processo de montagem produz um programa executável. A Figura 2.5 descreve o processo completo de com-
pilação/execução de um programa.
Depois de criado o programa executável, este poderá ser executado (rodado) do sistema operacional, bastando,
para isso, apenas teclar seu nome (no caso de DOS). Supondo que não existam erros durante a execução (chama-
dos erros em tempo de execução), será obtida a saída de resultados do programa.
10   Programação em C: Metodologia, algoritmos e estruturas de dados

As instruções ou ordens para compilar e executar um programa em C podem variar segundo o tipo de compi-
lador. Assim, o processo de Visual C++ é diferente do de C com o UNIX ou o Linux.

2.1.6 Verificação e depuração de um programa


A verificação ou compilação de um programa é o processo de execução do programa com ampla variedade
de dados de entrada chamados dados de teste, que determinarão se o programa tem erros («bugs»). Para rea­
lizar a verificação, deve-se desenvolver ampla gama de dados de teste: valores normais de entrada, valores
extremos de entrada que comprovem os limites do programa e valores de entrada que comprovem aspectos
especiais do programa.
A depuração é o processo de encontrar os erros do programa e corrigir ou eliminar esses erros.
Quando um programa é executado, três tipos de erros podem ser produzidos:
1. Erros de compilação. Normalmente, são produzidos pelo uso incorreto das regras de linguagem de pro-
gramação e costumam ser erros de sintaxe. Se existe um erro de sintaxe, o computador não pode com­
preender a instrução, não será obtido o programa-objeto e o compilador imprimirá uma lista de todos os
erros encontrados durante a compilação.
2. Erros de execução. Esses erros são produzidos por instruções que o computador pode compreender, mas
não executar. Exemplos típicos são: divisão por zero e raízes quadradas de números negativos. Nesses
casos, detém-se a execução do programa e imprime-se uma mensagem de erro.
3. Erros lógicos. São produzidos na lógica do programa e a fonte do erro costuma ser o projeto do algoritmo.
Esses erros são os mais difíceis de detectar, já que o programa pode funcionar e não produzir erros de
compilação nem de execução, e o erro só será descoberto pela obtenção de resultados incorretos. Assim,
deve-se voltar para a fase do projeto do algoritmo, modificá-lo, mudar o programa-fonte e compilar e exe-
cutar mais uma vez.

2.1.7 Documentação e manutenção


A documentação de um problema é formada pelas descrições dos passos a serem dados no processo de resolução
do problema. A importância da documentação deve ser destacada por sua decisiva influência no produto final. Pro-
gramas mal documentados são difíceis de ler, mais difíceis de depurar e quase impossíveis de manter e modificar.
A documentação de um programa pode ser interna e externa. A documentação interna é aquela contida nas
linhas de comentários. A documentação externa inclui análise, diagramas de fluxo e/ou pseudocódigos, manuais
do usuário com instruções para executar o programa e para interpretar os resultados.
A documentação é vital quando se deseja corrigir possíveis erros futuros ou mudar o programa. Tais mudanças
são denominadas manutenção do programa. Depois de cada mudança a documentação deve ser atualizada para
facilitar mudanças posteriores. É prática freqüente numerar as sucessivas versões dos programas 1.0, 1.1, 2.0, 2.1
etc. (quando as mudanças introduzidas são importantes, ocorre variação no primeiro dígito [1.0, 2.0...], no caso de
pequenas mudanças, somente muda o segundo dígito [2.0, 2.1…].)

2.2 PROGRAMAÇÃO MODULAR


A programação modular é um dos métodos de projeto mais flexível e potente para melhorar a produtividade de
um programa. Na programação modular, o programa é dividido em módulos (partes independentes), e cada um
dos quais executa uma única atividade ou tarefa e se codificam independentemente outros módulos. Cada um des-
tes módulos é analisado, codificado e tratado separadamente. Cada programa contém um módulo denominado
programa principal que controla tudo o que ocorre. Esse controle é transferido para submódulos (posteriormente
serão denominados subprogramas) de modo que eles possam executar suas funções, entretanto, cada submódulo
devolve o controle ao módulo principal após completar sua tarefa. Se a tarefa atribuída a cada submódulo é de-
masiadamente complexa, este deverá quebrar-se em outros módulos menores. O sucessivo processo de subdivisão
de módulos continua até que cada módulo tenha de executar somente uma tarefa específica. Essa tarefa pode ser
Metodologia da programação e desenvolvimento de software   11

entrada, saída, manipulação de dados, controle de outros módulos ou alguma combinação destes. Um módulo
pode transferir temporariamente (bifurcar) o controle para outro módulo, entretanto, cada módulo deve eventual-
mente devolver o controle ao módulo do qual originalmente recebeu o controle.
Os módulos são independentes no sentido que nenhum módulo pode ter acesso direto a qualquer outro módu-
lo, exceto ao módulo ao qual chama e seus próprios submódulos. No entanto, os resultados produzidos por um
módulo podem ser utilizados por qualquer outro módulo quando o controle é transferido para eles.

Raiz

Módulo 1 Módulo 2 Módulo 3 Módulo 4

Módulo 11 Módulo 12 Módulo 31 Módulo 41 Módulo 42

Módulo 21 Módulo 22

Módulo 221 Módulo 222

Figura 2.6 Programação modular.

Dado que os módulos são independentes, diferentes programadores podem trabalhar simultaneamente em di-
ferentes partes do mesmo programa. Isso reduzirá o tempo de projeto do algoritmo e a posterior codificação do
programa. Além disso, um módulo pode ser radicalmente modificado sem afetar outros módulos, inclusive sem
alterar sua função principal.
A decomposição de um programa em módulos independentes mais simples é conhecida como o método de
«divide e vencerás» (divide and conquer). Cada módulo é projetado com independência dos demais e, seguin-
do um método ascendente ou descendente, se chegará até a decomposição final do problema em módulos de
forma hierárquica.

2.3 PROGRAMAÇÃO ESTRUTURADA


As designações programação modular, programação descendente e programação estruturada foram introduzidas
na segunda metade da década de 1960 e, ainda que não tenham o mesmo significado, elas são, com freqüência,
utilizadas como sinônimos. As programações modular e descendente foram examinadas anteriormente. Programa-
ção estruturada significa escrever um programa de acordo com as seguintes regras:
• O programa tem um projeto modular.
• Os módulos são projetados de modo descendente.
• Cada módulo é codificado utilizando as três estruturas básicas de controle: seqüência, seleção e repetição.
Para quem está familiarizado com linguagens como BASIC, Pascal, FORTRAN ou C, a programação estrutu-
rada significa também programação sem /GOTO/ (C não requer o uso da sentença GOTO).
A designação programação estruturada se refere a um conjunto de técnicas que foram evoluindo desde os
primeiros trabalhos de Edgar Dijkstra. Essas técnicas aumentam consideravelmente a produtividade do programa
12   Programação em C: Metodologia, algoritmos e estruturas de dados

reduzindo em elevado grau o tempo necessário para escrever, verificar, depurar e manter os programas. A pro-
gramação estruturada utiliza um número limitado de estruturas de controle que minimizam a complexidade dos
programas e, conseqüentemente, reduzem os erros, tornam os programas mais fáceis de escrever, verificar, ler e
manter. Os programas devem estar dotados de uma estrutura.
A programação estruturada é um conjunto de técnicas que incorporam:
• recursos abstratos;
• projeto descendente (top-down);
• estruturas básicas.

2.3.1 Recursos abstratos


A programação estruturada utiliza dos recursos abstratos em vez dos recursos concretos de que dispõe determina-
da linguagem de programação.
Decompor um programa em termos de recursos abstratos — segundo Dijkstra — consiste em decompor dada
ação complexa em um número de ações mais simples, capazes de executá-las ou que constituam instruções dispo-
níveis de computadores.

2.3.2 Projeto descendente (top-down)


O projeto descendente (top-down) é o processo pelo qual um problema é decomposto em uma série de níveis ou
passos sucessivos de refinamento (stepwise). A metodologia descendente consiste em efetuar uma relação entre as
sucessivas etapas de estruturação de modo que estas se relacionem umas com as outras por meio de entradas e
saídas de informação, isto é, decompor-se o problema em etapas ou estruturas hierárquicas de maneira que cada
estrutura possa ser considerada de dois pontos de vista: o que faz? e como faz?
Levando em conta um nível n de refinamento, as estruturas serão consideradas da seguinte maneira:

Nível n: do exterior Nível n + 1: vi sta do interior


«o que fazer?» «como fazer?»

O projeto descendente pode ser visto na Figura 2.7.

Figura 2.7 Projeto descendente.


Metodologia da programação e desenvolvimento de software   13

2.3.3 Estruturas de controle


As estruturas de controle de uma linguagem de programação são métodos para especificar a ordem em que as
instruções de um algoritmo serão executadas. A ordem de execução das sentenças (linguagem) ou instruções
determina o fluxo de controle. Essas estruturas de controle são, portanto, fundamentais nas linguagens de pro-
gramação e nos projetos de algoritmos, especialmente os pseudocódigos.
As três estruturas básicas de controle são:
• seqüência;
• seleção;
• repetição.
e serão estudadas nos capítulos 5 e 6.
A programação estruturada torna os programas mais fáceis de escrever, verificar, ler e manter. Utiliza um nú-
mero limitado de estruturas de controle que minimiza a complexidade dos problemas.

2.3.4 Teorema da programação estruturada: estruturas básicas


Em maio de 1966, Böhm e Jacopini demonstraram que um programa próprio pode ser escrito utilizando somente
três tipos de estruturas de controle.
• seqüenciais;
• seletivas;
• repetitivas.
Um programa é definido como próprio quando satisfaz as seguintes características:
• Possui um só ponto de entrada e um de saída para controle do programa.
• Existem caminhos desde a entrada até a saída que podem ser seguidos e que passam por todas as partes do
programa.
• Todas as instruções são executáveis e não existem laços infinitos (sem fim).

A programação estruturada significa que


• O programa completo tem um projeto modular.
• Os módulos são projetados com metodologia descendente (também pode ser ascendente).
• Cada módulo é codificado utilizando as três estruturas básicas de controle: seqüenciais, seletivas e
repetitivas (ausência total de sentenças GOTO).
• Estruturação e modularidade são conceitos complementares (se completam).

2.4 CONCEITO E CARACTERÍSTICAS DE ALGORITMOS


O principal objetivo deste texto é ensinar a resolver problemas por meio de um computador. O programador de
computador é antes de mais nada uma pessoa que resolve problemas, portanto, para chegar a ser um programador
eficaz, é necessário aprender a resolver problemas de um modo rigoroso e sistemático. No decorrer deste livro,
vamos nos referir à metodologia necessária para resolver problemas por meio de programas, conceito denomina-
do metodologia da programação. O eixo central dessa metodologia é o conceito, já visto, de algoritmo.
Um algoritmo é um método para resolver um problema. Ainda que a popularização do termo tenha vindo com
o advento da era informática, algoritmo provém — como se comentou — de Mohammed Al-Khowarizmi, mate-
mático árabe que viveu durante o século IX e conquistou grande reputação por enunciar, passo a passo, as regras
para somar, diminuir, multiplicar e dividir números decimais. Da tradução para o latim do seu sobrenome na pa-
lavra algorismus derivou em algoritmo. Euclides, o grande matemático grego (do século IV a.C.) que inventou um
método para encontrar o máximo divisor comum de dois números, é considerado, ao lado de Al-Khowarizmi, o
outro grande pai da algoritmia (ciência que trata dos algoritmos).
14   Programação em C: Metodologia, algoritmos e estruturas de dados

O professor Niklaus Wirth — inventor de Pascal, Modula-2 e Oberon — intitulou um dos seus mais famosos
livros, Algoritmos e estruturas de dados, mostrando-nos que só é possível chegar a desenvolver um bom programa
com o projeto de um algoritmo e uma correta estrutura de dados. Essa equação será uma das hipóteses fundamen-
tais consideradas nesta obra.
A resolução de um problema exige o projeto de um algoritmo que resolva o problema proposto.

Projeto Programa
Problema
do algoritmo de computador

Figura 2.8 Resolução de um problema.

Os passos para a resolução de um problema são:


1. Projeto do algoritmo que descreva a seqüência ordenada de passos — sem ambigüidades — que conduzam
à solução de um problema dado. (Análise do problema e desenvolvimento do algoritmo.)
2. Expressar o algoritmo como um programa em uma linguagem de programação adequada. (Fase de codi-
ficação.)
3. Execução e validação do programa pelo computador.
Para desenvolver um programa, é necessário o projeto prévio de um algoritmo, de modo que sem algoritmo
não pode existir um programa.
Os algoritmos são independentes tanto da linguagem de programação em que são expressos como do com-
putador que os executa. Em cada problema, o algoritmo pode ser expresso em uma linguagem de programação
diferente e ser executado em um computador diferente, entretanto, o algoritmo será sempre o mesmo. Por exem-
plo, em uma analogia com a vida diária, uma receita de um prato pode estar em português, inglês ou francês,
mas, em qualquer que seja o idioma, os passos para a sua elaboração serão realizados independentemente do
idioma do cozinheiro.
Na ciência da computação e na programação, os algoritmos são mais importantes que as linguagens de pro-
gramação ou que os computadores. Uma linguagem de programação é apenas um meio para expressar um algo-
ritmo e um computador só um processador para executá-lo. Tanto a linguagem de programação quanto o compu-
tador são os meios para obter um fim: conseguir executar o algoritmo e efetuar o processo correspondente.
Dada a importância do algoritmo na ciência da computação, um aspecto muito importante será o projeto de
algoritmos. Grande parte deste livro é dedicada ao ensino e à prática dessa tarefa.
O projeto da maioria dos algoritmos requer criatividade e conhecimentos profundos da técnica de programa-
ção. Em essência, a solução de um problema pode ser expressa mediante um algoritmo.

2.4.1 Características dos algoritmos


As características fundamentais que todo algoritmo deve satisfazer são:
• Um algoritmo deve ser preciso e deve indicar a ordem de realização de cada passo.
• Um algoritmo deve estar definido. Quando seguido duas vezes, o resultado obtido deve ser o mesmo a
cada vez.
• Um algoritmo deve ser finito. Quando seguido, um algoritmo deve terminar em algum momento, ou seja,
deve ter um número finito de passos.
A definição de um algoritmo deve descrever três partes: Entrada, Processamento e Saída. No algoritmo da
receita citada, teremos:
Entrada: ingredientes e utensílios empregados.
Processamento: elaboração da receita na cozinha.
Saída: término do prato (por exemplo, cordeiro).
Metodologia da programação e desenvolvimento de software   15

Exemplo 2.3
Um cliente faz um pedido para uma fábrica. A fábrica examina em seu banco de dados a ficha do cliente, se
o cliente for solvente, então a empresa aceita o pedido, caso contrário, o pedido é recusado. Redigir o algo-
ritmo correspondente.
Os passos do algoritmo são:
1. Início.
2. Ler o pedido.
3. Examinar a ficha do cliente.
4. Se o cliente for solvente, aceitar o pedido; caso contrário, recusar o pedido.
5. Fim.

Exemplo 2.4
Quer-se projetar um algoritmo para saber se um número é primo ou não.

Um número primo é aquele que só pode ser dividido por si mesmo e pela unidade (tem somente dois divisores:
ele mesmo e a unidade). Por exemplo, 9, 8, 6, 4, 12, 16, 20 etc. não são primos, já que são divisíveis por números
diferentes deles mesmos e da unidade. Assim, 9 é divisível por 3, 8 é divisível por 2 etc. O algoritmo de resolução
do problema passa por dividir sucessivamente o número por 2, 3, 4... etc.

1. Início.
2. Colocar X igual a 2 (X = 2, X variável que representa os divisores do número
N que se busca).
3. Dividir N por X (N/X).
4. Se o resultado de N/X for inteiro, então N não é um número primo e retornar
ao ponto 7; caso contrário continuar o processo.
5. Soma 1 a X (X ← X + 1).
6. Se X for igual a N, então N é um número primo, caso contrário, retornar ao
ponto 3.
7. Fim.

Por exemplo, se N fosse 131, os passos anteriores seriam:


1. Início.
2. X = 2.
3. 131/X. Como o resultado não é inteiro, o processo continua.
5. X ← 2 + 1, logo, X = 3.
6. Como X não é 131, o processo continua.
3. 131/X resultado não é inteiro.
5. X ← 3 + 1, X = 4.
6. Como X não é 131, o processo continua.
3. 131/X... etc.
7. Fim.

Exemplo 2.5
Realizar a soma de todos os números pares entre 2 e 1.000.

O problema consiste em somar 2 + 4 + 6 + 8 ... + 1.000. Utilizaremos as palavras SOMA e NÚMERO (as
variáveis serão denominadas mais tarde) para representar as somas sucessivas (2+4), (2+4+6), (2+4+6+8) etc.
A solução pode ser escrita com o seguinte algoritmo:
16   Programação em C: Metodologia, algoritmos e estruturas de dados

1. Início.
2. Estabelecer SOMA a 0.
3. Estabelecer NÚMERO a 2.
4. Somar NÚMERO a SOMA. O resultado será o novo valor da soma (SOMA).
5. Incrementar NÚMERO em 2 unidades.
6. Se NÚMERO =< 1.000 retornar ao passo 4, caso contrário, escrever o último
valor de SOMA e terminar o processo.
7. Fim.

2.4.2 Projeto do algoritmo


Um computador somente é capaz de solucionar problemas quando os sucessivos passos que deve desenvolver
para atingir esse objetivo lhes são proporcionados. Esses passos sucessivos que indicam as instruções a serem
executadas pela máquina constituem, como já sabemos, o algoritmo.
A informação proporcionada ao algoritmo constitui sua entrada e a informação produzida pelo algoritmo,
sua saída.
Os problemas complexos podem ser mais eficazmente resolvidos com o computador quando são divididos
em subproblemas que sejam mais fáceis de solucionar que o problema original. Esse método costuma ser cha-
mado divide e vencerás (divide and conquer) e consiste em dividir um problema complexo em outros mais sim-
ples. Assim, o problema de encontrar a superfície e o comprimento de um círculo pode ser dividido em três
problemas mais simples ou subproblemas (Figura 2.9).

Superfície
e comprimento de
circunferência

Entrada Cálculo de Cálculo de


Saída
de superfície comprimento
resultados
dados (S) (C)

Entrada Saída Saída Saída


S = PI * R 2 C = 2 * PI * R
raio (R) R S C

Figura 2.9 Refinamento de um algoritmo.

A decomposição do problema original em subproblemas mais simples e, em seguida, a divisão destes subpro-
blemas em outros ainda mais simples que possam ser implementados para sua solução no computador é chamada
projeto descendente (top-down design). Em geral os passos projetados no primeiro esboço do algoritmo são in-
completos e indicam apenas alguns poucos passos (um máximo de doze aproximadamente). Depois dessa primei-
ra descrição, eles aumentam em uma descrição mais detalhada, com mais passos específicos. Esse processo é
denominado refinamento do algoritmo (stepwise refinement). Para problemas complexos, com freqüência são ne-
cessários diferentes níveis de refinamento, antes que se possa obter um algoritmo claro, preciso e completo.
O problema de cálculo da circunferência e superfície de um círculo pode ser decomposto em subproblemas
mais simples: (1) ler dados de entrada, (2) calcular superfície e comprimento da circunferência e (3) escrever re-
sultados (dados de saída).
Metodologia da programação e desenvolvimento de software   17

Subproblema Refinamento
ler raio ler raio
calcular superfície superfície = 3.141592 * raio ^ 2
calcular circunferência circunferência = 2 * 3.141592 * raio
escrever resultados escrever raio, circunferência, superfície

As vantagens mais importantes do projeto descendente são:


• O problema é compreendido com mais facilidade ao ser dividido em partes mais simples denominadas módulos.
• As modificações nos módulos são mais fáceis.
• A comprovação do problema pode ser mais facilmente verificada.
Depois dos passos anteriores (projeto descendente e refinamento por passos), é preciso representar o algoritmo
por meio de determinada ferramenta de programação: diagrama de fluxo, pseudocódigo ou diagrama N-S.
Dessa maneira, o projeto do algoritmo é decomposto nas fases mostradas na Figura 2.10.

Projeto
de um
algoritmo

Projeto Refinamento Ferramentas de


descendente por casos programação (3)
(1) (2) — diagrama de fluxo
— pseudocódigo
— diagrama N-S

Figura 2.10 Fases do projeto de um algoritmo.

2.5 ESCRITA DE ALGORITMOS


Como já comentamos, o sistema para descrever («escrever») um algoritmo consiste em desenvolver uma des-
crição passo a passo com uma linguagem natural do citado algoritmo. Recordemo-nos de que um algoritmo é
um método ou conjunto de regras para solucionar um problema. Em cálculos elementares essas regras têm as
seguintes propriedades:
• devem ser seguidas de alguma seqüência definida de passos até que se obtenha um resultado coerente;
• somente uma operação pode ser executada de cada vez.
O fluxo de controle usual de um algoritmo é seqüencial. Consideremos o algoritmo que responde à pergunta
O que fazer para ver o filme Harry Potter?
A resposta é muito simples e pode ser descrita em forma de algoritmo geral de modo similar a:
ir ao cinema
comprar uma entrada
ver o filme
voltar para casa

O algoritmo é formado por quatro ações básicas, e cada uma delas deve ser executada antes de realizar a
seguinte. Em termos de computador, cada ação será codificada em uma ou várias sentenças que executam uma
tarefa particular.
18   Programação em C: Metodologia, algoritmos e estruturas de dados

O algoritmo descrito é muito simples, entretanto, como já foi indicado em parágrafos anteriores, o algoritmo
geral será decomposto em passos mais simples em um procedimento denominado refinamento sucessivo já que
cada ação pode ser decomposta em outras ações mais simples. Dessa maneira, por exemplo, um primeiro refina-
mento do algoritmo ir ao cinema pode ser escrito da seguinte forma:

1. início
2. ver a página de cinemas no jornal
3. se não estiver passando “Harry Potter” então
3.1. decidir por outra atividade
3.2. retornar ao passo 7
se_não
3.3. ir ao cinema
fim_se
4. se há fila então
4.1. entrar na fila
4.2. enquanto houver pessoas adiante fazer
4.2.1. avançar na fila
fim_enquanto
fim_se
5. se houver lugar então
5.1. comprar uma entrada
5.2. entrar na sala
5.3. localizar a(s) poltrona(s)
5.4. enquanto projetam o filme fazer
5.4.1. ver o filme
fim_enquanto
5.5. sair do cinema
se_não
5.6. resmungar
fim_se
6. voltar para casa
7. fim

Existem diferentes aspectos a considerar no algoritmo anterior. Em primeiro lugar, certas palavras reservadas
foram deliberadamente escritas em negrito (enquanto, se_não etc.). Essas palavras descrevem as estruturas de
controle fundamentais e os processos de tomada de decisão no algoritmo. Incluem também os conceitos importan-
tes de seleção (expressos por se-então-se_não, if-then-else) e de repetição (expressos com enquanto-fazer
ou, às vezes, repetir-até e iterar-fim_iterar — em inglês, while-do e repeat-until) que se encontram em
quase todos os algoritmos, especialmente os de processo de dados. A capacidade de decisão permite selecionar
alternativas de ações a seguir ou a repetição uma e outra vez de operações básicas.
se passam o filme escolhido ir ao cinema
se_não ver televisão, ir ao futebol ou ler jornal
enquanto houver pessoas na fila, ir avançando repetidamente
até chegar ao guichê
Outro aspecto a considerar é o método escolhido para descrever os algoritmos: emprego de indentação ou jus-
tificativa na escrita de algoritmos. Atualmente, a escrita de um programa é tão importante quanto a sua posterior
leitura. A escrita de um programa é facilitada com a justificativa das ações interiores às estruturas fundamentais
citadas: seletivas e repetitivas. No decorrer de todo este livro, a justificativa dos algoritmos será norma constante.
Para terminar essas considerações iniciais sobre algoritmos, descreveremos as ações necessárias para refinar
o algoritmo objeto do nosso estudo. Para isso, analisemos a ação:
Localizar a(s) poltrona(s).
Se os números dos assentos estiverem impressos na entrada, a ação composta será resolvida com o se-
guinte algoritmo:
Metodologia da programação e desenvolvimento de software   19

1. início //algoritmo para encontrar a poltrona do espectador


2. caminhar até chegar à primeira fila de poltronas
3. repetir
compara número de fila com número impresso no bilhete
se não são iguais, então passar para a fila seguinte
até_que se localize a fila correta
4. enquanto número da poltrona não coincidir com número do bilhete
fazer avançar pela fila para a poltrona seguinte
fim-enquanto
5. sentar-se na poltrona
6. fim

Nesse algoritmo, a repetição foi mostrada de duas maneiras, utilizando ambas as notações, repetir...
até_que e enquanto... fim_enquanto. Foi considerado, também, como ocorre normalmente, que o número
do assento e a fila coincidem com o número e fila impressos no bilhete.

2.6 REPRESENTAÇÃO GRÁFICA DOS ALGORITMOS


Para representar um algoritmo, é necessário utilizar um método que permita tornar o algoritmo independente da
linguagem de programação escolhida. Isso permitirá que um algoritmo possa ser codificado indistintamente em
qualquer linguagem. Para atingir esse objetivo, é necessário que o algoritmo seja representado gráfica ou numeri-
camente, de modo que as sucessivas ações não dependam da sintaxe de nenhuma linguagem de programação,
apenas que a descrição possa servir facilmente para sua transformação em um programa, isto é, sua codificação.
Os métodos usuais para representar um algoritmo são:
1. diagrama de fluxo;
2. diagrama N-S (Nassi-Schneiderman);
3. linguagem de especificação de algoritmos: pseudocódigo;
4. idioma português, inglês…;
5. fórmulas.
Os métodos 4 e 5 não costumam ser fáceis de transformar em programas. Uma descrição em português nar-
rativo não é satisfatória, já que é demasiadamente prolixa e, em geral, ambígua. Uma fórmula, entretanto, é um
bom sistema de representação. Por exemplo, as fórmulas para a solução de uma equação quadrática (de segundo
grau) são um meio sucinto de expressar o procedimento algorítmico que deve ser executado para obter as raízes
da mencionada equação.

xl = (– b + b2 – 4ac) / 2a x2 = (– b – b2 – 4ac) / 2a


e significa o seguinte:
1. Elevar b ao quadrado.
2. Tomar a; multiplicar por c; multiplicar por 4.
3. Dividir o resultado obtido de 2 do resultado de 1 etc.
Entretanto, não é uma prática freqüente expressar um algoritmo por meio de uma simples fórmula.

2.6.1 Pseudocódigo
O pseudocódigo é uma linguagem de especificação (descrição) de algoritmos. O uso de tal linguagem torna o
passo de codificação final (isto é, a tradução a uma linguagem de programação) relativamente fácil. As linguagens
APL Pascal e Ada são utilizadas, às vezes, como linguagens de especificação de algoritmos.
O pseudocódigo nasceu como uma linguagem semelhante ao inglês e era um meio de representar basicamente
as estruturas de controle de programação estruturada, as quais serão vistas em capítulos posteriores. Considera-se
como um primeiro rascunho, dado que o pseudocódigo tem de ser posteriormente traduzido para uma linguagem
20   Programação em C: Metodologia, algoritmos e estruturas de dados

de programação. O pseudocódigo não pode ser executado por um computador. A vantagem do pseudocódigo é que,
com a sua utilização, no planejamento de um programa, o programador pode se concentrar na lógica e nas estrutu-
ras de controle, não precisando preocupar-se com as regras de uma linguagem específica. Além disso, quando erros
ou anomalias são descobertos na lógica do programa seu pseudocódigo, é fácil de ser modificado, enquanto em
muitas ocasiões costuma ser difícil a alteração na lógica uma vez que está codificada em uma linguagem de progra-
mação. Outra vantagem do pseudocódigo é que pode ser facilmente traduzido para linguagens estruturados como
Pascal, C, FORTRAN 77/90, C++, Java, C# etc.
Para representar as ações, o pseudocódigo original utiliza sucessivas palavras reservadas em inglês — similares
a suas homônimas nas linguagens de programação —, tais como start, end, stop, if-then-else, while-end,
repeat-until etc. A escrita do pseudocódigo exige normalmente a indentação (sangramento na margem esquer-
da) de diferentes linhas.
A representação do diagrama de fluxo em pseudocódigo na Figura 2.11 é a seguinte:
start
//cálculo de imposto e salários
read nome, horas, preço_hora
salário_bruto ← horas * preço_hora
taxas ← 0,25 * salário_bruto
salário_líquido ← salário_bruto - taxas
write nome, salário_bruto, taxas, salário_líquido
end
O algoritmo começa com a palavra start e termina com a palavra end, em inglês (em português, início,
fim). Entre essas palavras, somente se escreve uma instrução ou uma ação por linha.
A linha precedida por // é denominada comentário. É apenas uma informação para o leitor do e não realiza
nenhuma instrução executável, apenas tem efeito como documentação interna do programa. Alguns autores cos-
tumam colchetes ou chaves.
O uso de apóstrofes ou aspas, como são representados os comentários no BASIC, da Microsoft, não é reco-
mendável, uma vez que esse caractere é utilizado para abertura ou fechamento de cadeias de caracteres em lingua-
gens Pascal ou FORTRAN e causariam confusão.
Outro exemplo para esclarecer o uso do pseudocódigo poderia ser um simples algoritmo da partida matinal de
um carro.
início
//partida matinal de um carro
introduzir a chave no contato
puxar o afogador
girar a chave no contacto
pisar no acelerador
ouvir o ruído do motor
pisar novamente no acelerador
esperar uns instantes para que esquente o motor
voltar o afogador para sua posição
fim
Felizmente, ainda que o pseudocódigo tenha nascido como um substituto da linguagem de programação e, por
conseguinte, suas palavras reservadas se conservaram ou ficaram muito semelhantes às palavras do idioma inglês,
o uso do pseudocódigo se estendeu com termos em português como início, fim, parada, ler, escrever, se-
então-se_no, enquanto, fim_enquanto, repetir, até_que etc. Sem dúvida, o uso da terminologia do
pseudocódigo em português facilitou e facilitará consideravelmente a aprendizagem e o uso diário da programação.
Nesta obra, da mesma maneira que em outras de nossas obras, utilizaremos o pseudocódigo em português e dare-
mos nos momentos adequados as estruturas equivalentes em inglês com o objetivo de facilitar a tradução do pseu-
docódigo à linguagem de programação selecionada.
Dessa forma, nos pseudocódigos citados anteriormente, as palavras start, end, read, write deveriam ser
substituídas, respectivamente, por início, fim, ler, escrever.
Metodologia da programação e desenvolvimento de software   21

início start ler read


.
.
.
.
.
fim end escrever write

2.6.2 Diagramas de fluxo


Um diagrama de fluxo ( flowchart) é uma das técnicas mais antigas de representação de algoritmos e também uma
das mais utilizadas, ainda que seu emprego tenha diminuído consideravelmente, sobretudo, desde o aparecimento
das linguagens de programação estruturadas. Um diagrama de fluxo é um diagrama que utiliza os símbolos (caixas)-
padrão mostrados na Tabela 2.1 e que tem os passos do algoritmo escritos nessas caixas unidas por setas, denomi-
nadas linhas de fluxo, que indicam a seqüência em que deve ser executado.
A Figura 2.11 é um diagrama de fluxo básico. Esse diagrama representa a resolução de um programa que cal-
cula o salário líquido de um trabalhador partindo da leitura do nome, horas trabalhadas, preço da hora e sabendo
que os impostos aplicados são de 25% sobre o salário bruto.
Os símbolos-padrão normatizados pelo Ansi (American National Standard Institute) são muito variados. Na
Figura 2.12, representa-se um modelo de um projeto típico onde se contemplam a maioria dos símbolos usados no
diagrama, entretanto, os símbolos mais utilizados representam:
• processamento • decisão • conectores
• fim • entrada/saída • direção do fluxo

Tabela 2.1 Símbolos de diagrama de fluxo

Principais
Função
símbolos

Terminal (representa o começo, «início», e o final, «fim» de um programa. Pode representar também uma
parada ou interrupção programada que seja necessário desenvolver em um programa.

Entrada/Saída (qualquer tipo de introdução de dados na memória nos periféricos, «entrada», ou registro da
informação processada em um periférico, «saída».

Processo (qualquer tipo de operação que possa originar mudança de valor, formato ou posição da infor-
mação armazenada na memória, operações aritméticas, de transferência etc.).

Decisão (indica operações lógicas ou de comparação entre dados — normalmente, dois — e, em função
dela, o resultado determina qual dos distintos caminhos alternativos do programa deve seguir.
NÃO Em geral, há duas saídas — respostas SIM ou NÃO —, mas pode haver três ou mais,
SIM de acordo com os casos).

Decisão múltipla (em função do resultado da comparação e de acordo com esse resultado se seguirá um
dos diferentes caminhos).

Conector (serve para enlaçar duas partes quaisquer de um organograma por meio de um conector na saí-
da e outro conector na entrada. Trata-se da conexão na mesma página do diagrama.

Indicador de direção ou linha de fluxo (indica o sentido de execução das operações).

Linha conectora (serve de união entre dois símbolos).

(Continua)
22   Programação em C: Metodologia, algoritmos e estruturas de dados

(Continuação)
Conector (conexão entre dois pontos do organograma situado em páginas diferentes).

Símbolos
Função
secundários

Chamada a sub-rotina ou a um processo predeterminado (uma sub-rotina é um módulo independente do


programa principal, que recebe uma entrada procedente do mencionado programa, realiza uma tarefa de-
terminada e ao terminar retorna ao programa principal).

Tela (em algumas ocasiões é utilizado no lugar do símbolo de E/S).

Impressora (em algumas ocasiões é utilizado no lugar do símbolo de E/S).

Teclado (em algumas ocasiões é utilizado no lugar do símbolo de E/S).

Comentários (utilizado para acrescentar comentários classificadores a outros símbolos do diagrama de


fluxo. Podem ser desenhados de qualquer lado do símbolo).

início

ler nome,
horas,
preço

bruto ←
horas * preço

alíquota ←
0,25 * bruto

líquido ←
bruto – alíquotas

escrever nome,
bruto, alíquotas,
nome

fim

Figura 2.11 Diagrama de fluxo.


Metodologia da programação e desenvolvimento de software   23

O diagrama de fluxo da Figura 2.12 resume suas características:


• existe uma caixa etiquetada “início”, que é do tipo elíptico;
• existe uma caixa etiquetada “fim” da mesma maneira que a anterior;
• se existem outras caixas, normalmente são retangulares, tipo losango ou paralelogramo (o restante das figu-
ras é utilizado somente em diagramas de fluxo gerais ou de detalhe e nem sempre são imprescindíveis).
Em uma caixa retangular pode ser escrito mais de um passo do algoritmo. O uso de setas significa que a caixa
não necessita ser escrita embaixo de sua antecessora, entretanto, o abuso em demasia dessa flexibilidade leva a
diagramas de fluxo complicados e ininteligíveis.

Terminal Decisão Subprograma


não

sim

Entrada/ Processo
Saída

Figura 2.12 Modelo típico para diagramas de fluxo.

Exemplo 2.6
Calcular a média de uma série de números positivos, supondo que os dados sejam lidos de um terminal. Um valor
de zero — como entrada — indicará que o final da série de números positivos foi alcançado.
O primeiro passo a ser dado no desenvolvimento do algoritmo é decompor o problema em uma série de passos
seqüenciais. Para calcular uma média, é necessário somar e contar os valores. Conseqüentemente, nosso algoritmo
na forma descritiva seria:
1. Inicializar contador de números C e variável soma S.
2. Ler um número.
3. Se o número lido for zero :
• calcular a média ;
• imprimir a média ;
• fim do processo.
Se o número lido não for zero :
• calcular a soma ;
• incrementar em um o contador de números ;
• ir para o passo 2.
4. Fim.
O refinamento do algoritmo conduz aos passos sucessivos necessários para realizar as operações de leitura,
verificação do último dado, soma e média dos dados.
Se o primeiro dado lido for 0, a divisão S/C produzirá um erro ao se executar o algoritmo em um computador
já que não é permitida a divisão por zero.
24   Programação em C: Metodologia, algoritmos e estruturas de dados

Diagrama de fluxo

Início Se o primeiro dado lido


for 0, a divisão S/C
produzirá um erro
Média S/C
em um computador,
C 0 C – contador
já que não é permitida
S 0 de números
nele a divisão por zero
S – somador
de números Imprimir
média

ler dado

Fim

não
dado <> 0

sim

C C+1
S S + dado

Codificação em C

long dado;
int C;
double Média, S;

C = 0;
S = 0;
puts(“Dados numéricos; para
finalizar se introduz 0.”);

do {
scanf(“%ld”, &dado);
if (dado != 0)
{
C = C + 1;
S = S + dado;
}
} while (dado != 0);
/* Calcula a média e se
escreve */
if (C > 0)
{
Média = S/C;
printf(“\nMédia = %.2lf”,
Média);
}
Metodologia da programação e desenvolvimento de software   25

Exemplo 2.7
Soma dos números pares compreendidos entre 2 e 100.

Diagrama de fluxo Codificação em C

Início
int número, soma;

soma = 2;
SOMA 2
NÚMERO 4
número = 4;

while (número <= 100)


{
soma = soma + número;
SOMA
número = número + 2;
SOMA + NÚMERO
}

printf(“\nSoma pares entre 2 e 100 = %d”, soma);


NÚMERO
NÚMERO + 2

sim
NÚMERO = < 100

não

escrever
SOMA

Fim

Exemplo 2.8
Desenvolver o algoritmo que resolva o seguinte problema: cálculo dos salários mensais dos funcionários de uma
empresa, sabendo que esses salários são calculados com base nas horas semanais trabalhadas e de acordo com
um preço especificado por horas. Se as quarenta horas semanais são ultrapassadas, as horas
extraordinárias serão pagas na razão de 1,5 vez a hora ordinária.
Os cálculos são:
1. Ler dados do arquivo da empresa até que se encontre a ficha final do arquivo
(HORAS, PREÇO_HORA, NOME).
2. Se HORAS <= 40, então SALÁRIO é o produto de horas por PREÇO_HORA.
3. Se HORAS > 40, então SALÁRIO é a soma de 40 vezes PREÇO_HORA mais 1,5 vez
PREÇO_HORA por (HORAS-40).
26   Programação em C: Metodologia, algoritmos e estruturas de dados

O diagrama de fluxo completo do algoritmo e a codificação em C são indicados a seguir:

Diagrama de fluxo Codificação em C

Início

float horas, preçoHora, salário;


char nome[81];
char maisDados;

Ler puts(“\tCalcula salário”);


HORAS, PREÇO_HORA
puts(“Introduzir horas, preço hora e
NOME
nome.\n”);

do {
printf(“Nome: “);
gets(nome);
sim não
HORAS < = 40 printf(“Horas trabalhadas: “);
scanf(“%f”, &horas);
printf(“Preço hora: “);
scanf(“%f%*c”, &preçoHora);
if (horas <= 40.0)
SALÁRIO = SALÁRIO = salário = horas * preçoHora;
HORAS* 40* PREÇO_HORA + else
PREÇO_HORA 1,5* PREÇO_HORA salário = 40 * preçoHora +
(HORAS–40) 1.5 * (horas — 40.0) *
preçoHora;

printf(“Salário de %s %.1f\n”,
nome, salário);
printf(“\nMais trabalhadores (S/N): “);
scanf(“%c%*c”, &maisDados);

Escrever }while (maisDados == ‘S’ ||


SALÁRIO maisDados == ‘s’);

sim
mais dados

não

Fim
Metodologia da programação e desenvolvimento de software   27

Uma variável do diagrama de fluxo anterior que também é válida é:

Diagrama de fluxo Codificação em C

Início

float horas, preçoHora, salário;


char nome[81];
não
char maisDados;
mais dados?
maisDados = ‘S’;
sim puts(“\tCalcula salário”);
puts(“Introduzir horas, preço hora e
Ler nome.\n”);
HORAS, PREÇO_HORA
NOME while (maisDados == ‘S’ || maisDados == ‘s’)
{
printf(“Nome: “);
gets(nome);
sim não
HORAS < = 40 printf(“Horas trabalhadas: “);
scanf(“%f”, &horas);
printf(“Preço hora: “);
scanf(“%f%*c”, &preçoHora);
SALÁRIO = SALÁRIO =
HORAS* 40* PREÇO_HORA + if (horas <= 40.0)
PREÇO_HORA 1,5* PREÇO_HORA salário = horas * preçoHora;
(HORAS–40) else
salário = 40 * preçoHora +
1,5 * (horas — 40.0) * preçoHora;

printf(“Salário de %s %.1f\n”,
Escrever
SALÁRIO nombre, salário);

printf(“\nMais trabalhadores (S/N): “);


scanf(“%c%*c”, &maisDados);
}
Fim

Exemplo 2.9
A escrita de algoritmos para realizar operações simples de contagem é uma das primeiras coisas que um progra-
mador pode aprender.
Suponhamos que se proporcione uma seqüência de números, tais como
5 3 0 2 4 4 0 0 2 3 6 0 2
e se deseje contar e imprimir o número de zeros da seqüência.
O algoritmo é muito simples, já que basta somente ler os números da esquerda para a direita, enquanto se
contam os zeros. Utiliza como variável a palavra NÚMERO para os números que são examinados e TOTAL para o
número de zeros encontrados. Os passos a seguir são:
1. Estabelecer TOTAL em zero.
2. Restam mais números para examinar?
3. Se não restam números, imprimir o valor de TOTAL e fim.
28   Programação em C: Metodologia, algoritmos e estruturas de dados

4. Se existem mais números, executar os passos 5 a 8.


5. Ler o número seguinte e dar seu valor para a variável NÚMERO.
6. Se NÚMERO = 0, incrementar TOTAL em 1.
7. Se NÚMERO <> 0, não modificar TOTAL.
8. Retornar ao passo 2.
O diagrama de fluxo e a codificação em C correspondente é:

Diagrama de fluxo Codificação em C

Início

int número, total;


char maisDados;
Total 0
puts(“Conta de zeros lidos do teclado”);
maisDados = ‘S’;
total = 0;

não while (maisDados == ‘S’ || maisDados == ‘s’)


mais números? {
scanf(“%d”, &número);
sim if (número == 0)
total = total + 1;
printf(“\nMais números (S/N): “);
Ler
NÚMERO scanf(“%c%*c”, &maisDados);
}

não
NÚMERO = 0

sim

TOTAL
TOTAL + 1

escrever printf(“\nTotal de zeros %d “, total);


TOTAL

Fim

Exemplo 2.10
Dados três números, determinar se a soma de qualquer par deles é igual ao terceiro número. Se a condição for
cumprida, escrever «Iguais», caso contrário, escrever «Diferentes».
No caso de os números serem: 3 9 6
Metodologia da programação e desenvolvimento de software   29

a resposta é “Iguais” , visto que 3 + 6 = 9. Entretanto, se os números fossem:


2 3 4
o resultado seria “Diferentes”.
Para resolver esse problema, pode-se comparar a soma de cada par com o terceiro número. Com três números,
somente existem três pares diferentes e o algoritmo de resolução do problema será fácil.
1. Ler os três valores, A, B e C.
2. Se A + B = C escrever “Iguais” e parar.
3. Se A + C = B escrever “Iguais” e parar.
4. Se B + C = A escrever “Iguais” e parar.
5. Escrever “Diferentes” e parar.
O diagrama de fluxo e a codificação em C correspondente estão na Figura 2.13.

Diagrama de fluxo Codificação em C

Início

int a, b, c;

Ler printf(“Teste com três números: “);


A, B, C scanf(“%d %d %d”, &a, &b, &c);

if ((a + b) == c)
printf(“\nIguais, %d + %d = %d”, a, b, c);
sim else if ((a + c) == b)
A+B=C
printf(“\nIguais, %d + %d = %d”, a, c, b);
else if ((b + c) == a)
não printf(“\nIguais, %d + %d = %d”, b, c, a);

sim
A+C=B

não

sim
B+C=A

não

escrever escrever
“diferentes” “iguais”

Fim

Figura 2.13 Diagrama de fluxo e codificação em C (Exemplo 2.10).

2.6.3 Diagramas de Nassi-Schneiderman (N-S)


O diagrama N-S de Nassi Schneiderman — também conhecido como diagrama de Chapin — é como um diagra-
ma de fluxo no qual se omitem as setas de união e as caixas são contínuas. As ações sucessivas são escritas em
caixas sucessivas e, como nos diagramas de fluxo, diferentes ações podem ser escritas em uma caixa.
30   Programação em C: Metodologia, algoritmos e estruturas de dados

Um algoritmo é representado por um retângulo no qual cada face é uma ação a realizar:

ler
nome, horas, preço

calcular
salário ← horas * preço

impostos ← 0,25 * salário

calcular
líquido ← salário – impostos

escrever
nome, salário, impostos, líquido

nome do algoritmo

<ação 1>

<ação 2>

<ação 3>

...

fim

Figura 2.14 Representação gráfica N-S de um algoritmo.

Outro exemplo é a representação da estrutura condicional (Figura 2.15).

a) b)
sim não condição?
condição?
sim não

ação 1 ação 2

<ações> <ações>

Figura 2.15 Estrutura condicional ou seletiva: (a) diagrama de fluxo: (b) diagrama N-S.
Metodologia da programação e desenvolvimento de software   31

Exemplo 2.11
Calcular o salário líquido semanal de um trabalhador em função do número de horas trabalhadas e da alíquota
de impostos:
• as primeiras 35 horas são pagas a tarifa normal;
• as horas que ultrapassem 35 são pagas a 1,5 vez a tarifa normal
• as alíquotas de impostos são:
a)  as primeiras 60.000 pesetas são livres de impostos;
b)  as 40.000 pesetas seguinte têm 25% de impostos;
c)  as demais têm 45% de impostos;
• a tarifa horária é de 800 pesetas.
Também é necessário escrever o nome, salário bruto, alíquotas e salário líquido (esse exemplo será deixado
como exercício para o aluno).

2.7 O CICLO DE VIDA DO SOFTWARE


Existem dois níveis na construção de programas: aqueles relativos a pequenos programas (os que normalmente são
realizados por programadores individuais) e aqueles que se referem a sistemas de desenvolvimento de programas
grandes (projetos de software) e que, geralmente, requerem uma equipe de programadores em vez de pessoas in-
dividuais. O primeiro nível é denominado programação em pequena escala e o segundo nível, programação em
larga escala.
A programação em pequena escala se preocupa com os conceitos que ajudam a criar pequenos programas —
aqueles que variam em comprimento indo de umas poucas linhas a poucas páginas. Nesses programas costuma-se
requerer clareza, precisão mental e técnica. Na realidade, o maior interesse, do ponto de vista do futuro programa-
dor profissional, está nos programas de larga escala que requerem princípios sólidos e firmes do que se conhece
como engenharia de software e que constitui um conjunto de técnicas para facilitar o desenvolvimento de progra-
mas de computador. Esses programas ou projetos de software são realizados por equipes de pessoas coordenados
por um diretor de projetos (analista ou engenheiro de software) e os programas podem ter mais de 100.000 linhas
de código.
O desenvolvimento de um bom sistema de software é realizado durante o ciclo de vida que é o período que se
estende desde a concepção inicial do sistema até sua eventual retirada de comercialização ou seu uso. As ativida-
des humanas relacionadas com o ciclo de vida implicam processos tais como análise de requisitos, projeto, imple-
mentação, codificação, testes, verificação, documentação, manutenção e evolução do sistema e obsolescência.
Essencialmente, o ciclo de vida do software começa com uma idéia inicial, inclui a escrita e depuração de progra-
mas e continua durante anos com correções e melhorias ao software original.3
O ciclo de vida do software é um processo iterativo, de modo que as sucessivas etapas serão modificadas em
função da modificação das especificações dos requisitos produzidos na fase de projeto ou implementação, ou, ainda,

ANÁLISE

PROJETO

IMPLEMENTAÇÃO

DEPURAÇÃO

MANUTENÇÃO

Figura 2.16 Ciclo de vida do software.

3
  CARRANO et al. Data structures and problem solving with Turbo Pascal. The Benjaming/Cummings Publishing, 1993, p. 210.
32   Programação em C: Metodologia, algoritmos e estruturas de dados

quando depois do sistema implementado e depurado, apareçam erros que necessitem ser corrigidos e depurados e
que requeiram a repetição de etapas anteriores.
A Figura 2.16 mostra o ciclo de vida do software e a disposição típica de suas diferentes etapas no sistema
conhecido como ciclo de vida em cascata, que supõe que a saída de cada etapa seja a entrada da etapa seguinte.

2.7.1 Análise
A primeira etapa na produção de um sistema de software é decidir exatamente o que se supõe que o sistema deva
fazer. Essa etapa também é conhecida como análise de requisitos ou de especificações e, por essa razão, muitos
tratadistas costumam subdividi-la em outras duas etapas:
• Análise e definição do problema.
• Especificação de requisitos.
A parte mais difícil na criação de um sistema de software é definir qual é o problema e em seguida especifi-
car o que é necessário para resolvê-lo. Normalmente a definição do problema começa com a análise dos requisi-
tos feitos pelo usuário, mas, com freqüência, esses requisitos costumam ser imprecisos e difíceis de descrever.
Todos os aspectos do programa devem ser especificados, porém, habitualmente, as pessoas que descrevem o pro-
blema não são programadores e isso torna imprecisa a definição. A fase de especificação, em geral, requer uma
comunicação entre os programadores e os futuros usuários do sistema e a iteração da especificação até que tanto
o especificador quanto os usuários estejam satisfeitos com as especificações e que todos os problemas tenham sido
normalmente resolvidos.
Na etapa de especificações pode ser muito útil para melhorar a comunicação entre as diferentes partes impli-
cadas na construção de um protótipo ou modelo simples do sistema final, isto é, escrever um programa protótipo
que simule o comportamento das partes do produto software desejado. Por exemplo, um programa simples — in-
clusive, ineficiente — pode demonstrar ao usuário a interface proposta pelo analista. É melhor descobrir qualquer
dificuldade ou mudar sua idéia original agora que depois que a programação se encontre em estado avançado ou,
inclusive, já esteja terminada. A modelagem de dados é uma ferramenta muito importante na etapa de definição
do problema. Essa ferramenta é muito utilizada no projeto e construção de bases de dados.
Lembre que o usuário final, como regra, não conhece exatamente o que deseja que o sistema faça, portanto, o
analista de software ou programador, de acordo com o caso, deve interagir com o usuário para descobrir o que ele
quer que o sistema faça. Nessa etapa, é importante responder a perguntas tais como:
Quais são os dados de entrada?
Que dados são válidos e que dados são inválidos?
Quem utilizará o sistema: especialistas qualificados ou quaisquer usuários (sem formação)?
Que interfaces de usuário serão utilizadas?
Quais mensagens de erro e de detecção de erro são desejadas? Como o sistema deverá atuar quando o usuário
cometer um erro na entrada?
Que hipóteses são possíveis?
Existem casos especiais?
Qual deve ser o formato de saída?
Que documentação é necessária?
Que melhorias serão introduzidas — provavelmente — no programa (no futuro)?
Qual deve ser a rapidez do sistema?
A cada quanto tempo, depois de pronto, o sistema deverá ser alterado?
O resultado final da fase de análise é uma especificação dos requisitos do software.

• Descrição prévia e detalhada do problema.


• Protótipos de programas que possam ajudar a resolver o problema.
Metodologia da programação e desenvolvimento de software   33

2.7.2 Projeto
A especificação de um sistema indica o que o sistema deve fazer. A etapa de projeto do sistema indica como de-
verá fazer. Para um sistema pequeno a etapa de projeto pode ser tão simples como escrever um algoritmo em
pseudocódigo, já para um sistema grande essa etapa inclui também a fase de projeto de algoritmos (que inclui o
projeto e a interação de um número diferente de algoritmos), com freqüência apenas esboçados, bem como uma
estratégia para satisfazer todos os detalhes e produzir o código correspondente.
É preciso determinar se programas ou subprogramas já existentes podem ser utilizados ou se será necessário
construí-lo totalmente. O projeto deverá ser dividido em módulos utilizando os princípios de projeto descendente
e em seguida deve-se indicar a interação entre módulos. Um diagrama de estruturas proporciona um esquema cla-
ro destas relações.4
Neste ponto, é importante especificar claramente não só o propósito de cada módulo como também o fluxo de
dados entre os módulos. Por exemplo, deve-se responder às seguintes perguntas: quais dados estão disponíveis
para o módulo antes de sua execução? O que o módulo supõe? O que fazem os dados depois de executado o mó-
dulo? Conseqüentemente, as hipóteses, a entrada e a saída para cada módulo devem ser especificadas em detalhes.
Um meio para desenvolver essas especificações é escrever uma precondição, ou seja, uma descrição das condições
que devem ser satisfeitas no início do módulo e uma pós-condição que é uma descrição das condições que devem
ser encontradas no final de um módulo. Por exemplo, é possível descrever um subprograma que ordena uma lista
(um array) da seguinte forma:
subprograma ordenar (A, n)
{Ordena uma lista em ordem ascendente}
precondição: A é um array de n inteiros, 1<= n <= Máx.
pós-condição: A[1] <= A[2] <...<= A[n], n é inalterável}
Por último, pode-se utilizar pseudocódigo5 para especificar os detalhes do algoritmo. É importante que se
empregue bastante tempo na fase de projeto de seus programas. O resultado final do projeto descendente é uma
solução que seja fácil de transformar em estruturas de controle e estruturas de dados de uma linguagem de pro-
gramação específica — em nosso caso, C.

O tempo gasto na fase de projeto se traduzirá em economia de tempo na escrita e depuração do seu
programa.

2.7.3 Implementação (codificação)


A etapa de implementação (codificação) traduz os algoritmos do projeto em um programa escrito em uma lingua-
gem de programação. Os algoritmos e as estruturas de dados desenvolvidas em pseudocódigo serão traduzidos
codificados em uma linguagem que o computador entenda: Pascal, FORTRAN, COBOL, C, C++, C# ou JAVA.
Quando um problema é dividido em subproblemas, os algoritmos que resolvem cada subproblema (tarefa ou
módulo) devem ser codificados, depurados e testados de maneira independente.
É relativamente fácil encontrar um erro em um procedimento pequeno, mas é quase impossível encontrar todos
os erros de um programa grande que foi codificado e testado com uma única unidade em vez de um conjunto de
módulos (procedimentos) bem-definidos.
As regras de indentação e os comentários bem-feitos facilitam a escrita do código. O pseudocódigo é uma
excelente ferramenta que facilita notavelmente a codificação.

4
  Para conhecer mais sobre o tema de diagramas de estruturas, consulte estas obras: JOYANES, Luis. Fundamentos de programación. 3. ed.
McGraw-Hill, 1992; Id. Problemas de metodología de la programación. McGraw-Hill, 1992 ou também a obra de JOYANES et al. Pascal
y Turbo Pascal. Un enfoque práctico. McGraw-Hill, 1995.
5
  Para consultar o tema pseudocódigo, consulte as obras: JOYANES, Luis. Fundamentos de programación. Algoritmos y estructuras de da-
tos, 2. ed. McGraw-Hill, 2003; e JOYANES et al. Fundamentos de programación. Libro de problemas, McGraw-Hill, 1996.
34   Programação em C: Metodologia, algoritmos e estruturas de dados

2.7.4 Testes e integração


Quando os diferentes componentes de um programa foram implementados e testados individualmente, o sistema
completo se encaixa e se integra.
A etapa de testes serve para mostrar que um programa está correto. Os testes nunca são fáceis. Edgar Dijkstra
escreveu que os testes mostram se realmente existem erros, mas nunca podem mostrar a ausência de erros. Um tes-
te com «êxito» em sua execução significa somente que erros não foram encontrados nessas circunstâncias específi-
cas, mas não diz nada a respeito de outras circunstâncias. Teoricamente, o único modo de um teste mostrar que um
programa está correto é quando todos os casos possíveis foram tentados e comprovados (é o que se conhece como
teste exaustivo). Esta é uma situação tecnicamente impossível, inclusive para os programas mais simples. Suponha-
mos, por exemplo, que se tenha escrito um programa que calcule a nota média de um exame. Um teste exaustivo
vai requerer todas as combinações possíveis de marcas e tamanhos de classes e isso pode levar muitos anos até que
seja concluído.
A fase de testes é uma parte essencial de um projeto de programação. Durante a fase de testes, é necessário
eliminar tantos erros lógicos quanto seja possível. Em primeiro lugar, deve-se testar o programa com dados de
entrada válidos que conduzem a uma solução conhecida. Se certos dados devem estar dentro de uma categoria, os
valores devem ser incluídos nos extremos finais da categoria. Por exemplo, se o valor de entrada de n está na ca-
tegoria de 1 a 10, há que se incluir casos de teste nos quais n esteja entre 1 e 10. Dados inválidos também devem
ser incluídos para comprovar a capacidade de detecção de erros do programa. Alguns dados aleatórios ainda de-
verão ser testados e, por último, deve-se tentar alguns dados reais.

2.7.5 Verificação
A etapa de testes na fase de projeto deve começar tão logo seja possível e deve continuar durante a a implemen-
tação do sistema. Ainda que os testes sejam ferramentas extremamente válidas para proporcionar a evidência de
que um programa está correto e satisfaz suas especificações, é difícil saber se os testes realizadas são suficientes.
Por exemplo, como é possível saber se os conjuntos de dados de teste são suficientes ou ainda se executaram todos
os caminhos possíveis por meio do programa?
Por essas razões um segundo método foi desenvolvido para demonstrar a correção e a exatidão de um progra-
ma. Esse método, denominado verificação formal, implica a construção de testes matemáticas que ajudam a de-
terminar se os programas fazem o que se propõem a fazer. A verificação formal implica a aplicação de regras
formais para mostrar que um programa satisfaz sua especificação. A verificação formal funciona bem em progra-
mas pequenos, mas torna-se complexa quando utilizada em programas grandes. A teoria da verificação pressupõe
conhecimentos matemáticos avançados o que está fora dos objetivos deste livro, por essa razão, somente consta-
tamos a importância dessa etapa.
Provar que um algoritmo está correto é como testar um teorema matemático. Por exemplo, provar que um
módulo é exato (está correto) começa com as precondições (axiomas e hipóteses em matemática) e mostra que as
etapas do algoritmo conduzem às pós-condições. A verificação trata de testar com meios matemáticos que os al-
goritmos estão corretos.
Quando um erro é descoberto durante o processo de verificação seu algoritmo deve ser corrigido e possivelmen-
te as especificações do problema devem ser modificadas. Um método para isso é utilizar invariáveis (condições sem-
pre verdadeiras em um ponto específico de um algoritmo), o que provavelmente fará que seu algoritmo contenha
poucos erros antes do início da codificação. Como resultado, será gasto menos tempo na depuração do programa.

2.7.6 Manutenção
Quando o produto software (o programa) estiver terminado, este será distribuído entre os possíveis usuários, ins-
talado nos computadores e utilizado (produção). Entretanto, e ainda que a priori o programa funcione corretamen-
te, o software deve ser mantido e atualizado. De fato, custo típico de manutenção excede o custo de produção do
sistema original.
Metodologia da programação e desenvolvimento de software   35

Um sistema de software produzirá erros que serão detectados pelos usuários do sistema que não foram desco-
bertos na fase de teste. A correção desses erros faz parte da manutenção do software. Outro aspecto importante da
fase de manutenção é a melhoria do software, acrescentando mais características ou modificando partes existentes
que se adaptem da melhor maneira possível aos usuários.
Outras causas que obrigarão à revisão do sistema de software na etapa de manutenção são as seguintes:
1) quando um novo hardware é introduzido, o sistema pode ser modificado para executá-lo em um novo am-
biente; 2) quando mudam as necessidades dos usuários, costuma ser mais rápido e mais barato modificar o
sistema existente que produzir um sistema totalmente novo. A maior parte do tempo dos programadores é
gasta em manutenção dos sistemas existentes e não com projeto de sistemas totalmente novos. Por essa razão,
entre outras, há sempre que se projetar programas de modo que estes sejam fáceis de compreender e entender
(legíveis) e também fáceis de mudar.

2.7.7 A obsolescência: programas obsoletos


A última etapa do ciclo de vida do software é a evolução do mesmo, passando por sua vida útil e indo até sua ob-
solescência ou fase na qual o software se torna antiquado e é preciso atualizá-lo ou escrever um novo programa
que substitua o antigo.
A decisão de abandonar um software por ser obsoleto não é uma decisão fácil. Um sistema grande representa
um enorme investimento de capital e parece que a primeira vista é mais barato modificar o sistema existente que
construir um sistema totalmente novo. Esse critério costuma ser correto e, por esse motivo, os sistemas grandes
são projetados para serem modificados. Um sistema pode ser produtivamente revisado muitas vezes, entretanto,
os grandes programas também podem se tornar obsoletos por caducidade ao ultrapassar uma data-limite determi-
nada. A menos que um programa grande esteja bem escrito de forma adequada às tarefas que tenha de realizar,
como ocorre com programas pequenos, costuma ser mais eficiente escrever um novo programa que corrigir o pro-
grama antigo.

2.7.8 Iteração e evolução do software


As etapas de vida do software costumam fazer parte de um ciclo ou laço como sugere seu nome e não são sim-
plesmente uma lista linear. É provável, por exemplo, que, durante a fase de manutenção, seja necessário voltar às
especificações do problema para verificá-las ou modificá-las.
Observe na Figura 2.17 as diferentes etapas que rodeiam o núcleo documentação. A documentação não é, como
se espera, una etapa independente, está integrada em todas as etapas do ciclo de vida do software.

Manutenção Especificações

Evolução Projeto

Produção Verificação

Testes Codificação

Figura 2.17 Etapas do ciclo de vida do software cujo núcleo aglutinador é a documentação.
36   Programação em C: Metodologia, algoritmos e estruturas de dados

2.7.9 Fatores na qualidade do software


A construção de um software requer o cumprimento de inúmeras características. Entre elas se destacam as
seguintes:

Eficiência
A eficiência de um software é sua capacidade para fazer um bom uso dos recursos que manipula.

Transportabilidade (portabilidade)
A transportabilidade ou portabilidade é a facilidade com a qual um software pode ser transportado em diferentes
sistemas físicos ou lógicos.

Verificabilidade
A verificabilidade — facilidade de verificação de um software — é sua capacidade para suportar os procedimentos
de validação e de aceitar conjuntos de teste ou ensaio de programas.

Integridade
A integridade e a capacidade de um software de proteger seus próprios componentes contra os processos que não
tenham o direito de acessar.

Fácil de utilizar
Um software é fácil de utilizar se pode comunicar com o usuário de maneira cômoda.

Correção (exatidão)
Capacidade dos produtos software de realizar exatamente as tarefas definidas por sua especificação.

Robustez
Capacidade dos produtos software de funcionar, inclusive em situações anormais.

Extensibilidade
Facilidade que os produtos têm de se adaptar a mudanças em sua especificação. Existem dois princípios funda-
mentais para conseguir essa característica:
• projeto simples;
• descentralização.

Reutilização
Capacidade dos produtos de ser reutilizados, em sua totalidade ou em parte, em novas aplicações.

Compatibilidade
Facilidade dos produtos para ser combinados com outros.

2.8 MÉTODOS FORMAIS DE VERIFICAÇÃO DE PROGRAMAS


Ainda que a verificação formal de programas saia do âmbito deste livro, por sua importância, vamos considerar
dois conceitos-chave, asserções (afirmações) e precondições/pós-condições invariantes que ajudam a documentar,
corrigir e esclarecer o projeto de módulos e de programas.
Metodologia da programação e desenvolvimento de software   37

2.8.1 Asserções6
Uma parte importante de uma verificação formal é a documentação de um programa por meio de asserções ou
afirmações — sentenças lógicas acerca do programa que são declaradas «verdadeiras». Uma asserção é escrita
como um comentário e descreve o que se supõe ser verdadeiro sobre as variáveis do programa nesse ponto.

Uma asserção é uma frase sobre uma condição específica em certo ponto de um algoritmo ou programa.

Exemplo 2.12
O fragmento de programa a seguir contém uma seqüência de sentenças de atribuição seguidas por uma asserção.

A = 10; { asserção: A é 10 }
X = A; { asserção: X é 10 }
Y = X + A; { asserção: Y é 20 }

A verdade da primeira afirmação {A é 10} segue a execução da primeira sentença com o conhecimento de
que 10 é uma constante. A verdade da segunda afirmação {X é 10} vem da execução de X = A com o conhe-
cimento de que A é 10. A verdade da terceira afirmação {Y é 20} vem da execução Y = X + A com o
conhecimento de que X é 10 e A é 10. Nesse segmento do programa, afirmações são utilizadas como comentá-
rios para documentar a mudança em uma variável do programa depois de executada cada sentença de afirmação.
A tarefa de utilizar verificação formal serve para testar que um segmento de programa satisfaz sua especifica-
ção. A afirmação final é chamada pós-condição (nesse caso, {Y é 20}) e segue a presunção inicial ou precondi-
ção (nesse caso, {10 é uma constante}), depois de executado o segmento do programa.

2.8.2 Precondições e pós-condições


As precondições e pós-condições são afirmações simples sobre condições feitas no princípio e no final de los mó-
dulos. Uma precondição de um procedimento é uma afirmação lógica sobre seus parâmetros de entrada, supõe-se
verdadeira quando o procedimento é chamado. Uma pós-condição de um procedimento pode ser uma afirmação
lógica que descreve a mudança no estado do programa produzida pela execução do procedimento. A pós-condição
descreve o efeito de chamar ao procedimento, em outras palavras, a pós-condição indica que será verdadeira depois
que o procedimento for executado.

Exemplo 2.13
Precondições e pós-condições do subprograma LerInteiros.

subprograma LerInteiros (Mín, Máx: Inteiro;var N: Inteiro);


{
Leitura de um inteiro entre Mín e Máx em N
Pré: Mín e Máx são valores atribuídos
Pós: devolve em N o primeiro valor do dado entre Mín e Máx
se Mín <= Máx é verdadeiro; caso contrário
N não está definido.
}

6
  Este termo também costuma ser traduzido como afirmações ou declarações. O termo asserção está difundido na linguagem de informática.
38   Programação em C: Metodologia, algoritmos e estruturas de dados

A precondição indica que os parâmetros de entrada Mín e Máx são definidos antes do início da execução do
procedimento. A pós-condição indica que a execução do procedimento atribui o primeiro dado entre Mín e Máx
para o parâmetro de saída sempre que Mín <= Máx for verdadeiro.
As precondições e as pós-condições são mais que um método para resumir ações de um procedimento. A declara-
ção dessas condições deve ser sempre a primeira etapa no projeto e escrita de um procedimento. Ao escrever algoritmos
de procedimentos, é conveniente que se escreva o cabeçalho do procedimento que mostra os parâmetros afetados pelo
procedimento, bem como alguns comentários de cabeçalho que contenham as precondições e as pós-condições.

Precondição: Predicado lógico que deve ser cumprido ao começar a execução de uma operação.
Pós-condição: Predicado lógico que deve ser cumprido ao terminar a execução de uma operação desde
que a precondição correspondente tenha sido cumprida.

2.8.3 Regras para teste de programas


Um meio útil para testar que um programa P realmente faz o que tem de fazer é proporcionar asserções que ex-
pressem as condições antes e depois de P ser executado. Na realidade, as asserções são como sentenças ou decla-
rações que podem ser verdadeiras ou falsas.
A primeira asserção, a precondição, descreve as condições que terão de ser verdadeiras antes de executar P.
A segunda asserção, a pós-condição, descreve as condições que terão de ser verdadeiras depois que P tiver sido
executado (supondo que a precondição tenha sido verdadeira). O modelo geral é:
{precondição} {= condições lógicas que são verdadeiras antes da execução de P}
{pós-condição} {= condições lógicas que são verdadeiras depois da execução de P}

Exemplo 2.14
O procedimento OrdenarSeleção (A, m, n) ordena os elementos do array A[m..n] em ordem descendente.
O modelo correspondente pode ser escrito assim:
{m  n} {precondição: A tem de ter pelo menos 1 elemento}
OrdenarSeleção (A,m,n) {programa de ordenação a executar}
{A[m]  A[m+1]  ... A[n]} {pós-condição: elementos de A em ordem
descendente}

Problema 2.2
Encontrar a posição do maior elemento de uma lista com indicação de precondições e pós-condições.
int EncontrarMax (int* A,int m,int n)
{
/* precondição : m < n
pós-condição: devolve posição elemento maior em A[m..n] */
int i, j;
i = m;
j = n; {asserção}
/* (i = m)^(j = m)^(m < n) */ {^, operador and}
do {
i = i + 1;
if (A[i] > A[j])
j = i;
}while (i<n);
return j; /*devolve j como maior elemento*/
}
Metodologia da programação e desenvolvimento de software   39

2.8.4 Invariantes de laços


Uma invariante de laço é uma condição que é verdadeira antes e depois da execução de um laço. As invariantes de
laços são utilizadas para demonstrar a correção (exatidão) de algoritmos iterativos. Com a utilização de invariáveis
erros podem ser detectados antes de começar a codificação e assim se reduz o tempo de depuração e teste.

Exemplo 2.15
Um laço que calcula a soma dos n primeiros elementos do array (lista) A:
{calcular a soma de A[0], A[2],...A[n]}
{asserção n >= 14}
Soma = 0;
j = 0;
while (j <= n)
{
Soma = Soma + A[j];
j = j+1;
}
Antes de que esse laço comece a execução, Soma é 0 e j é 0. Depois de o laço ter sido executado uma vez,
Soma é A[0] e j é 1.
A invariante do laço Soma é a soma dos elementos A[0]a A[j+0]

Uma invariante é um predicado que cumpre tanto antes quanto depois de cada iteração (volta) e que
descreve a missão do laço.

Invariantes de laço como ferramentas de projeto


Outra aplicação para as invariantes de laço é a especificação do laço: iniciação, condição de repetição e corpo do laço.

Exemplo 2.16
Se a invariante de um laço é:
{invariante : i <= n e Soma é a soma de todos os números lidos do teclado}
Pode-se deduzir que:
Soma = 0.0; {iniciação}
i = 0;
i < n {condição/prova do laço}
scanf(“%d”,&Item);
Soma = Soma + Item; {corpo do laço}
i = i + 1;
Com todas essas informações, escrever o laço de soma torna-se uma tarefa fácil
Soma = 0.0;
i = 0;
while (i < n) /*i, recebe os valores 0,1,2,3,..n-1*/
{
scanf(“%d”,&Item);
Soma = Soma + Item;
i = i + 1;
}
40   Programação em C: Metodologia, algoritmos e estruturas de dados

Exemplo 2.17
Também é possível declarar invariantes nos laços for, mas deve-se ter presente a particularidade desta sentença:
a variável de controle do laço é indefinida depois que sai do laço, razão pela qual para, definir sua invariante,
é necessário considerar que a mencionada variável de controle seja incrementada antes de sair do laço e que
mantenha seu valor final.
/*precondição n >= 1*/
Soma = 0;
for (i=1; i<=n; i=i+1)
{ /*invariante : i <= n+1 e Soma é 1+2+...i-1*/
Soma = Soma + i;
}
/*pós-condição: Soma é 1+2+3+..n-1+n*/

Problema 2.3
Escrever um laço controlado por sentinela que calcule o produto de um conjunto de dados.
/*Calcular o produto de uma série de dados*/
/*precondição : sentinela é constante*/
Produto = 1;
printf(“Para terminar, introduza %d”, Sentinela);
puts(“Introduza número:”);
scanf(“%d”,&Número);
while (Número != Sentinela)
{ /*invariante: Produto é o produto de todos os valores
lidos em Número e nenhum é o Sentinela*/
Produto = Produto * Número;
puts(“Introduza número seguinte:”); scanf(“%d”,&Número);
}
/*pós-condição: Produto é o produto de todos os números lidos em
Número antes do sentinela*/

2.8.5 Etapas para estabelecer a exatidão (correção) de um programa


Podem-se utilizar invariantes para estabelecer a correção de um algoritmo iterativo. Suponhamos o algoritmo
já estudado:
/*calcular a soma de A[0], A[2],...A[n-1]*/
Soma = 0;
j = 0;
while (j <= n-1)
{
Soma = Soma + A[j];
j = j+1;
}
/*invariante: Soma é a soma dos elementos A[0] a A[j-1]*/
Os quatro pontos a seguir têm de ser verdadeiros:7
1. Antes do início da primeira execução do laço, a invariante deve ser inicialmente verdadeira. No exem-
plo anterior, Soma é 0 e j é inicialmente 0. Nesse caso, a invariante significa que Soma contém a soma
dos elementos A[0]a A[j—1], que é verdade, uma vez já que não há elementos nesse intervalo.

7
  CARRASCO et al., 1993, op. cit., p. 15.
Metodologia da programação e desenvolvimento de software   41

2. Uma execução de laço deve manter a invariante. Se a invariante for verdadeira antes de qualquer ite-
ração do laço, deve-se demonstrar que também é verdadeira depois da iteração. No exemplo, o laço
acrescenta A[j] a Soma e em seguida incrementa j em 1. Por conseguinte, depois de uma execução do
laço, o elemento acrescentado mais recentemente a Soma é A[j—1]; ou seja, a invariante que é verdadei-
ra depois da iteração.
3. A invariante deve capturar a exatidão de algoritmo, isto é, deve demonstrar que se a invariante for
verdadeira quando terminar o laço, então o algoritmo está correto. Quando o laço do exemplo termina, j
contém n e a invariante é verdadeira: Soma contém a soma dos elementos A[0] a A[j—1], que é a soma
que se quer calcular.
4. O laço deve terminar, isto é, deve-se demonstrar que o laço termina depois de um número finito de ite-
rações. No exemplo, j começa em 0 e em seguida se incrementa em 1 em cada execução do laço. Por
conseguinte, j eventualmente excederá a n independente do valor de n. Esse fato e a característica funda-
mental de while garantem que o laço terminará.

A identificação de invariantes de laços ajuda a escrever laços corretos. A invariante é representada como
um comentário que precede cada laço. No exemplo anterior
{Invariante: 0 <= j < N e Soma = A[0]+...+A[j-1]}
while (j <= n—1)

2.8.6 Programação segura contra falhas


Um programa é seguro contra falhas quando é razoavelmente executado por qualquer pessoa que o utilize. Para
conseguir esse objetivo, os erros nos dados de entrada e na lógica do programa terão de ser comprovados.
Suponhamos um programa que espera ler dados inteiros positivos, mas lê –25. Uma mensagem típica visua-
lizada diante desse erro costuma ser:
Erro de categoria
Entretanto, é mais útil uma mensagem tal como esta:
—25 não é um número de anos válidos
Por favor volte a introduzir o número
Outras regras práticas a serem consideradas são:
• Comprovar dados de entrada inválidos
scanf(“%f %d”, Grupo, Número);
...
if (Número >= 0)
agregar Numero a total
else manejar o erro
• Cada subprograma deve comprovar os valores de seus parâmetros. Assim, no caso da função SomaIntervalo,
que soma todos os inteiros compreendidos entre m e n.
int SomaIntervalo (int m,int n)
/*precondição : m e n são inteiros, tais que m <= n
pós-condição: Devolve SomaIntervalo = m+(m+1)+...+n
m e n são inalteráveis */
{
int Soma,Índice;
Soma = 0;
for (Índice= m; Índice<=n ;Índice++) Soma = Soma + Índice;
return Soma;
}
42   Programação em C: Metodologia, algoritmos e estruturas de dados

2.9  RESUMO
Um método geral para a resolução de um problema com descendentes e refinamento sucessivo, chegar a módulos
computador tem as seguintes fases: facilmente codificáveis. Esses módulos devem ser codificados
1. Análise do programa. com as estruturas de controle de programação estruturada.
2. Projeto do algoritmo. 1. Seqüenciais: as instruções são executadas uma depois
3. Codificação. da outra.
4. Compilação e execução. 2. Repetitivas: uma série de instruções são repetidas até
5. Verificação e depuração. que se cumpra certa condição.
6. Documentação e manutenção. 3. Seletivas: permite escolher entre duas alternativas
O sistema mais seguro para resolver um pro­blema é de- (dois conjuntos de instruções), dependendo de uma
compô-lo em módulos mais simples e, por meio de projeto condição determinada.

2.10  EXERCÍCIOS
  2.1. Projetar uma solução para resolver cada um dos se- corredor e o algoritmo deve imprimir o tempo em
guintes problemas e tratar de refinar suas soluções minutos e segundos bem como a velocidade média.
por meio dos algoritmos adequados: Exemplo de entrada de dados: (3,53) (3,40) (3,46)
a) Realizar uma chamada telefônica de um telefone (3,52) (4,0) (0,0); o último par de dados será utiliza-
público. do, como fim da entrada de dados.
b) Cozinhar uma omelete.
c) Consertar um furo em uma bicicleta.   2.9. Projetar um algoritmo para determinar se um número
d) Fritar um ovo. N é primo. (Um número primo somente pode ser di-
visível por ele mesmo e pela unidade).
  2.2. Escrever um algoritmo para:
a) Somar dois números inteiros. 2.10. Escrever um algoritmo que calcule a superfície de um
b) Diminuir dois números inteiros. triângulo em função da base e da altura (S = 1/2 Base
c) Multiplicar dois números inteiros.  Altura).
d) Dividir um número inteiro por outro.
2.11. Calcular e visualizar o comprimento da circunferên-
  2.3. Escrever um algoritmo para determinar o máximo cia e a área de um círculo de raio dado.
divisor comum de dois números inteiros (MDC) pelo
2.12. Escrever um algoritmo que encontre o salário sema-
algoritmo de Euclides:
nal de um trabalhador, dada a taxa horária e o núme-
• Dividir o maior dos dois inteiros positivos pelo ro de horas trabalhadas diariamente.
menor deles.
• Em seguida dividir o divisor pelo resto. 2.13. Escrever um algoritmo que indique se uma palavra
• Continuar o processo de dividir o último divisor lida do teclado é um palíndromo. Um palíndromo é
pelo último resto até que a divisão seja exata. uma palavra que se lê igual em ambos os sentidos
• O último divisor é o mdc. como «radar».

  2.4. Projetar um algoritmo que leia e imprima uma série 2.14. Escrever um algoritmo que conte o número de ocor-
de números diferentes de zero. O algoritmo deve ter- rências de cada letra em uma palavra lida como en-
minar com um valor zero que não deve ser impresso. trada. Por exemplo, «Mortimer» contém dois «m»,
Visualizar o número de valores lidos. um «o», dois «r», um «i», um «t» e um «e».

  2.5. Projetar um algoritmo que imprima e some a série de 2.15. Muitos bancos calculam diariamente os juros das
números 3, 6, 9, 12…, 99. quantidades depositadas pelos clientes com base nas
seguintes premissas. Um capital de 1.000 euros com
  2.6. Escrever um algoritmo que leia quatro números e em uma taxa de juros de 6% rende em um dia 0,06 mul-
seguida imprima o maior dos quatro. tiplicado por 1.000 e dividido por 365. Essa operação
produzirá 0,16 euros de juros e o capital acumulado
  2.7. Projetar um algoritmo que leia três números e descu- será 1.000,16. Os juros para o segundo dia serão
bra se um deles é a soma dos outros dois. calculados multiplicando-se 0,06 por 1.000 e divi-
dindo-se o resultado por 365. Projetar um algoritmo
  2.8. Projetar um algoritmo para calcular a velocidade (em que receba três entradas: o capital a ser depositado,
m/s) dos corredores da corrida de 1.500 metros. A a taxa de juros e a duração do depósito em semanas
entrada consistirá em pares de números (minutos, e calcule o capital total acumulado no final do perío-
segundos) que dão o tempo do corredor; para cada do de tempo especificado.
Metodologia da programação e desenvolvimento de software   43

2.11  EXERCÍCIOS RESOLVIDOS


Desenvolva os algoritmos que resolvam os seguintes pro- Projeto do algoritmo
blemas: início
2.1 Ir ao cinema. ir à bilheteria
se não há entradas na bilheteria
Análise do problema se nos interessa comprá-la com
DADOS DE SAÍDA: Ver o filme. cambistas
DADOS DE ENTRADA: Nome do filme, endereço da sala, ir comprar a entrada
hora de projeção. se não ir a fim
DADOS AUXILIARES: Entrada, número do assento. < comprar a entrada >
selecionar sol ou sombra
Para solucionar o problema, deve-se selecionar um selecionar primeira fila,
filme no jornal, ir ao cinema e comprar a entrada para, final- arquibancada ou camarote
mente, poder ver o filme. selecionar número do assento
solicitar a entrada
Projeto do algoritmo
se está disponível
início adquirir a entrada
< selecionar o filme > se não
pegar o jornal se quisermos outro tipo de entrada
enquanto não passar a folha até chegar ir comprar a entrada
ao caderno de cinema fim.
enquanto não termine o caderno
ler os filmes 2.3 Fazer uma xícara de chá.
se nos agrada, recordá-la
DADOS DE SAÍDA: xícara de chá.
escolher um dos filmes selecionados
ler o endereço do cinema e a sala, ver a DADOS DE ENTRADA: chá, água.
hora da projeção DADOS AUXILIARES: apito da chaleira, aspecto da
< comprar a entrada > infusão.
ir até o cinema
se não há entradas, ir a fim Depois de colocar água na chaleira, levá-la ao fogo e
se há fila esperar que a água ferva (até que soe o apito da chaleira).
colocar-se no final Adicionamos o chá e esperamos por um tempo até que este-
enquanto não chegamos à bilheteria ja pronto.
avançar
Projeto do algoritmo
se não há entradas, ir a fim
comprar a entrada início
< ver o filme > pegar a chaleira
ler o número do assento que está na enchê-la de água
entrada acender o fogo
buscar o assento colocar a chaleira no fogo
sentar-se enquanto a água não ferve
ver o filme esperar
fim. pegar o chá
introduzi-la na chaleira
2.2 Comprar uma entrada para ir à tourada. enquanto não está feito o chá
esperar
Análise do problema despejar o chá em uma xícara
fim.
DADOS DE SAÍDA: A entrada.
DADOS DE ENTRADA: Tipo de entrada (sol, sombra, 2.4 Fazer uma chamada telefônica. Considerar os casos:
arquibancada de sombra, ar- a) chamada manual com telefonista; b) chamada au-
quibancada). tomática; c) chamada a cobrar.
DADOS AUXILIARES: Disponibilidade da entrada. Análise do problema
Ir à bilheteria e escolher a entrada desejada. Se há Para decidir o tipo da chamada que se efetuará, primeiro
entradas, se compra (na bilheteria ou com os cambistas). Se deve-se considerar se dispomos ou não de dinheiro ou se a
não há, pode-se escolher outro tipo de entrada ou desistir, chamada será a cobrar. Se há dinheiro, deve-se ver se o lugar
repetindo essa ação até que se consiga a entrada ou que o para fazermos a chamada está conectado à rede automática
possível comprador tenha desistido. ou não.
44   Programação em C: Metodologia, algoritmos e estruturas de dados

Para uma chamada com telefonista, deve-se chamar Projeto do algoritmo


a central e solicitar a chamada, esperando até que a comuni-
cação seja estabelecida. Para uma chamada automática, lê-se
os prefixos do país e da cidade, se for necessário, e se reali- Início
za a chamada, esperando até que atendam o telefone. Para
chamar a cobrar, deve-se ligar para a central, solicitar a
chamada e esperar que o assinante do telefone que está sen- Ler
do chamado dê sua autorização, quando então se estabelece- palavra
rá a comunicação.
Como dados de entrada teríamos as variáveis que vão
nos condicionar o tipo da chamada, o número do telefone e, Ler
no caso de chamada automática, os prefixos (caso existam). último caractere
Como dado auxiliar, poderíamos considerar, nos casos a) e
c), o contato com a central.
juntar o caractere
Projeto do algoritmo aos anteriores
início
se temos dinheiro
Ler
se podemos fazer uma chamada caractere anterior
automática
Ler o prefixo do país e localidade
teclar o número
não
se não mais caracteres?
< chamada manual >
chamar a central
sim
solicitar comunicação
enquanto não respondem não sim
esperar palavras iguais?
estabelecer comunicação
se não não é é
um palíndromo um palíndromo
< realizar uma chamada a cobrar >
chamar a central
solicitar a chamada
esperar até ter a autori-
fim
zação
estabelecer comunicação
fim.
2.6 Projetar um algoritmo para calcular a velocidade
2.5 Averiguar se uma palavra é um palíndromo. Um pa- (em metros/segundo) dos corredores de uma corrida
líndromo é uma palavra que se lê igual da esquerda de 1.500 metros. A entrada será pares de números
para a direita e da direita para a esquerda, por (minutos, segundos) que darão o tempo de cada cor-
exemplo, «radar». redor. Para cada corredor se imprimirá o tempo em
minutos e segundos, bem como a velocidade média.
Análise do problema O laço será executado até darmos uma entrada de
0,0 que será a marca de fim de entrada de dados.
DADOS DE SAÍDA: A mensagem que nos diz se
é ou não um palíndromo. Análise do problema
DADOS DE ENTRADA: Palavra
DADOS DE SAÍDA: v (velocidade média).
DADOS AUXILIARES: cada caractere da palavra,
palavra ao contrário. DADOS DE ENTRADA: mm,ss (minutos e segun-
dos).
Para comprovar se uma palavra é um palíndromo, DADOS AUXILIARES: distância (distância per-
pode-se ir formando uma palavra com os caracteres inverti- corrida, que, nesse exemplo,
dos em relação à palavra original e comprovar se a palavra é de 4 1.500 metros) e tempo
ao contrário é igual a original. Para obter essa palavra ao (os minutos e os segundos
contrário, os caracteres da palavra original serão lidos ao con- que demoraram a correr).
trário e vão se juntando sucessivamente até chegar ao primei-
ro caractere.
Metodologia da programação e desenvolvimento de software   45

Deve-se efetuar um laço até que mm seja 0 e ss seja


0. Dentro do laço se calcula o tempo em segundos com a Início
fórmula tempo = ss + mm * 60. A velocidade será encontra-
da com a fórmula
soma 0
velocidade = distância / tempo. núm 0
Projeto do algoritmo
início núm núm + 1
distância ← 1500 soma soma + núm
ler (mm, ss)
enquanto não (mm = 0 e ss = 0 fazer não
tempo ← ss + mm * 60 núm = 10
v ← distância / tempo
escrever (mm,ss,v) sim
ler (mm,ss) escrever
fim soma

2.7 Escrever um algoritmo que calcule a superfície de Fim


um triângulo em função da base e da altura.

Análise do problema
2.9 Desenvolver um algoritmo que calcule e visualize as
DADOS DE SAÍDA: s (superfície). potências de 2 entre 0 e 10.

DADOS DE ENTRADA: b (base a (altura). Análise do problema

Para calcular a superfície se aplica a fórmula Deve-se implementar um laço que seja executado onze ve-
zes e dentro dele ir incrementando uma variável que assuma
S = base * altura / 2. valores entre 0 e 10. Essa variável será chamada num. Den-
tro do laço também será visualizado o resultado da opera-
Projeto do algoritmo ção 2 ^ num.
início
Projeto do algoritmo
ler (b, a)
s = b * a / 2 TABELA DE VARIÁVEIS
escrever (s) inteiro: num
fim
Início
2.8 Desenvolver um algoritmo que calcule a soma dos
inteiros entre 1 e 10, ou seja, 1+2+3+...+10. núm 0

Análise do problema
escrever
DADOS DE SAÍDA: Soma (contém a soma reque- 2 ^ núm
rida
DADOS AUXILIARES: num (será uma variável que núm núm + 1
assumirá valores entre 1 e 10
e se acumulará em soma).
não
núm > 10
Deve-se executar um laço que se realize 10 vezes.
Nele, a variável num vai sendo incrementada em 1 e seu sim
valor será acumulado na variável soma. Depois de sair do Fim
laço se visualizará o valor da variável soma.

Projeto do algoritmo 2.10 Quer-se obter o salário líquido de um trabalhador


conhecendo o número de horas trabalhadas, o salá-
TABELA DE VARIÁVEIS rio hora e a alíquota dos impostos que devem ser
inteiro: soma, num aplicados como deduções.
46   Programação em C: Metodologia, algoritmos e estruturas de dados

As entradas do algoritmo são:


horas trabalhadas, salário_hora, alíquotas Diagrama de fluxo
As saídas do algoritmo são:
pagamento bruto, total de impostos e Início
pagamento líquido
introduzir
O algoritmo geral é: HORAS
1. Obter valores de horas trabalhadas, SALÁRIO_HORA
ALÍQUOTAS
salário_hora e alíquotas.
2. Calcular salário_bruto, total de calcular
impostos e salário_líquido. SALÁRIO_BRUTO←
3. Visualizar salário_bruto, total de HORAS*SALÁRIO_HORA
impostos e salário_líquido.
calcular
O refinamento do algoritmo em passos de nível inferior é:
TOTAL_IMPOSTOS←
1. Obter valores de horas trabalhadas, SALÁRIO_BRUTO*ALÍQUOTAS
salário bruto e alíquotas.
2. Calcular salário bruto, total de calcular
impostos e pagamento líquido SALÁRIO_LÍQUIDO ←
2.1. Calcular salário bruto SALÁRIO_BRUTO-TOTAL_IMPOSTOS
multiplicando as horas
trabalhadas pelo salário hora. visualizar
SALÁRIO_BRUTO
2.2. Calcular o total de impostos
TOTAL_IMPOSTOS
multiplicando-se salário bruto por
SALÁRIO_LÍQUIDO
alíquotas (tanto por cento de
impostos). fim
2.3. Calcular o salário líquido
diminuindo o total de impostos
do pagamento bruto.
3. Visualizar salário bruto, total de
impostos e salário líquido.
O diagrama de fluxo a seguir representa o algoritmo dado:

2.11 Definir o algoritmo necessário para intercambiar os valores de duas variáveis numéricas.
Análise do problema
Para desenvolver essa análise, utiliza-se uma variável denominada auxiliar que,
de modo temporal, assuma um dos valores dados. Início
Variáveis: A B AUX
O método consiste em atribuir uma das variáveis para a variável auxiliar:
AUX ← A ler A, B
Em seguida, atribui-se o valor da outra variável B para a primeira:
A ← B
AUX ← A
Por último, atribui-se o valor da variável auxiliar para a segunda variável A: A←B
B ← AUX B ← AUX
Variáveis: A primeiro valor
B segundo valor
AUX variável auxiliar escrever
A, B
Projeto do algoritmo
início
ler(A, B)
Fim
AX ← A
A ← B
B ← AUX
escrever(A, B)
fim

Você também pode gostar