Escolar Documentos
Profissional Documentos
Cultura Documentos
ENGENHARIA AUTOMÓVEL
ENGENHARIA E GESTÃO INDUSTRIAL
PROGRAMAÇÃO I
por
Filipe Neves
&
Micaela Esteves
Versão 1.3
2003/2004
Análise do problema e formulação de um algoritmo
O que é a programação?
O comportamento e o pensamento humano dão-se em sequências lógicas. Muito do que fazemos cada dia
fazemo-lo automaticamente, a um nível inconsciente. Felizmente não é necessário para nós pensarmos
conscientemente cada passo que damos em algo tão simples como mover esta página com a mão. Mas se
o fizermos conscientemente damo-nos conta das seguintes tarefas:
§ Levantar a mão
§ Mover a mão para a parte direita da folha
§ Agarrar a esquina da página
§ Mover a mão da direita para a esquerda
§ Soltar a página
Por sua vez, cada uma destas tarefas elementares é ainda composta por outras tarefas mais pequenas.
Outro exemplo de uma sequência lógica é a forma como limpamos os dentes. Tem-se o cuidado de
colocar a pasta de dentes na escova e depois escovar os dentes. Qualquer coisa que fazemos necessita de
uma sequência lógica de passos. Toda a nossa civilização está baseada na ordem das coisas e das acções.
A ordenação consciente ou inconsciente é uma parte importante das nossas vidas. Esta ordenação adquire-
se através de um processo que se pode chamar de programação.
Para escrever uma sequência de instruções que um computador deve seguir é necessário seguir uma certa
estratégia. Este procedimento é composto por uma fase de resolução do problema e uma fase de
implementação.
Solução geral (algoritmo)- Desenrolar de uma sequência ló gica de passos utilizados para resolver o
problema.
Prova- Seguir a sequência de passos anteriormente determinada para ver se a solução resolve
verdadeiramente o problema.
Fase de implementação
O computador não é inteligente. Não pode analisar um problema e encontrar uma solução. O programador
é que deve encontrar uma solução e comunicá-la ao computador. Assim, é necessário que à máquina
sejam indicados todos os passos a seguir bem como os possíveis e diferentes caminhos pelos quais é
possível optar, a fim de se obter um determinado resultado. Torna-se pois, necessário conhecer todos
estes passos elementares e a respectiva sequência, por forma a dá-los a conhecer à máquina. É a esta
tarefa que chamamos programar, ou codificar, como veremos já à frente.
Prova- Fase em que o computador segue as instruções. Comprovar os resultados e fazer as correcções se
necessário.
Algoritmo
Mas voltemos ao assunto do algoritmo para o definir e usar na resolução de dois problemas.
Algoritmo é uma sequência ordenada e precisa de passos, acções ou operações, que conduzem à solução
de um dado problema.
Usamos algoritmos todos os dias. São simplesmente uma descrição verbal ou escrita de sequências
lógicas de acções. Receitas de cozinha, instruções para instalar um vídeo e para instalar uma impressora,
são exemplos de algoritmos escritos.
EXEMPLO:
Quando se arranca com um carro realiza-se um procedimento passo a passo. O algoritmo pode ser:
§ Colocar a chave
§ Assegurar que o carro está em ponto morto
§ Dar à chave para a posição de arranque.
§ Se o motor arranca antes de 6 segundos, pode-se começar a andar.
§ Se o motor não arranca antes dos 6 segundos esperar 10 segundos e repetir os passos desde
o 3 até ao 5 (mas não mais do que cinco vezes).
§ Se o carro não arranca chamar um mecânico.
Sem a condição “não mais de cinco vezes” no passo 5, podia-se interpretar que estaríamos a tentar
arrancar com o carro para sempre. Porquê? Se algo falha no carro, a repetição dos passos 3 a 5 nunca
mais pára. Esta situação de nunca mais terminar chama-se de ciclo infinito. Um algoritmo deve terminar
ao fim de uma quantidade finita de tempo (ou de vezes, neste contexto chamadas de iterações) em todas
as possíveis condições.
EXEMPLO:
Um programador pode necessitar de um algoritmo para determinar o salário semanal de um empregado.
Se fizer mais que 35 horas, recebe o dobro pelas horas extra. O algoritmo deve reflectir como o faríamos
à mão:
§ Ir buscar o preço por hora do empregado.
§ Determinar o número de horas efectuadas durante a semana
§ Se o número de horas de trabalho durante a semana for menor ou igual a 35, multiplicar
esse número pelo preço de uma hora.
§ Se o número de horas for superior a 35 horas, multiplicar 35 horas pelo preço de uma hora
e às restantes pelo dobro do preço de uma hora, de forma a calcular o preço das horas
extras.
§ Somar o salário normal com as horas extras (se houver) obtendo-se assim o salário a pagar.
Destes exemplos podemos notar que um algoritmo deve possuir as seguintes características:
§ total ausência de ambiguidade, ou seja; a interpretação dada ao algoritmo é sempre a mesma,
independente da leitura que dele se faça ou de quem faça a sua leitura;
§ número finito de acções;
§ generalidade.
Depois de encontrar uma solução geral, o programador percorre-a (o algoritmo) efectuando cada passo
manualmente ou mentalmente. Se esta prova não produz as respostas correctas, então repete-se o processo
analisando de novo o problema formando um outro algoritmo. Quando o programador está satisfeito com
o algoritmo, há que transcrevê-lo para a linguagem de programação que se pretende utilizar – trata-se da
operação de codificação: produzir uma mensagem ou conjunto de mensagens que designam acções em
conformidade com um determinado código que o computador entenda.
Sobre a resolução de problemas, pode-se resumir o que se disse anteriormente através do seguinte
esquema:
Início do programa
Análise da situação/problema
Formulação de um algoritmo
Prova:
Não
A solução resolve o
problema?
do
Sim
Revisão
Codificação:
do
Tradução do algoritmo numa linguagem de
programação específica
Revisão
Sim
Detecção de Não
erros de lógica
Sim
Programa terminado
Quando se depara com um problema extenso e complexo, o ser humano mostra-se limitado no sentido de,
só conseguir compreender pequenas partes de cada vez. Uma estratégia para colmatar esta limitação
humana baseia-se no princípio «dividir para conquistar». Não é mais do que dividir o problema inicial em
subproblemas menores de modo a que este se torne mais facilmente compreensível e de mais fácil
interpretação e resolução. Nesta estratégia várias abordagens são possíveis. Entre elas podem contar-se a
de tentativas sucessivas, a abordagem ascendente e a abordagem descendente. No nosso estudo focaremos
a abordagem descendente ou “top-down”.
Abordagem “top-down”
Esta técnica leva-nos a adquirir um hábito essencial ao sucesso, que é a organização e método, obrigando-
nos à divisão do problema em módulos e cada módulo noutros mais simples e assim sucessivamente até
se compreender, sem qualquer ambiguidade, o significado dessa parte do problema. Este método é
conhecido como método descendente (ou “top-down”).
Pode-se dividir o método descendente do seguinte modo:
1. Análise do problema.
2. Algoritmo
3. Prova1
Análise do problema
1.1- Compreender o problema. Compreender o que se dá (dados de entrada) e o que se pede (dados
de saída).
1.2- Especificar os formatos de entrada e os de saída.
1.3- Pensar em como se pode resolver o problema à mão.
Algoritmo
2.1- Escrever o módulo principal
Se este módulo é demasiado grande é porque está com um nível demasiado baixo de detalhe, o
que é sinal que pode ser decomposto em módulos. Tudo o que se tem a fazer no módulo principal
é atribuir nomes aos módulos do nível inferior. Como nomes para os módulos, devem sempre
utilizar-se nomes com significado.
Este módulo é quase linguagem falada, à qual foram retiradas palavras redundantes.
1
Já conhecemos estes termos e nesta sequência. O que abordáramos antes fora uma introdução à abordagem
descendente.
Micaela Esteves & Filipe Neves 6/61
Análise do problema e formulação de um algoritmo
Prova
Trata-se da verificação mental para ver se a solução “funciona”.
EXEMPLO:
Para melhor compreender esta técnica vejamos um exemplo prático.
Pretende-se elaborar um programa que determine a média ponderada de três exames. Os dados são
inseridos na seguinte sequência: nota do exame; respectivo peso.
Para facilidade poderemos no fim fazer um teste considerando pesos de 30%, 30% e 40%
respectivamente.
Análise do problema
Dados de entrada: três grupos de dados, cada um composto por nota de exame e respectivo peso;
As notas de exame são sempre valores inteiros.
Peso dos exames podem ser números reais.
O valor da média é de 14.8. Não nos esqueçamos que estivemos a raciocinar para o caso de os pesos
terem aqueles valores concretos.
O algoritmo deverá contemplar um caso genérico.
Obter dados
Encontrar a média
Mostrar a média
Obter dados:
Encontrar a média
Imprimir a média
Escrever a Média
Embora este seja um problema de fácil resolução e possa portanto parecer desnecessário esta abordagem
exaustiva, o uso desta abordagem é crucial para a aprendizagem da programação. Com o evoluir da
complexidade dos problemas/programas, esta abordagem revelar-se-á útil e gratificante. Também com o
evoluir na tarefa de programar, o método irá progressivamente sendo assimilado e chega o ponto em que
o utilizaremos tão fácil e intuitivamente que já não nos damos conta do seu uso, nem mesmo da facilidade
que ele permite.
Por vezes a abordagem anterior revela-se impotente para resolver todos os problemas, tornando-se mais
evidente resolver o problema por tentativas sucessivas. Como exemplo apresenta-se o seguinte problema:
Maria tinha um cesto com maçãs e encontrou um amigo a quem deu metade das
maçãs e mais meia maçã. Depois encontrou outro amigo a quem deu igualmente
metade das maçãs que tinha e mais meia maçã e mais meia maçã. Por fim encontrou
um terceiro amigo a quem deu metade das maçãs que lhe restavam e mais meia maçã
e ficou sem nenhuma. Quantas maçãs tinha a Maria antes de encontrar o primeiro
amigo?
Podemos chegar à solução por tentativas sucessivas. Numa primeira tentativa imaginemos que Maria
tinha inicialmente 13 maçãs. Seguindo os passos apresentados no problema, chegamos a um resultado
final no qual Maria fica com ¾ de maçã. Concluímos que partimos dum valor demasiado elevado
inicialmente.
Na nova tentativa teremos que começar com um número menor. E assim por tentativas chegaremos à
solução.
Esta técnica segue o sentido inverso da primeira para chegar à solução do problema, isto é, partimos do
resultado e tentamos atingir as condições iniciais do problema. Embora aparentemente absurda revela-se
muitas vezes útil. O problema anterior tem uma solução muito mais rápida se usarmos esta abordagem
pois conhecemos a quantidade final de maçãs que Maria tinha no cesto.
Existem algumas formas para representar algoritmos. De entre elas podem citar-se:
• pseudocódigo
• fluxogramas,
• tabelas de decisão,
• quadros estruturados,
• diagramas HIPO
Pseudocódigo
Na fase de escrita do algoritmo do problema anterior utilizámos uma linguagem muito próxima da falada
pelo ser humano. A esta técnica chamamos pseudocódigo e não consiste em mais que pegar no texto em
português que descreve a resolução do problema e retirar-lhe as redundâncias de linguagem bem como as
suas ambiguidades, ficando assim um texto ainda compreensível de forma inequívoca. Para este
pseudocódigo existem algumas palavras chave habitualmente escritas em maiúsculas, a saber:
• INÍCIO / FIM
• LER / ESCREVER
• SE ... ENTÃO ... SENÃO ...
• REPETIR ... ATÉ QUE ...
• ENQUANTO ... FAZER ...
• FAZER n VEZES ...
EXEMPLO:
* programa que lê um número, calcula o seu dobro e apresenta o resultado
INÍCIO
LER a
b=a*2
ESCREVER “o dobro de a é:” b
FIM
Neste exemplo note-se uma hierarquia de tabulação para explicitar um pedaço de pseudocódigo como
pertencendo a um troço funcionalmente independente. A esta forma de apresentação chamamos
identação, e é uma prática bastante útil para uma fácil legibilidade dum algoritmo.
Para referir uma linha como comentário, isto é, cujo conteúdo não tem qualquer significado em termos da
execução na resolução do problema, mas simplesmente constituindo notas para o leitor, usa-se o símbolo
asterisco no início de linha.
Fluxogramas
Fluxogramas são diagramas que mostram as operações a realizar e a sequência segundo a qual as
diferentes operações que constituem o algoritmo deverão ser realizadas. Para tal recorre-se a um conjunto
de símbolos padrão, a saber :
• Processamento
• Leitura/escrita
• Linhas de fluxo
• Conector
• Decisão
• Decisão múltipla
• Início/fim
Estruturas algorítmicas
Perante a análise dum algoritmo com alguma atenção, podemos reparar que certas vezes o fluxo das
acções a desempenhar apresenta diversos padrões ou estruturas:
• Sequência: o fluxo do programa é linear, sendo as instruções executadas uma após outra.
• Selecção: o fluxo dum programa não é linear. Dependendo da avaliação de uma dada condição é
executada o fluxo é desviado numa direcção ou noutra, executando diferentes conjuntos de
instruções. É o caso do uso de SE... ENTÃO...SENÃO...
• Repetição: um dado conjunto de instruções é retomado várias vezes até que ou enquanto uma
dada condição seja verificada, sendo que o fluxo do programa tema forma cíclica. É o caso do uso
das expressões como ENQUANTO ... FAZER, REPETIR ... ATÉ QUE ... ou FAZER n VEZES.
Bem sabida agora a definição de algoritmo, podemos descrevê-lo como sendo uma “caixa” com uma
lógica implementada no seu interior, mas cujos pormenores nada conhecemos sob ponto de vista de
utilizadores (por isso se lhe chama uma caixa preta) que recebe valores de entrada (os dados; o input) e
apresenta valores à saída (os resultados; o output).
Os dados que um computador pode manipular podem ser de vários tipos. Basicamente podemos ter:
• Dados numéricos. São constituídos por números positivos, negativos, inteiros, reais. O tamanho
de um determinado dado tem um limite inferior e um limite superior.
• Dados alfabéticos. São constituídos unicamente por letras ou sequências de letras às quais se dá o
nome de cadeias ou “strings”.
• Ao conjunto de dados numéricos em união com o conjunto de dados alfabéticos dá-se o nome de
alfanuméricos, e é o dado mais frequentemente designado que qualquer um dos outros dois.
Como exemplo refira-se a cadeia “escola123”.
Para representar cadeias ou “strings” usam-se como seus delimitadores as aspas. Assim por
exemplo temos o caracter “4” que não é o mesmo que o número 4. Voltaremos a este assunto
mais tarde.
• Dados lógicos. Também são chamados booleanos, por homenagem ao matemático Boole. Só
podem tomar dois valores: Verdadeiro ou Falso, também muitas vezes representados por 1 (um)
ou 0 (zero), respectivamente.
EXEMPLOS:
a) 3,14 é numérico real
b) 244 é um numérico inteiro
c) “António” é um alfabético (e também alfanumérico, apesar de não ter números)
d) V (verdade) é um lógico
e) “3,14” é um alfanumérico: tem 3 numéricos e um alfabético – a vírgula.
Definimos como constante um objecto (uma entidade) do algoritmo que contém um valor imutável, que
nunca se altera qualquer que seja a ocasião onde aparece, onde aparece ou o número de vezes que o
algoritmo é executado. Pode ser de qualquer dos tipos de dados apresentados.
Definimos variável como sendo um objecto cujo conteúdo é possível de ser alterado durante o decorrer
do algoritmo. Em instantes diferentes pode assumir distintos valores.
Uma expressão é obtida pela composição de variáveis e constantes com operadores aritméticos ou lógicos
de modo a permitir o cálculo daquilo que se pretende. Como exemplo pode citar-se a
“Cálculo intermédio”, já utilizada nas aulas. Outro exemplo relacionado com o cálculo da área dum
círculo: “PI*R*R”.
Que acontece então ao valor PI*R*R? Há que atribuí-lo a algo! Neste caso estamos mesmo a ver que é a
uma área. Podemos pois atribuí-lo a uma variável que a represente.
Operação de atribuição
Para o caso anterior o nome area seria bem escolhido, pois é um nome sugestivo.
Como forma de representarmos essa atribuição escrevemos: area ← PI*R*R.
O símbolo ← significa que o valor resultante da expressão PI*R*R é atribuído à variável area.
Uma instrução de atribuição é sempre feita da direita para a esquerda e armazenada no objecto à
esquerda. É uma operação destrutiva, isto é, o valor que antes estava na variável desaparece para dar lugar
ao novo. A uma variável podemos atribuir uma outra variável ou uma constante. Por exemplo, após
definida a variável var podemos fazer:
var ← 5 significa que var toma o valor 5
var ← var + 1 significa que var toma o valor 6
NOTA1
Uma regra de bom senso é que a declaração sempre seja feita no início do bloco de código em que seja
utilizada.
NOTA2
Não é possível atribuir diferentes tipos de valores à mesma variável: umas vezes números, outras vezes
alfanuméricos.
var ← 5 não se pode fazer as duas, pois trata-se de tipos diferentes. 5 é
var ← “António” numérico; “António” é alfabético.
Outros operadores
Além do operador de atribuição, outros existem para fazer cálculos, cujo tipo varia de acordo com o tipo
dos dados que estão a ser operados (os operandos). Estes operadores dividem-se nos seguintes tipos:
• Aritméticos:
§ de soma (+)
§ de subtracção (-)
§ de multiplicação (*)
§ de divisão (/)
§ de potenciação (^)
• Lógicos:
§ conjunção (E), (AND)
§ disjunção (OU), (OR)
§ negação (NAO), (NOT)
Relativamente a estes operadores há a referir a regra das prioridades que diz que numa expressão são
feitas em primeiro lugar as potenciações, depois as multiplicações e divisões, consoante a ordem em que
aparecem e depois as somas e subtracções, também segundo a ordem em que aparecem. Para subverter
esta regra podem usar-se os parêntesis, de cujo uso as seguintes expressões apresentam resultados
diferentes.
6+4/2 cujo resultado é diferente de (6+4)/2;
5+4*2^2/4-7, cujo resultado é 2, ou ((5+4)*2^2)/(4-7), cujo resultado é - 12.
Os operadores lógicos permitem avaliar condições e tomar decisões consoante o resultado da sua
avaliação. Uma condição é uma expressão acerca da qual se pode dizer se é verdadeira ou falsa.
Por exemplo na avaliação da condição:
Carro vai ao posto se [(falta gasolina) OU (falta faróis E circula de noite)].
Vemos que basta que falte a gasolina para ir ao posto, mas é necessário que se verifique simultaneamente
faltar faróis e que circule de noite para que necessite de lá ir.
Podemos estabelecer a chamada tabela de verdade para cada um destes operadores lógicos:
A LINGUAGEM C
Utilizamos programas para fazer com que o computador trabalhe para nós. Estes programas são escritos
numa das muitas linguagens que existem hoje em dia.
A linguagem C foi projectada em 1972 nos laboratórios da Bell, por Brian W. Kernighan e Dennis
Ritchie, deriva do ALGOL 68, e tinha por finalidade permitir a escrita de um sistema operativo (o UNIX),
desenvolvido em 1967 e rescrito em 1973 utilizando a linguagem C.
Rapidez − consegue obter performances semelhantes às obtidas pelo Assembly, usando instruções de alto
nível, isto é, utilizando instruções semelhantes às utilizadas por linguagens como o PASCAL ou o
COBOL mesmo para aceder a mecanismos de mais baixo nível, como os endereços de memória ou a
manipulação de Bits.
Simples − a sua sintaxe é extremamente simples e o número de palavras reservadas, tipos de dados
básicos e operadores é pequeno, reduzindo assim a quantidade de tempo e esforço necessários à
aprendizagem da linguagem.
Alto grau de portabilidade − que permite que os seus programas fonte sejam transportados entre
computadores sem maiores problemas.
É de uso geral − é eficiente tanto para programação de utilitários como para sistemas operativos,
processadores de texto, base de Dados e sistemas aplicativos em geral.
Gera um código executável (objecto) compacto e rápido em relação à maioria das outras linguagens
compiladas, tornando a sua aplicação eficiente para micro-computadores de memória reduzida.
Permite total interacção com o sistema operativo e inserção de código Assembly no programa fonte,
sendo assim , "ilimitada" por software.
Possui uma sintaxe e poucos comandos e um grande número de operadores aritméticos e lógicos
(incluindo operadores para manipulação de bits) que mantêm relação directa com operações básicas dos
processadores.
É uma linguagem estruturada e modular − permite a simplificação do uso das mais modernas técnicas
de programação.
Bibliotecas poderosas - O C é composto por um número muito pequeno de palavras chave, facto que
poderia limitar as suas capacidades caso não fossem as funcionalidades que as bibliotecas adicionais lhe
proporcionam.
Evolução − O C é uma linguagem estável. A evolução das linguagens fez com que o C também evoluísse
no sentidos das Linguagens Orientadas por Objectos, dando origem a uma nova linguagem, o C++, a
qual mantém a sintaxe da linguagem C e permite um conjunto adicional de características -
Hereditariedade, Polimorfismo, Encapsulamento, “Overloading”, etc..
1. Edição do Código Fonte- a escrita do código pelo programador é guardada em ficheiros de texto
com extensão .C
2. Compilação do Programa- não é mais do que a verificação se o programa contém uma sintaxe
correcta. Esta operação é realizada pelo Compilador, e os erros interrompem o processo de
compilação tendo o programador que os corrigir.
Depois de efectuada a compilação, o compilador cria um ficheiro objecto, com o nome igual ao
nome do programa mas de extensão .OBJ (em DOS) ou .O (em UNIX).
3. “Linkagem” dos objectos- É o Linker que torna o ficheiro objecto, obtido através do processo
de compilação em executável e através das bobliotecas (extensões .LIB (em DOS) e .a (em
UNIX).
4. Execução do programa- logo que a “linkagem” termine com sucesso temos disponível um
ficheiro executável, o qual podemos testar para verificar se ele faz o que era suposto fazer. Caso
isso não venha a acontecer é porque o programador se enganou.
Edição
1.1.1.1.1.1.1
Outros programas-fonte
Pré-processamento
a serem incluídos e directivas de
pré-processamento
( # ...)
Prog.C
expandido
Compilação
1.1.1.1.1.1.2
Bibliotecas
Linkagem de funções (*. 0bj)
Programa
PROG. EXE executável
A linguagem C não possui instruções de entrada e saída de dados, logo, estas operações só serão possíveis
com a chamada explícita de funções. Estas funções são fornecidas em cada implementação da linguagem
(compilador).
Estrutura de um programa
O aspecto de um programa C é modular e funcional, sendo o próprio programa principal uma função
main().
Esta forma de apresentação da linguagem facilita o desenvolvimento de programas, pois permite o
emprego de formas estruturadas e modulares.
Todo o programa em C consiste numa ou mais funções. A única função que necessita de estar presente é a
função main( ), pois ela é a primeira função a ser chamada quando a execução do programa começa, mas
pode conter muitas outras.
Esquematicamente temos:
[
[ < tipo >] fun ([< lista de parâmetros >])
[ < declaração de parâmetros > ]
{
Anotações:
[ e ] - significa que o que está no seu interior é facultativo.
< e > - qualquer coisa que pode ser substituído
{ e } - inicializa e termina um bloco de instruções (corresponde ao begin e end)
As chavetas podem conter blocos de instruções elementares (chamados de expressões) ou outros
blocos de instruções. Assim, podem existir blocos de instruções aninhados um dentro de outro.
/* e */ - limitadores de comentários ou observações (podem aparecer em qualquer parte do programa)
fun - Função
Entradas e Saídas
Uma das ideias mais interessantes na filosofia da linguagem C é a inexistência de comandos específicos
para entrada e saída de dados, sendo este processamento efectuado por funções pré-definidas e
armazenadas na biblioteca padrão "stdio.h", esta filosofia difere das outras linguagens de programação:
BASIC possui os comandos INPUT e PRINT ; FORTRAN, PASCAL e COBOL utilizam os comandos
READ e WRITE.
NOTA:
As funções já definidas estão sempre seguidas de parênteses.
EXEMPLO:
#include <stdio.h>
main ()
{
printf ("Primeiro programa em C");
puts ("Primeiro programa em C");
}
A função printf()
A função printf() é uma das muitas que pertencem à biblioteca stdio.h. Esta função converte, formata e
imprime dados, mediante as especificações presentes nos seus parâmetros de entrada. Nos seus
parâmetros de entrada, geralmente a função tem duas partes interligadas entre si:
- a primeira contém texto a imprimir à mistura com caracteres que influenciam a segunda :
formatam-na; isto é, especificam
- a segunda é uma lista de variáveis cujo conteúdo se pretende seja afixado no monitor.
A separação entre a primeira e a segunda é feita por uma vírgula e a separação entre as variáveis também
é feita por um conjunto de vírgulas. Assim sendo, poderemos dizer que a separação entre a primeira parte
e a segunda é feita pela primeira vírgula.
Sendo uma função recebe um conjunto de parâmetros de entrada, para , mediante a sua interpretação
poder realizar a tarefa acometida.
Nos seus argumentos, a função printf() tem o seguinte formato:
Identificadores são nomes dados aos vários elementos de um programa, como variáveis, constantes e
funções. Os identificadores consistem de letras e dígitos e devem obedecer às seguintes regras:
• caracteres permitidos: alfabéticos, numéricos e sublinha _ (“underscore”)
• não começar por algarismo
• maiúsculas ≠ minúsculas => var1 ≠ Var1
• não permitidos nomes reservados: auto, break, case, char, const, continue, default, do, double,
else, extern, ...
• desaconselhado uso de acentuação: ã, ú, ..., ç, ...
• sublinha para separar palavras do mesmo nome
• comprimentos até 32 carateres
• nomes sugestivos
• evitar tudo em maiúsculas (usa-se em constantes)
Existem certas palavras reservadas, chamadas de palavras–chave, que têm significados padrões
predefinidos em C. Essas palavras-chave podem ser usadas apenas para seu propósito predefinido.
Exemplo de algumas palavras-chave.
auto break case char
const continue default
do double else extern
Note-se que, os identificadores devem ter relação com as variáveis, constantes ou funções, isto é, estas
devem ter nomes significativos a fim de tornar a programação de mais fácil compreensão e reduzir
comentários supérfluos. Por exemplo, para uma variável contadora deve escolher-se como identificador
cont, contador ou cont_1, ou algo semelhante.
Tipos de dados
O número de bytes ocupados por cada tipo depende do sistema operativo. Em caso de dúvida pode usar-
se a função sizeof().
EXEMPLO:
Sizeof(int) dará o tamanho do inteiro para o computador em questão.
Qualificadores
Em C existem determinados qualificadores que, aplicados a um certo tipo de dados, originam alterações
no número de bytes ocupados e consequentemente na gama de valores representáveis.
As tabelas seguintes apresentam os qualificadores e os respectivos tipos de dados que os podem utilizar:
O qualificador de tamanho altera o número de bytes utilizados para representar o número e por sua vez a
gama de valores.
O qualificador de sinal apenas desloca a gama de valores. Por omissão, isto é, se nada em contrário fôr
especificado, os tipos de dados int e char têm implícito o qualificador signed. Tal significa que os dados
representáveis podem ser positivos e negativos (têm sinal).
unsigned char
unsigned int
signed char
1
A designação deste tipo como char, não significa que seja destinado unicamente ao armazenamento de carateres imprimíveis ou
não imprimíveis, mas sim pelo facto de na sua representação interna o computador usar 1 byte para esse armazenamento. (Por
razões históricas os carateres usam 1 byte para serem representados). Na verdade o que as variáveis contêm são códigos. O que
estes códigos representam não é aqui mencionado. Um char só será apresentado como um caracter se for especificado
explicitamente que seja traduzido esse número no seu carater ASCII correspondente. Voltaremos ao assunto da formatação
(forma de escrever um número) mais tarde.
signed int
A tabela seguinte representa o tamanho dos tipos de dados após a aplicação dos qualificadores de
tamanho e a gama de valores consequente:
float 4 bytes n. d.
double 8 bytes n. d.
Turbo C
Gamas de valores
Inteiros
Reais
Como se declaram Gama de valores
float ±3,4E-38 a ±3,4E+38
Declaração de variáveis
Uma variável não é mais do que um nome que nós damos a uma determinada posição de memória
(endereços) para guardar/conter um valor de um determinado tipo.
O tipo que lhe está associado indica o nº de bytes que irão ser utilizados para guardar um valor nessa
variável.
Ao declarar-se uma variável estamos a solicitar ao compilador para reservar espaço em memória para a
armazenar e esse espaço passará a ser referenciado através do nome da variável.
Nome Ana
10001
10002 Ana
10003
Morada Leiria
10004 Leiria
10005
10006
Telefone 656869
10007 656869
10008 25
Idade 25 10009
NOTA:
É uma prática tradicional do C, usar letras minúsculas para nomes de variáveis e maiúsculas para nomes
de constantes. Isto facilita a leitura do código.
As variáveis no C devem ser declaradas antes de serem usadas1. A forma geral da declaração de variáveis
é:
<tipo_da_variável> < lista_de_variáveis_separadas_por_vírgula>;
Tipo Variável ;
As variáveis da lista de variáveis terão todas o mesmo tipo e deverão ser separadas por vírgula.
1
Era essa a prática aquando da análise dum problema.
Como o tipo “default ” do C é o int, quando vamos declarar variáveis int com algum dos qualificadores de
sinal ou tamanho, basta colocar o nome do qualificador. Assim basta declarar long para declarar um
long int.
[<qualificador de sinal>] [<qualificador de tamanho<] <tipo_variável> < lista_de_variáveis>;
EXEMPLO:
unsigned long int a, b, c;
unsigned long a, b, c;
declaram duas variáveis do tipo char (ch e letra), uma variavel long int (count), duas variáveis reais: uma
float (pi) e outra um long float (num).
main( )
{
Declaração de variáveis ß aqui
Instrução1;
Instrução2;
}
O primeiro lugar no qual se pode declarar variáveis é no início de um bloco de código. Estas variáveis são
chamadas locais e só têm validade dentro do bloco no qual são declaradas, isto é, só a função à qual ela
pertence sabe da existência desta variável, dentro do bloco no qual foram declaradas.
O segundo lugar onde se pode declarar variáveis é na lista de parâmetros de uma função. Mais uma vez,
apesar de receberem valores externos, estas variáveis são conhecidas apenas pela função onde são
declaradas. Veja o programa abaixo:
#include <stdio.h>
int func1(int );
void main()
{
char condicao;
int i;
/* aqui viria o código da função main*/
func1(i);
/* etc ... */
return;
}
As variáveis condição e i, só existem dentro de main(), isto é são variáveis locais de main().
A variável inteira j é um exemplo de declaração na lista de parâmetros de uma função (a função func1).
As regras que regem onde uma variável é válida chamam-se regras de escopo da variável. Há mais dois
detalhes que devem ser salientados:
• duas variáveis locais não podem ter o mesmo nome.
• duas variáveis locais, de funções diferentes, podem ter o mesmo nome sem perigo algum de
conflito.
Inicialização de variáveis
, ;
Tipo variável variável
, constante =
Isto é importante pois quando o C cria uma variável ele não a inicializa. Isto significa que até que um
primeiro valor seja atribuído à nova variável ela tem um valor indefinido e que não pode ser utilizado para
nada. Nunca presuma que uma variável declarada vale zero ou qualquer outro valor conhecido.
POR EXEMPLO:
char ch='D';
char ch1 = ‘3’;
int count=0;
float pi=3.141;
NOTA:
Em C o sinal de igual, =, é o operador de atribuição.
Constantes
Em C, constantes referem-se a valores fixos guardados em memória que o programa não pode alterar, ou
seja que não podem ser alterados durante a execução do programa.
‘\"’ Aspas
‘\'’ Apóstrofo
Constantes simbólicas
Uma constante simbólica é um nome que substitui uma sequência de caracteres. Uma constante simbólica
permite que um nome apareça no lugar de uma constante numérica, constante caracter ou uma constante
“string”. Quando um programa é compilado, cada ocorrência de uma constante simbólica é substituída
pela sua sequência de caracteres correspondente.
EXEMPLO:
#define PI 3.1415926
Esta instrução permite que todo o programa possa utilizar a palavra PI em vez do valor definido.
Operadores de atribuição
O operador de atribuição do C é o =, como já referido várias vezes. O que ele faz é pegar o valor à direita
e atribuir à variável da esquerda. Além disto ele devolve o valor que ele atribuiu.
EXEMPLO:
int a;
float p;
a=2;
p=1.5;
Atribuições Múltiplas
C permite que se atribua o mesmo valor a muitas variáveis usando atribuições múltiplas num único
comando.
EXEMPLO:
atribuir o valor oito a x, y e z.
x = y = z = 8;
Ordem da atribuição
NOTA:
Em C todos os operadores devolvem o valor da operação efectuada, podendo este ser ou não ser utilizado.
Operadores aritméticos
OPERADOR ACÇÃO
- Subtração
+ Adição
* Multiplicação
/ Divisão
O operador / (divisão) quando aplicado a variáveis inteiras, fornece o resultado da divisão inteira; quando
aplicado a variáveis em ponto flutuante fornece o resultado da divisão "real". O operador % fornece o
resto da divisão de dois inteiros. Assim seja o seguinte trecho de código:
int a = 17, b = 3;
int x, y;
float z = 17.0, z1, z2;
x = a / b;
y = a % b;
z1 = z / b;
z2 = a/b;
a = b^5;
Note que, na linha correspondente a z2, primeiro é feita uma divisão inteira (pois os dois operandos são
inteiros). Somente após efectuada a divisão é que o resultado é atribuído a uma variável float.
O C possui operadores unários e binários. Os unários agem sobre uma variável apenas, modificando ou
não o seu valor, e devolvem o valor final da variável. Os binários usam duas variáveis e devolvem um
terceiro valor, sem alterar as variáveis originais.
A soma é um operador binário pois pega duas variáveis, soma os seus valores, sem alterar as variáveis, e
devolve esta soma. Outros operadores binários são os operadores - (subtração), *, / e %.
O operador - (subtração), como troca de sinal é um operador unário que não altera a variá vel sobre a qual
é aplicado, pois ele devolve o valor da variável multiplicado por -1.
Um caso muito comum em qualquer processamento é operar-se uma variável e armazenar nesta o
resultado obtido. Na linguagem C pode utilizar-se uma forma alternativa de realizar o processamento
anterior, substituindo a fórmula:
EXEMPLO:
Vamos supor que queríamos adicionar 3 à variável x.
Normalmente escreveríamos: x = x + 3;
NOTA:
Quando se utilizam atribuições compostas, é absolutamente necessário que o operador fique
imediatamente junto ao sinal da atribuição.
EXEMPLO SIGNIFICADO
x += 1 x=x+1
y *= 2+3 y = y * (2+3)
a - = b+1 a = a - (b+1)
k /= 12 k = k / 12
r %= 2 r=r%2
Operadores ++ e --
Estes operadores podem ser pré-fixados ou pós-fixados. A diferença é que quando são pré-fixados eles
incrementam e devolvem o valor da variável já incrementada. Quando são pós- fixados eles devolvem o
valor da variável sem o incremento e depois incrementam a variável.
Equivalente a Equivalente a
y = x++ y = ++x
1º - y = x 1º - x++
2º - x++ 2º - y = x
Acontecem duas coisas por esta ordem: Acontecem duas coisas por esta ordem:
EXEMPLO:
int x, y = 5;
x = y++;
Se fosse
x = ++y;
teríamos x a valer 6 e y a valer também 6.
Operador Ação
> Maior do que
== Igual a
!= Diferente de
NOTA :
Em C não existe nenhum tipo específico de dados para armazenar valores lógicos, como em Pascal que
existe o tipo Boolean para receber os valores True e False.
Em C o valor lógico FALSO é representado por 0 (zero) e tudo aquilo que seja diferente de zero
representa o valor lógico VERDADEIRO.
Operadores Lógicos
São designados por operadores lógicos, aqueles que efectuam operações de conjunção (E), disjunção(OU)
ou negação (NÃO) entre expressões de valor lógico.
Os operadores lógicos são:
OPERADOR Significado
|| OR ( OU lógico)
Usando os operadores relacionais e lógicos podemos realizar uma grande gama de testes. A tabela-
verdade destes operadores é dada a seguir:
p q p AND q p OR q
#include <stdio.h>
int main()
{
int i, j;
printf("informe dois números(cada um sendo 0 ou 1): ");
scanf("%d%d", &i, &j);
printf("%d AND %d é %d\n", i, j, i && j);
printf("%d OR %d é %d\n", i, j, i || j);
printf("NOT %d é %d\n", i, !i);
}
EXEMPLO:
No trecho de programa abaixo a operação j++ será executada, pois o resultado da expressão lógica é
verdadeiro:
int i = 5, j =7;
if ( (i > 3) && ( j <= 7) && ( i != j) ) j++;
V AND V AND V = V
NOTA:
Os operadores relacionais && e || são operadores binários, enquanto o operador ! é um operador unário,
sendo aplicado apenas a uma única condição ou expressão.
Expressões
Expressões são combinações de variáveis, constantes e operadores. Quando se escreve uma expressão tem
de se ter em consideração a ordem pela qual os operadores são executados, conforme a tabela de
precedências da linguagem C.
Exemplos de expressões:
Anos=Dias/365.25;
i = i+3;
c= a*b + d/e;
c= a*(b+d)/e;
Quando constantes e variáveis de tipos diferentes são misturadas numa expressão, elas são convertidas a
um mesmo tipo. O compilador C converte todos os operandos no tipo do maior operando ( o que é
chamado de promoção de tipo). Isso é feito operação por operação.
O C avalia uma expressões onde se tem variáveis de tipos diferentes o compilador verifica se as
conversões são possíveis. Se não são, ele não compilará o programa, dando uma mensagem de erro. Se as
conversões forem possíveis ele as faz, seguindo as seguintes regras:
Todos os chars e short ints são convertidos para ints. Todos os floats são convertidos para doubles.
1. Para pares de operandos de tipos diferentes: se um deles é long double o outro é convertido para
long double; se um deles é double o outro é convertido para double; se um é long o outro é
convertido para long; se um é unsigned o outro é convertido para unsigned.
EXEMPLO:
char ch;
int i;
float f;
double d;
double
Tabela de Precedências do C
<< >>
<<= >>=
== !=
&
^
|
&&
||
?
= += -= *= /=
Menor precedência ,
Modeladores (Casts)
Um modelador é aplicado a uma expressão. Ele força a mesma a ser de um tipo especificado. Sua forma
geral é:
(tipo)expressão
UM EXEMPLO:
#include <stdio.h>
int main ()
{
int num;
float f;
num=10;
f=(float)num/7;
printf ("%f",f);
return(0);
}
Se não tivéssemos usado o modelador no exemplo acima o C faria uma divisão inteira entre 10 e 7. O
resultado seria 1 (um) e este seria depois convertido para float mas continuaria a ser 1.0. Com o
modelador temos o resultado correcto.
O printf
Até aqui usámos a função printf de uma forma simplificada para imprimirmos caracteres no monitor.
Nesta secção faremos uma descrição mais completa das suas potencialidades, ainda que de forma não
exaustiva. Esta função converte, formata e imprime os seus argumentos no “standard output” (leia-se
monitor, para o efeito) sob controlo duma string de controlo. Podemos dizer que a função printf
obedece ao seguinte formato:
printf (“ ”, );
Por sua vez, a string de controlo pode conter caracteres comuns e especificadores de conversão.
Os caracteres comuns são os caracteres que queremos que sejam imprimidos no monitor, tal como os
escrevemos na string de controlo.
Os especificadores de conversão começam com o símbolo % e acabam com um dos seguintes caracteres,
chamados caracteres de conversão: d, o, x, u, c, s, e, f ou g. Entre o caracter % e o caracter de conversão
pode haver, por esta ordem:
1. o sinal menos (-): significa encostar à esquerda a variável a imprimir,
2. 0 (zero): preenche com zeros,
3. um conjunto de dígitos que especificam a largura do campo constituído pela variável. O número
convertido será impresso num campo com pelo menos esta largura espeificada ou maior se
necessário. Se o argumento convertido tiver menos caracteres que a largura especificada, será
encostado à direita (a menos que se tenha especificado para encostar à esquerda). O espeço
restante contém normalmente espaços em branco ou zeros, se a largura do campo foi especificada
com um zero no início.
4. um ponto (.): separa a largura do campo do próximo conjunto de algarismos,
5. o próximo conjunto de algarismos: especifica a precisão, isto é: o nº de casas decimais,
tratando-se de uma variável tipo float; ou o número máximo de caracteres, tratando-se de uma
string.
6. um modificador de comprimento (l-long; h-short)
7. o caracter de conversão, com o seguinte significado:
i. d: inteiro decimal
ii. o: inteiro octal
iii. x: inteiro hexadecimal
iv. u: unsigned decimal
v. c: caracter
vi. s: string
vii. f: float. o será considerado um float ou um double e convertido para a notação decimal da
forma [-] mmm.nnnnn. m é a parte inteira da variável. O número de ns é indicado pela
precisão. Por omissão é seis.
viii. e: o resultado será imprimido na notação científica: [-]m.nnnnnnE[+|-]xx. O que está
entre parêntesis rectos é opcional. m é a mantissa, n as casas decimais. Por omissão são
seis.
ix. g: usa %e ou %f (o que for mais curto). Os zeros não significativos não são impressos.
Se o caracter seguinte ao % não for um caracter de conversão, esse caracter será impresso, como caracter
comum.
EXEMPLO:
printf(“% – 0 6 . 3 l d ”, a );
encosta à esquerda
preenche com zeros
largura do campo
nº de casas decimais
modificador de tamanho (long)
caracter de conversão (inteiro (decimal))
variável a imprimir, (declarada como inteiro)
EXEMPLO:
Supomos uma variável do tipo string com o conteúdo “ola”.
A tabela abaixo mostra o resultado no monitor em função da string de controlo.
O scanf
A função scanf é análoga à printf, fornecendo muitas das mesmas facilidades de conversão, mas no
sentido oposto; isto é, recebe dados em vez de os imprimir. Antes de mais esta função faz parar o
programa, e este só prossegue na execução das restantes instruções após introduzidos os dados de entrada
pretendidos e premida a tecla Enter. Scanf lê dados do input standard, (leia-se teclado, para o efeito)
interpreta-os de acordo com o formato especificado pela string de controlo e guarda os resultados nas
variáveis mencionadas no respectivo local.
scanf (“ ”, );
O uso da string de controlo é análogo ao da string de controlo da função printf. Já o nome da(s)
variável(eis) deverá ser ser precedido pelo caracter &. Futuramente abordaremos o significado deste
símbolo no seu contexto.
EXEMPLO
int a;
scanf(“%d”, &a);
Esta última instrução pára a execução do programa ficando à espera da introdução dum dado pelo teclado,
interpreta-o como sendo um inteiro decimal e guarda-o na variável de nome a.
Poderemos usar a mesma instrução para receber do teclado mais que uma variável. Nesse caso,
deveremos especificar tantos caracteres de especificação quantas as variáveis e no teclado espaçar as
variáveis por espaços.
EXEMPLO
scanf(“%d %f”, &i, &y);
Esta última instrução pára a execução do programa ficando à espera da introdução de dados pelo teclado.
A cada espaço encontrado passa para a variável seguinte. Neste caso atribui o conteúdo antes dum espaço
à variável i formatando-a como um inteiro decimal e o conteúdo após o espaço à variável y formatado
como um float.
ESTRUTURAS DE SELECÇÃO
if
O comando if representa uma tomada de decisão do tipo "SE isto ENTÃO aquilo". A sua forma geral é:
if (condição)
bloco de instruções;
A condição do comando if é uma expressão que será avaliada. Se o resultado for zero, o bloco de
instruções não será executado. Se o resultado for qualquer valor diferente de zero o bloco de instruções
será executado. O bloco de instruções pode ser um bloco de código ou apenas uma instrução.
O caso do bloco de instruções ser uma única instrução, não é necessário o uso de chavetas a delimitar
esssa instrução. No entanto se o bloco de código contiver mais que uma instrução é necessário o uso de
chavetas a delimitá-la.
EXEMPLO:
#include <stdio.h>
void main (void)
{
int num;
printf ("Digite um numero: ");
scanf ("%d",&num);
if (num>10)
printf ("\n\nO numero e maior que 10");
if (num==10)
{
printf ("\n\nAcertou!\n");
printf ("O numero e igual a 10.");
}
if (num<10)
printf ("\n\nO numero e menor que 10");
return;
}
No programa acima a expressão num>10 é avaliada e devolve um valor diferente de zero, se verdadeira,
e zero, se falsa.
No exemplo, se num for maior que 10, será escrito no ecrã a frase:
o compilador iria atribuir o valor 10 à variável num e a expressão num=10 iria devolver o 10, fazendo
com que o valor de num fosse modificado e consequentemente a declaração seria sempre executada. Este
problema gera erros frequentes entre iniciantes e, portanto, muita atenção deve ser tomada.
O else
Podemos pensar no comando else como sendo um complemento do comando if. O comando if completo
tem assim a seguinte forma geral:
if (condição)
instrução_1;
else
instrução_2;
A expressão da condição será avaliada. Se ela for diferente de zero a instrução_1 será executada. Se for
zero a instrução_2 será executada. Mas é com base na avaliação da mesma condição que será executada
uma das duas declarações; ou a que está dentro do if, ou a que está dentro do else.
É importante nunca esquecer que, quando usamos a estrutura if - else, estamos a garantir que uma das
duas declarações será executada. Nunca serão executadas as duas e sempre se executará uma delas.
Note-se também que no pedaço de código apresentado não existem delimitadores de início e fim (as
chavetas { } ) para cada instrução. Neste caso não foram necessários pois foram usadas instruções
simples. Caso uma das instruções fosse substituída por duas ou mais, seria obrigatório o uso das chavetas
delimitadoras, como se verá a seguir.
O programa anterior pode ser alterado utilizando-se a estrutura if –else, o que resulta no seguinte.
EXEMPLO:
#include <stdio.h>
void main (void)
{
int num;
printf ("Digite um numero: ");
scanf ("%d",&num);
if (num==10)
{
printf ("\n\nAcertou!\n");
printf ("O numero e’ igual a 10.\n");
}
else
{
printf ("\n\nErrou!\n");
printf ("O numero e diferente de 10.\n");
}
return;
}
O if-else-if
A estrutura if – else - if é apenas uma extensão da estrutura if-else. A sua forma geral pode ser escrita
assim:
if (condição_1)
instrução_1;
else
if (condição_2)
instrução_2;
else
if (condição_3)
instrução_3;
.
.
else
if (condição_n)
instrução_n;
A estrutura acima funciona da seguinte maneira: o programa começa a testar as condições, começando
pela condição_1 e continua a testar até que encontre na condição uma expressão cujo resultado seja
diferente de zero. Nesse caso será executada a declaração correspondente 1 . Só uma declaração será
executada, ou seja; só executa a declaração equivalente à primeira condição que for diferente de zero.
EXEMPLO:
#include <stdio.h>
void main ()
{
int num;
if (num>10)
printf ("\n\nO numero e’ maior que 10");
else if (num==10)
{
printf ("\n\nAcertou!\n");
printf ("O numero e’ igual a 10.");
}
else
printf ("\n\nO numero e’ menor que 10");
return;
}
O Comando switch
O comando if - else e o comando switch são os dois comandos de tomada de decisão. Sem dúvida
alguma, o mais importante dos dois é o if, mas o comando switch tem aplicações valiosas.
Não esquecer que devemos usar o comando certo no local certo. Isto assegura um código limpo e de fácil
entendimento. Vejamos então as características do comando switch.
1
Por declaração, entenda-se aqui que poderá ser uma instrução simples ou um conjunto de instruções. Neste último caso, não
esquecer o uso das chavetas delimitadoras.
O comando switch é próprio para comparar o valor de um inteiro em relação a conjunto de constantes
inteiras pré-estabelecidas e fazer a execução do programa saltar directamente para a única declaração que
executará, sem ter de testar todas as condições intermédias. A sua forma geral é:
Podemos fazer uma analogia entre o switch e a estrutura if-else-if apresentada anteriormente. O switch
testa a variável e executa a declaração cujo case corresponda ao valor actual da variável. A declaração
default é opcional e será executada apenas se a variável, que está sendo testada, não for igual a nenhuma
das constantes.
O comando break faz com que o switch seja interrompido assim que uma das declarações seja executada.
Mas ele não é essencial ao comando switch. No entanto, se não for utilizado, as restantes cases, serão
todas executadas, o que nas maior parte das vezes não interessa.
Veremos agora um exemplo de uso do comando switch.
EXEMPLO:
#include <stdio.h>
void main (void)
{
int num;
printf ("Digite um numero: ");
scanf ("%d",&num);
switch (num)
{
case 9:
printf ("\n\nO numero e igual a 9.\n");
break;
case 10:
printf ("\n\nO numero e igual a 10.\n");
break;
case 11:
printf ("\n\nO numero e igual a 11.\n");
break;
default:
printf ("\n\nO numero nao e nem 9 nem 10 nem 11.\n");
}
return;
}
CICLOS
Inicialmente considerámos um programa como uma sequência linear de instruções a serem executadas.
No capítulo anterior analisámos situações em que certas instruções podem ou não serem executadas,
segundo se verifiquem ou não certas condições. O fluxo do programa deixou assim de ser linear. Vamos
neste capítulo abordar um conjunto de estruturas que alteram também a linearidade do fluxo do programa,
desta feita em situações em que existe carácter repetitivo dum certo conjunto de instruções bem definido.
while()
A estrutura while, também chamada ciclo while, faz executar um conjunto de instruções, chamado bloco
de instruções, enquanto uma determinada condição for verdadeira. A sua sintaxe é a seguinte:
while(condição)
{
instrução_1;
instrução_2;
instrução_3; bloco de instruções ou corpo da estrutura while
.......
instrução_n;
}
Dentro do bloco de instruções deverá haver, uma instrução que a certa altura altere o valor da condição;
caso contrário teríamos um ciclo infinito, pois a condição seria sempre verdadeira, caso o fosse na
primeira vez que o bloco fosse executado.
A cada execução do bloco chama-se iteração.
Se o bloco de instruções for constituído por uma única instrução, não será necessário o uso de chavetas
como delimitadoras de início e fim de bloco.
Nota: o cabeçalho da estrutura while, não termina com ponto e vírgula, à semelhança das estruturas if() e
switch()
EXEMPLO:
Escrever um programa que coloque no écran os primeiros dez números inteiros, mudando de linha entre
cada número:
#include <stdio.h>
void main(void)
{ int i;
i=1;
while(i<=10)
{
printf(“%d\n”, i);
i=i+1;
}
}
EXEMPLO:
Escrever um programa que ponha no monitor a tabuada do 5, alinhada como em baixo à direita.
5* 1= 5
#include <stdio.h>
5* 2=10
void main(void) 5* 3=15
{ int n=1; 5* 4=20
while(n<=10) 5* 5=25
{ 5* 6=30
printf(“5*%2d=%2d\n”, n, 5*n); 5* 7=35
5* 8=40
n++;
5* 9=45
}
5*10=50
}
for()
1 4
for (inicialização(ões); condição; pós-instrução(ões))
{ 2
instrução_1;
3
instrução_2;
instrução_3; bloco de instruções ou corpo da estrutura for
.......
instrução_n;
}
EXEMPLO:
O anterior programa de escrever na vertical os dez primeiros números inteiros, feito com um ciclo while,
pode assim escrever-se usando o for.
...
int i;
for(i=1; i<=10; i=i+1)
{
printf(“%d\n”,i);
}
Neste caso o ciclo for só tem uma instrição no seu bloco de instruções; as chavetas seriam dispensadas,
por esse motivo.
EXEMPLO:
Escrever um programa que calcule a soma e o produto dos dez primeiros números inteiros.
...
int soma;
int produto;
for(soma=0, n=1, produto=1; n<=10; n=n+1)
{
soma=soma+n;
produto=produto*n
}
printf(“adicoes: %d; multiplicacoes: %d”, soma, produto);
Se observarmos com atenção as diferenças entre um ciclo for e um ciclo while, podemos verificar que um
pode ser substituído pelo outro de acordo com as equivalências a seguir ilustradas.
do ... while
O ciclo do ... while é muito semelhante aos anteriores, excepto que neste a condição é verificada no fim
do bloco de instruções, o que significa que qualquer que seja o valor da condição, é sempre executado o
conjunto de instruções que o constituem.
Este ciclo tem a seguinte sintaxe:
do
{
instruções;
}
while(condição)};
Uma vez que executa sempre pelo menos uma vez o bloco de instruções, é uma estrutura muito
apropriada à protecção de dados de entrada. O seguinte exemplo mostra uma aplicação deste ciclo em tal
situação.
EXEMPLO:
Programa que apresenta um menu com as opções Clientes, Fornecedores , Encomendas e Sair, permitindo
ao utilizador escolher uma delas. Só sai do ciclo quando escolher Sair.
#include <stdio.h>
#include <conio.h>
void main(void)
{ char opcao;
do
{ clrscr();
printf("\t MENU PRINCIPAL\n");
printf("\n\n\t\tClientes");
printf("\n\n\t\tFornecedores");
printf("\n\n\t\tEncomendas");
printf("\n\n\t\tSair");
printf("\n\n\nA sua opcao e': ");
scanf("%c",&opcao);
fflush(stdin);
switch(opcao)
{ case 'c':
case 'C':
printf("A sua opcao foi CLIENTES\n");
break;
case 'f':
case 'F':
printf("A sua opcao foi FORNECEDORES\n");
break;
case 'e':
case 'E':
printf("A sua opcao foi ENCOMENDAS\n");
break;
case 's':
case 'S':
break;
default:
printf("opcao INVALIDA!!!\n");
} /* fecha switch */
getch();
} /* fecha ciclo */
while(opcao != 's' && opcao != 'S');
} /* fecha main() */
Neste programa aparece pela primeira vez a função fflush(), utilizando como parâmetro de entrada
stdin. É uma função que limpa os caracteres que existam no buffer do teclado. Doravante utulizá-la-emos
sempre que utilizemos a função scanf().
break
à semelhança do que já conhecemos da instrução break utilizada na estrutura switch(), também quando
utilizada numa estrutura cíclica, o que faz é terminar a execução completa de toda a estrutura, isto é, não
será executada mais nenhuma instrução do ciclo, passando a ser executada a primeira instrução a seguir à
chaveta de fecar o ciclo.
continue
A instrução cotinue usada dentro dum ciclo é semelhante à break, excepto que em vez de terminar todo o
ciclo só termina a iteração em que se encontra. Isto é; não executa mais nenhuma instrução do bloco de
instruções, mas vai de novo testar a condição e se ela for verdadeira, continuará a executar o bloco de
instruções.
Ciclos encadeados
1
1 2
1 2 3
1 2 3 4
...
1 2 3 4 5 6 7 8 9 10
Como se constata existem dois padrões de repetição. Por um lado há dez linhas a imprimir, mas por outro
em cada linha há também um padrão a repetir, embora dependendo de linha para linha, pois enquanto na
primeira só um número é escrito, na última são dez.
Um programa solução seria:
No caso do uso de ciclos encadeados, há a notar que um ciclo não se pode estender para além do ciclo a
que pertence, isto é, se está dentro doutro, terá de estar completamente dentro do outro.
Um ciclo interno pode alterar as variáveis do externo e vice-versa.
Quando no ciclo for não é colocada qualquer condição, tudo se passa como se lá estivesse uma condição
verdadeira.
Como se termina então um ciclo infinito? Em programação termina-se colocando no seu bloco de
instruções a avaliação de uma condição seguida duma instrução break.
FUNÇÕES
ADENDA
Quanto à passagem de parâmetros, as funções podem ter quatro configurações, conforme passem ou não
parâmetros de entrada ou de saída. Assim, poderíamos agrupá-las em quatro “configurações”:
Devolve parâmetro? Recebe parâmetros? Cabeçalho da definição da função com nome nome_funcao, viria:
Não Não void nome_funcao (void)
<declaração de variaveis>
Neste campo, usam-se (declaram-se!) variáveis quando esta função recebe valores da função que chama
esta.
Se porventura, esta função receber valores unicamente pelo teclado, ou não receber qualquer valor (nem
por teclado, nem da função que a chama), usa-se void.
A função que chama esta poderá ser a função main(), ou outra qualquer, já que qualquer função pode
chamar qualquer função.
<tipo devolvido>
Menciona-se aqui um tipo de dados quando esta função devolve um valor à função que chama esta.
Se porventura esta função “devolver” valores unicamente para o monitor (o que não costuma chamar-se
“devolver”), ou se não devolver qualquer valor para a função que a chamou, usa-se “void”.
Nota: Quando se diz devolver valores para a função que chama esta, estamos sempre a ter em mente a
instrução return.
Resumindo… O uso da instrução return requer que neste campo se mencione um tipo de dados.
Situação 1
Definição duma função que recebe do teclado dois valores, os soma e imprime o resultado.
void funcao(void)
{ float a, b, c;
scanf(“%d%d”, &a, &b);
c=a+b;
printf(“%f”, c);
}
Poderia ser incluída no seguinte programa (com declaração e chamada a esta função acabada de definir).
#include <stdio.h>
void funcao(void); //declaracao
void main (void)
{
funcao(); //chamada
}
Situação 2
Definição duma função que recebe da função chamadora dois valores, os soma e imprime o
resultado.
Poderia ser incluída no seguinte programa (com declaração e chamada a esta função acabada de definir).
#include <stdio.h>
void funcao(float, int); //declaracao
void main (void)
{ float a, b;
scanf(“%d%d”, &a, &b); //chamada
funcao(a, b);
}
Situação 3
Definição duma função que recebe do teclado dois valores, os soma e devolve o resultado.
float funcao(void)
{ float a, b, c;
scanf(“%d%d”, &a, &b);
c=a+b;
return c;
}
Poderia ser incluída no seguinte programa (com declaração e chamada a esta função acabada de definir).
#include <stdio.h>
float funcao(void); //declaracao
void main (void)
{ float z;
z=funcao(); //chamada
printf(“%f”, z);
}
Situação 4
Definição duma função que recebe dois valores, os soma e devolve o resultado.
Poderia ser incluída no seguinte programa (com declaração e chamada a esta função acabada de definir).
#include <stdio.h>
float funcao(float a, int b); //declaracao
void main (void)
{ float x, y, z;
scanf(“%d%d”, &x, &y);
z=funcao(); //chamada
printf(“%f”, z);
}