You are on page 1of 87

Universidade Estadual do Rio Grande do Sul - UERGS

Unidade de Guaíba
Curso de Engenharia em Sistemas Digitais

Apostila da Disciplina de

Algoritmos e Programação

Prof. João Carlos Gluz

Guaíba, 2003
UERGS - Algoritmos e Programação Sumário

Sumário
CAPÍTULO 1 - INTRODUÇÃO ..................................................................................................................... 1
1.1. UMA BREVE HISTÓRIA DA COMPUTAÇÃO ............................................................................................. 1
1.2. CONCEITOS ELEMENTARES DE COMPUTAÇÃO E INFORMÁTICA............................................................. 7
1.3. PROGRAMAS, ALGORITMOS E PROGRAMAÇÃO ................................................................................... 10
1.4. ESTRUTURA DE UM PROGRAMA .......................................................................................................... 12
1.5. LINGUAGENS DE PROGRAMAÇÃO........................................................................................................ 13
1.6. TRADUÇÃO DE LINGUAGENS............................................................................................................... 15
1.7. CARGA E EXECUÇÃO DE PROGRAMAS................................................................................................. 16
CAPÍTULO 2 - ALGORITMOS E LINGUAGEM C................................................................................. 18
2.1. ALGORITMOS ...................................................................................................................................... 18
2.2. FORMAS DE DESCRIÇÃO DE ALGORITMOS .......................................................................................... 19
2.3. A NOÇÃO DE PROGRAMA.................................................................................................................... 19
2.4. A LINGUAGEM C ................................................................................................................................ 20
CAPÍTULO 3 - ELEMENTOS BÁSICOS DE C......................................................................................... 22
3.1. CONSTANTES ...................................................................................................................................... 22
3.2. VARIÁVEIS .......................................................................................................................................... 22
3.3. TIPOS DE DADOS PRIMITIVOS ............................................................................................................. 23
3.4. COMENTÁRIOS .................................................................................................................................... 24
3.5. EXPRESSÕES ARITMÉTICAS ................................................................................................................. 24
3.6. EXPRESSÕES RELACIONAIS ................................................................................................................. 27
3.7. EXPRESSÕES LÓGICAS ........................................................................................................................ 28
3.8. TIPOS DE DADOS DAS EXPRESSÕES ..................................................................................................... 29
3.9. EXPRESSÕES (COMANDOS) DE ATRIBUIÇÃO ....................................................................................... 30
CAPÍTULO 4 - COMANDOS SIMPLES E BLOCO DE COMANDOS .................................................. 32
4.1. COMANDO DE ATRIBUIÇÃO E SEQUÊNCIA DE COMANDOS .................................................................. 32
4.2. COMANDOS DE ENTRADA E SAÍDA ...................................................................................................... 33
4.2.1. A Função scanf ......................................................................................................................... 33
4.2.2. A Função printf......................................................................................................................... 34
4.2.3. Comandos de Formatação de scanf e printf ............................................................................. 35
4.3. BLOCOS DE COMANDO ........................................................................................................................ 37
4.4. UM PROGRAMA C BÁSICO .................................................................................................................. 37
CAPÍTULO 5 - COMANDOS DE SELEÇÃO............................................................................................. 40
5.1. COMANDO CONDICIONAL IF................................................................................................................ 40
5.2. COMANDO DE SELEÇÃO IF ... ELSE ...................................................................................................... 41
5.3. COMANDO DE SELEÇÃO SWITCH ......................................................................................................... 42
5.4. USANDO COMANDOS CONDICIONAIS .................................................................................................. 43
CAPÍTULO 6 - COMANDOS DE REPETIÇÃO ........................................................................................ 47
6.1. COMANDO DE REPETIÇÃO WHILE ........................................................................................................ 48
6.2. COMANDO DE REPETIÇÃO FOR ............................................................................................................ 50
6.3. COMANDO DE REPETIÇÃO DO...WHILE ................................................................................................ 52
CAPÍTULO 7 - FUNÇÕES E MÓDULOS .................................................................................................. 54
7.1. DECLARAÇÃO DE FUNÇÃO .................................................................................................................. 54
7.2. ESTRUTURA DE UM PROGRAMA .......................................................................................................... 56
7.3. CHAMADA DE FUNÇÃO ....................................................................................................................... 57
7.4. CABEÇALHOS DE FUNÇÃO................................................................................................................... 58
7.5. MÓDULOS ........................................................................................................................................... 58

ii Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Sumário

7.6. PARÂMETROS, VARIÁVEIS LOCAIS E VARIÁVEIS GLOBAIS ................................................................. 61


7.7. RECURSIVIDADE ................................................................................................................................. 61
CAPÍTULO 8 - ARRAYS, STRINGS E MATRIZES................................................................................... 63
8.1. DECLARAÇÃO DE VETORES................................................................................................................. 64
8.2. USO DOS VETORES .............................................................................................................................. 64
8.3. CADEIAS DE CARACTERES (STRINGS).................................................................................................. 65
8.4. DECLARAÇÃO DE MATRIZES ............................................................................................................... 68
8.5. USO DAS MATRIZES ............................................................................................................................ 69
CAPÍTULO 9 - APONTADORES (OU PONTEIROS) .............................................................................. 71
9.1. DECLARAÇÃO DE VARIÁVEIS APONTADORAS ..................................................................................... 72
9.2. INICIALIZAÇÃO DE APONTADORES ...................................................................................................... 72
9.3. USO DOS APONTADORES ..................................................................................................................... 74
9.4. MANIPULAÇÃO DE APONTADORES ...................................................................................................... 74
9.5. APONTADORES COMO PARÂMETROS DE FUNÇÕES .............................................................................. 75
CAPÍTULO 10 - ESTRUTURAS (STRUCTS)............................................................................................. 78
10.1. DECLARAÇÃO DE ESTRUTURAS ...................................................................................................... 78
10.2. UTILIZAÇÃO DAS ESTRUTURAS ...................................................................................................... 80
10.3. APONTADORES PARA ESTRUTURAS ................................................................................................ 81
BIBLIOGRAFIA ............................................................................................................................................ 83

iii Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Sumário

Lista de Figuras
Figura 1 -Compilação de um Programa em C ...................................................................... 38
Figura 2 - Um Vetor com n Posições ................................................................................... 63
Figura 3 - Uma Matriz com m x n Posições ......................................................................... 63
Figura 4 - Apontador para Variável...................................................................................... 71
Figura 5 - Formato de Variáveis de Tipo struct na Memória ............................................... 79

Lista de Tabelas
Tabela 1 - Exemplos de Constantes...................................................................................... 22
Tabela 2 - Operações Aritméticas Binárias .......................................................................... 25
Tabela 3 - Operações Aritméticas Unárias ........................................................................... 26
Tabela 4 - Operadores Relacionais....................................................................................... 27
Tabela 5 - Operadores Lógicos............................................................................................. 28
Tabela 6 - Tabela Verdade do NOT (!) ................................................................................ 28
Tabela 7 - Tabela Verdade do AND(&&) e do OR(||). ........................................................ 29
Tabela 8 - Expressões Abreviadas........................................................................................ 31
Tabela 9 - Caracteres Especiais do printf ............................................................................ 35
Tabela 10 - Comandos de Formatação do scanf e printf .................................................... 36

iv Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

Capítulo 1
Introdução

Antes de iniciar a aprendizagem das técnicas e conceitos envolvidos com a criação de


algoritmos e programas é importante saber claramente quais são os conceitos básicos da
Ciência da Computação (ou da Informática, que pode ser considerado como sinônimo de
Ciência da Computação).

Este texto introdutório tem como objetivo apresentar de forma simples, lógica e coerente
como a Ciência da Computação está sistematizada e organizada como corpo de
conhecimento humano. Neste contexto também será apresentado o histórico desta ciência,
sendo esclarecida a relação da computação com outras disciplinas do pensamento humano e
a fundamentação da informática em termos de conceitos de outras áreas.

1.1. Uma Breve História da Computação


A computação tem uma longa história por trás de si. Na verdade, pode-se dizer que o
objetivo de se conseguir efetuar cálculos aritméticos de uma forma automatizada e
mecânica vem sendo perseguido há milhares de anos. De uma forma geral a atividade de
executar uma grande quantidade repetitiva de cálculos aritméticos não é apenas uma tarefa
estafante mas também uma atividade muito sujeita a erros, devido a dificuldade normal que
nós, seres humanos, temos em seguir uma sequência excruciante e repetitiva de ações
(geralmente se diz, justamente, que este tipo de tarefa é desumano). Sendo assim um
mecanismo automático que efetuasse uma boa parte destes cálculos não apenas facilitaria a
tarefa mas também reduziria em muito a chance de erro no final das contas.

Por exemplo, a própria palavra cálculo, que deriva do latin calculus, originalmente
designava as pequenas pedras usadas para contar e manter o registro de operações de soma
quando dispostas sobre sulcos escavados no chão, ou seja, a própria etimologia da palavra
cálculo já aponta para uma espécie de mecanismo mecânico de cálculo, uma espécie de
ábaco primitivo.

São justamente os ábacos que podem ser classificados como os primeiros dispositivos de
“computação” automatizada. O mais antigo ábaco data de aproximadamente 3500 AC
proveniente da antiga Mesopotâmia (no atual Iraque). Em torno de 2600 AC o ábaco surge
na China e evoluiu rapidamente, sendo posteriormente adotado no Japão com o nome de
Sokoban.

Os ábacos permitiram um grande avanço na execução de repetitivas tarefas de cálculo. Os


resultados que se pode alcançar com o uso de um ábaco ainda hoje podem ser
surpreendentes mesmo quando comparados com a moderna tecnologia das calculadoras

1 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

eletromecânicas ou eletrônicas. Um usuário proficiente de ábaco ainda pode conseguir


efetuar sequências de cálculos de soma e subtração mais rapidamente do que um usuário
não muito experiente de uma calculadora.

Durante muito tempo este foi o avanço principal no que se poderia denominar de
“computação” ou cálculo automatizado. Outro avanço de igual magnitude somente iria
ocorrer no século XVII de nossa era quando o matemático, físico e filósofo francês Blaise
Pascal criou, no ano de 1648, uma máquina automática de calcular, que era um dispositivo
mecânico que simulava o funcionamento de um ábaco através de rodas dentadas. Esta
máquina de calcular, que é a “mãe” de todas as calculadoras mecânicas ou eletro-mecânicas
realizava operações de soma e subtração mostrando o resultado numa espécie de “display”
ou visor composto de uma série de janelinhas.

A máquina de calcular de Pascal, efetuava apenas somas e subtrações aritméticas de


números inteiros tendo sendo muito útil em diversas atividade humanas como o comércio, a
administração, a contabilidade, etc. Porém para os cálculos mais complexos envolvendo
não apenas somas e subtrações de inteiros mas multiplicações, divisões e potenciações de
números reais, que estavam sendo necessários pela crescente matematização que estava
ocorrendo na Física Natural, a máquina de Pascal não era suficiente. Entretanto, um auxílio
para ajudar nestes cálculos já estava a caminha com a criação, em 1650, por Partridge da
régua de cálculo, baseada nas tabelas de logaritmos naturais criadas pelo matemático
escocês John Napier no final do século XVI.

O interessante em relação a estes dois dispositivos, a máquina de calcular mecânica e a


régua de cálculo, é ambos permaneceram em uso, com mudanças relativamente menores,
até meados do século XX, cada uma ainda relacionada as atividades que ajudaram a
avançar: a máquina de calcular transmutada em caixa registradora, ferramenta
indispensável ao comércio e a contabilidade (e a cobrança de impostos), e a régua de
cálculo também como instrumento indispensável da engenharia mecânica e da cívil, ou
seja, instrumento de áreas de conhecimento que podem ser consideradas como aplicações
da física moderna.

A partir daí os avanços começaram a se acelerar. Em 1672 foi desenvolvida, pelo famoso
matemático alemão Gottfried Wilhelm von Leibnitz (co-criador com Newton do Cálculo
Integral), uma máquina calculador “universal”, que efetuava operações soma, subtração,
multiplicação, divisão e extração da raiz quadrada. O trabalho de Leibnitz não foi a partir
do zero, tendo sido essencialmente uma aprimoramento da máquina de calcular de Pascal.

No início do século XIX ocorreram importantes avanços no conceito de “programação”


(este conceito realmente começou a aparecer na sua forma moderna). O ponto interessante
destes avanços, é que o precursor de todos os dispositivos “programáveis” modernos, não é
uma das máquinas de calcular em voga na época, mas um outro dispositivo mecânico
totalmente desvinculado da atividade de cálculo: um simples tear. Em 1801, Joseph Marie
Jacquard construiu na Inglaterra um tear automático cuja “programação” era feita através
de cartões (de madeira) perfurados. A “programação” deste tear controlava justamente
como seria a confecção do tecido e seus desenhos. Este tear pode ser considerada a primeira
máquina programável da história.

2 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

A seguir e ainda na Inglaterra, Charles Babbage, matemático e professor da Universidade


de Cambridge, começou uma série de desenvolvimentos importantes que viriam a culminar
no projeto do que se poderia chamar de verdadeiro “ancestral” de todos os computadores
modernos. O primeiro destes desenvolvimentos foi o projeto e construção, em 1822, da
Máquina Diferencial, que era um dispositivo mecânico sofisticado que permitia o cálculo
de tabelas de números úteis a marinha comercial e de guerra (algo de extrema importância
para a Inglaterra, maior potência naval dos séculos XVIII e XIX). Esta máquina que chegou
a ter um protótipo construído, executava um algoritmo simples e fixo de diferenças finitas
por polinômios para calcular suas tabelas de funções.

Avançando esta idéia de forma a permitir a programação de qualquer tipo de função


desejada, Babbage começou, em 1833, a projetar o ancestral dos computadores: a Máquina
Analítica. Esta máquina nunca chegou a ser construída devido as dificuldades tecnológicas
de fabricação de componentes mecânicos da época, porém o seu projeto e as suas idéias
formam as base práticas da computação moderna. Por exemplo, além de ser uma máquina
programável, dispondo de uma verdadeira “linguagem” ou código de programação
(baseado em formalismos matemáticos), o dispositivo de Babbage teria uma “arquitetura”
organizada de uma forma muito similar aos computadores modernos: a Máquina Analítica
seria dividida em quatro subcomponentes ou unidades básicas:

• a unidade de entrada de dados: uma leitora de cartões perfurados


• a unidade de saída de dados: uma impressora e perfuradora de cartões
• um “silo” ou unidade de armazenagem: uma memória capaz de guardar até 1000
números de 50 algarismos cada um
• e finalmente um “moinho” ou engenho que executaria um programa de cálculo
sobre os dados de entrada, colocando os resultados na unidade de saída e usando
a armazenagem para guardar resultados intermediários. O programa seria
fornecido ao engenho por uma série de cartões perfurados, de maneira similar ao
tear de Jacquard.

É interessante observar que estas quatro unidades, continuam a ser considerados, do ponto
de vista conceitual, os elementos mais básicos de qualquer tipo de computador.

Mesmo que a máquina nunca viesse a ser construída, Babbage precisava demonstrar, pelo
menos em teoria, que a sua programação seria viável. Para tanto ele contratou Ada
Augusta Lovelace, condessa de Lovelace e filha do famoso poeta Lord Byron. Ada
Lovelace deu conta do recado, codificando os primeiros programas de cálculo numérico de
funções. Estes códigos eram instruções reais a serem carregadas na máquina de Babbage e
tinha uma forma muito similar a códigos em linguagem de máquina modernos, sendo assim
Ada Lovelace deve ser considerada, de uma forma bem realista, como sendo a primeira
programadora ou programador de computadores em toda a história.

Em termos de desenvolvimentos práticos, não ocorreram muitas outras coisas em todo o


século XIX, exceto pelo desenvolvimento em torno de 1885 de máquinas de tabulação de
dados automáticas que usavam cartões de dados perfurados de papel já em formato
moderno. Estas máquinas, com seus respectivos cartões, foram criadas por Herman
Hollerith, um (ex)funcionário do departamento de Recenseamento dos Estados Unidos,

3 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

extremamente preocupado com o fato de que o censo executado a cada 10 anos, tinha
levado, na sua última edição de 1870, exatos dez anos para ser tabulado e somado. Hollerith
criou uma companhia, a Tabulating Machines Company, que 1924 se juntou a outras
empresas para se transformar na atual International Business Machines.

A relativa falta de avanços tinha a ver com as limitações da tecnologia da época: as


máquinas diferenciais e de tabulação realmente atingiram os limites da tecnologia mecânica
(a máquina analítica, que foi apenas um projeto inconcluso, ultrapassou de longe estes
limites). Outros avanços práticos teriam que esperar um avanço tecnológico real, um
verdadeiro salto que deixaria de lado as limitações dos dispositivos mecânicos e abriria as
portas para as capacidades incríveis das novas tecnologias baseadas na eletricidade e na
eletrônica.

Entrementes, houveram avanços significativos no processo de desenvolvimento teórico da


computação. Em 1854, George Boole, matemático inglês, com a publicação do livro
“Investigation of the Laws of Thought” estabeleceu as bases da Álgebra de Boole ou
Álgebra Booleana, que fornece, entre outras coisas, o fundamento matemático da teoria
dos circuitos digitais modernos e, portanto, é uma das base teóricas da computação. O
trabalho de Boole, também não foi um desenvolvimento individual, tendo sido baseado em
trabalhos anteriores do seu amigo pessoal Augustos De Morgan, que desde a década de
1830 vinha trabalhando com os conceitos da álgebra simbólica e da lógica formal.

É importante salientar que esta visão de que os desenvolvimentos de Boole e De Morgam


formam uma das bases teóricas da computação é uma visão moderna, com uma perspectiva
um tanto “invertida” em relação ao trabalho destes matemáticos. O trabalho deles, em
conjunto com o de um sem-número de outros matemáticos no período, era parte de um
imenso esforço empreendido no final do século XIX e início do século XX para se definir
uma fundamentação teórica absolutamente precisa da própria Matemática, incluindo todas
as suas subdivisões: a Aritmética, o Cálculo, a Álgebra, a Lógica e demais áreas. É dentro
deste processo que estes desenvolvimentos devem ser encarados, entretanto, o fato curioso
neste processo é que todos os resultados positivos no sentido da formalização precisa, da
simbolização e da aritmetização da matemática permitiram, posteriormente, uma utilização
direta como bases formais do processo de cálculo automático ou de computação.

Dos vários desenvolvimentos importantes que ocorreram no período, vinculados àquele


tema maior de fundamentação da matemática, um se destaca no que tange ao impacto
ocasionado na Ciência da Computação: o trabalho, publicado em 1936, pelo grande
matemático inglês Alan Turing que lançou as bases da Teoria Matemática da
Computação. De uma forma simples, neste trabalho Turing criou uma máquina “virtual”,
existente apenas no pensamento, que poderia executar qualquer tipo de computação que se
pudesse imaginar. Precisando um pouco mais: se um problema tiver uma solução que possa
ser calculada automaticamente, ou seja, resolvida através de um programa composto de
pequenos passos, cada passo precisamente definido, então obrigatoriamente deve existir
uma máquina destas para representá-lo. Do ponto de vista técnico este resultado nunca foi
contradito: todo e qualquer computador moderno, por mais avançado, sofisticado, veloz ou
capaz que tenha sido criado pode ser representado por uma máquina de Turing, que é o
nome atribuído a estes computadores teóricos universais. Da mesma forma todo e qualquer

4 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

programa em qualquer linguagem também pode ser representado por alguma máquina de
Turing. Apenas por curiosidade, em relação ao esforço de fundamentação matemática, esta
proposição define uma tese, denominada de Tese de Church-Turing, que diz que tudo que
pode ser computável, pode ser computável por uma máquina de Turing. Esta declaração,
embora seja normalmente considerada verdadeira (quase uma tautologia por muitos) é
definida como uma tese por ser impossível de ser “demonstrada”: tudo depende de quais
serão as operações básicas (de quais serão os menores passos) usados nesta computação.
Em geral, até hoje, não parece válido assumir que existem passos menores diferentes do
que àqueles já definidos por Turing, sendo assim parece não fazer sentido pensar na
existência de outros tipos de “computações”, mas nunca se sabe...

Voltando ao desenvolvimento tecnológico, o início do século XX foi pródigo em


desenvolvimentos práticos da computação, acompanhando o próprio desenvolvimento
acelerado das tecnologias de projeto e construção de dispositivos eletro-mecânicos,
elétricos e eletrônicos.

Em 1937, Howard H. Aiken da Universidade de Harvard, começou a construir o primeiro


computador eletro-mecânico baseado em relés e engrenagens, seguindo as idéias de
Babbage. Este computador, denominado de MARK I, foi construído com o apoio da IBM e
concluído em 1944. Ele possuia unidades de entrada de dados, memória principal, unidade
de controle e aritmética e unidade de saída. Esta máquina, que chegou a ser usada por
pouco tempo, foi rapidademente deixada de lado porque o desenvolvimento da eletrônica e
dos computadores eletrônicos obsoletou os computadores eletro-mecânicos.

Já em 1940 os professores John W. Mauchly e J. Presper Eckert Jr. construiram, com o


financiamento do Exército dos Estados Unidos, o primeiro computador eletrônico: o
ENIAC (Electronic Numerical Integrator and Calculator). Este computador construído na
Universidade da Pensylvania era 1000 mais rápido que o MARK I, mas pesava apenas a
metade (pesava “apenas” 30 toneladas contra as 70 toneladas do MARK I). O ENIAC, cujo
projeto de construção foi aceito como parte do “esforço de guerra”, somente foi concluído
após a guerra terminar, em 1946. Apesar disso o ENIAC lançou as bases da computação
moderna: não só mostrou a viabilidade destes dispositivos, como também demonstrou o
poder e velocidade de cálculo que se poderiam alcançar. Além disso vários pesquisadores
importantes posteriormente para a computação, como John V. Atanasoff e John von
Neumann, começaram os seus estudos na área justamente trabalhando com o pessoal do
projeto do ENIAC.

Embora tenham existido alguns computadores eletrônicos ou parcialmente eletrônicos que


antecederam o ENIAC, este último pode ser considerado tranquilamente como o antecessor
dos computadores modernos, principalmente em razão de sua “visibilidade” e da
publicação dos seus resultados. Antecessores como o COLOSSUS que foi o primeiro
computador eletrônico da História, construído já em 1943 na Inglaterra com o trabalho do
grupo liderado pelo matemático Alan Turing permaceram em segredo até a década de 70,
perdendo, portanto, a chance de influenciar o desenvolvimento da computação. Da mesma
forma o trabalho do professor Zuse na Alemanha Nazista nas décadas de 30 e 40 e que
chegou a construir um computador eletro-mecânico completamente operacional já no início

5 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

década de 40, também foi completamente perdido com a derrota da Alemanha, somente
tendo sido recuperado 20 ou 30 anos depois.

Um outro fato interessante de se comentar é que a criação dos computadores no início da


década de 1940, parecece, em retrospectiva, um desenvolvimento quase “inevitável” da
época. O fato é que durante a década de 1940, nada menos do que 4 computadores
inteiramente distintos, mas usando tecnologia similares e respeitando os mesmos
princípios, foram construídos de forma completamente independentes. Mesmo que o
ENIAC se transformasse na vedete, sendo considerado durante muito tempo o ancestral
único dos computadores, a verdade é que ele foi apenas o mais bem divulgado. Uma
explicação para o fato de tantos computadores surgirem ao mesmo tempo, surge quase que
imediatamente, como relacionada ao imenso esforço econômico, social e militar provocado
pela II Guerra Mundial. No início da guerra já haviam sido desenvolvidos todos os
elementos e componentes (válvulas, relés, etc.) que permitiriam construir um computador.
Além disso, em virtude de trabalhos teóricos anteriores de Turing, Babbage e outros, não só
já se sabia como construí-los, mas também do custo quase que astronômico que uma
máquina destas teria. Com a guerra, entretanto, as razões e princípios econômicos normais
deixam de “funcionar”, numa “economia de guerra” somente há um objetivo final:
sobreviver e se a posse de um computador melhora, ainda que marginalmente, a
possibilidade de se atingir este objetivo final então ele deve ser construído. Embora os
Americanos não tivessem usado o seu computador na guerra, para os Ingleses esta não foi
uma questão apenas de cunho acadêmico: o uso dos sistemas de computação COLOSSUS
foi extremamente importante para a vitória da sua nação durante a Batalha do Atlântico o
que garantiu a sobrevivência deste país e a possibilidade do contra-ataque e vitória final
contra a Alemanha Nazista.

O ENIAC e alguns sucessores, formaram o que se convencionou chamar de “primeira


geração” dos computadores. Este termo “geração de computadores” embora esteja caindo
em desuso, ainda é usado para designar, pelo menos, as primeiras 3 tecnologias básicas
usadas na fabricação de computadores:

Primeira Geração: Computadores fabricados com válvulas eletrônicas, começou


na década de 40 e terminou na década de 50.

Segunda Geração: Computadores fabricados com transístores, ou seja, com


dispositivos semicondutores individuais, começou na década de 50 e durou até a
década de 60.

Terceira Geração: Computadores fabricados com dispositivos semicondutores


integrados, ou seja, com “Circuitos Integrados” ou CIs. Considera-se que esta
geração apenas designe os computadores fabricados com CIs de “baixa” ou “média”
integração (apenas algumas centenas ou poucos milhares de transístores por CI).
Esta geração começou na década de 60 e terminou na década de 70.

Após estas três gerações “clássicas” a computação, pelo menos a parte física ou hardware,
passou a ser cada vez mais dominado pelos computadores completamente integrados num

6 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

CI, os famosos Microprocessadores, que deram origem aos microcomputadores e que,


hoje em dia, perderam o adjetivo “micro” e são chamados apenas de computadores.

Os microprocessadores, que foram inventados pela Intel ainda no início da década de 70,
hoje correspondem pela quase totalidade dos computadores fabricados no mercado. Apesar
desta “simplificação” ocorrida de se colocar um computador num CI, hoje os sistemas de
computação se transformaram em entidade bem mais complexa, envolvendo não apenas o
que se denominada anteriormente de computadores, mais vários outros tipos de dispositivos
de comunicação, de entrada e saída de dados, multimídia, sofisticadas redes de
interconexão, etc.

Na verdade o processo evolutivo, apenas delineado muito superficialmente nesta seção,


continua muito mais acelerado do que nunca, aperfeiçoando não apenas os aspectos físicos
da computação, o seu hardware, mas também mudando e recriando continuamente todos os
seus aspectos lógicos e de programação.

1.2. Conceitos Elementares de Computação e Informática


Cência da Computação e Informática

Em primeiro lugar seria importante precisar um pouco mais o significado dos termos
“Ciência da Computação” e “Informática”. A Ciência da Computação é a ciência, ou
corpo sistematizado do conhecimento humano, que trata das formas de se executar
computações de forma automática e independente da intervenção humana. Vale ressaltar,
entretanto, que no contexto desta expressão a palavra computação não deve ser
compreendida como apenas designando cálculos ou operações numéricas, mas deve ser
entendida como denominando qualquer tipo de processamento de informações (que, no
caso, possa ser automatizado). Esta é justamente a relação da Ciência da Computação com
a Informática, que é apenas uma outra forma de denominar este mesmo conceito, ou seja,
informática é a ciência que estuda o tratamento automático do processamento das
informações.

Computador

Prosseguindo, deve-se aclarar a relação existente entre a computação ou informática e os


computadores. Normalmente as explanações ou definições da Ciência da Computação,
começavam por dizer que ela era a ciência que estudava os computadores. Esta entretando é
uma definição exageradamente limitadora, porque, é importante notar, não é
necessariamente obrigatório se trabalhar com computadores para se fazer Ciência da
Computação. Muito antes pelo contrário, existe, na verdade, todo um ramo teórico desta
ciência que pesquisa as características mais fundamentais e também os limites mais
avançados dos processos de computação sem a necessidade de se usar computadores reais
nesta pesquisa. Por exemplo, de acordo com o histórico visto anteriormente, pode se ver
que as definições teóricas precisas do processo de computação antecedem em quase dez
anos a construção real do primeiro computador digital.

7 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

Feitas estas ressalvas, pode-se definir um computador como qualquer dispositivo real que
execute computações ou processem de informações de forma automatizada. A definição do
que é um computador independe de tecnologia, no decorrer de sua longa história os
computadores já foram incorporados das mais diversas formas: como projeto mecânico
(Babbage), como dispositivo eletro-mecânico (MARK I), como dispositivo eletrônico
baseado em válvulas e finalmente na sua incarnação moderna como dispositivo baseado em
semicondutores. Dito isto deve ficar claro que não interessa muito a tecnologia em que o
computador está baseado (exceto por questões de capacidade e desempenho), mas sim se
este dispositivo consegue executar processos ou mais precisamente programas que
manipulem informações de forma automática.

Hardware e Software

Justamente aí começa uma das divisões mais importantes da informática: a divisão entre os
componentes físicos de um computador, usualmente agrupados sob o termo inglês
hardware, e os elementos lógicos e de controle deste computador, usualmente agrupados
sob o termo inglês software. O hardware de um computador se refere a todos os
componentes físicos necessários para a construção deste dispositivo. De uma forma bem
prática, o hardware designa os elementos que não podem ser mudados ou “programados”
num computador, exceto pela troca física de peças. Já o termo software tem uma conotação
muito mais “soft” ou leve (como o próprio termo indica), normalmente ele designa o
conjunto de programas ou sequências de instruções que comandam a execução de um
computador, fazendo com que este execute alguma computação útil. O software também se
refere, de uma forma mais geral, aos elementos lógicos ou de controle de um computador
que podem ser facilmente alterados, recarregados ou reconfigurados neste.

Programa e Algoritmo

O software de uma máquina ou computador está intimamente ligado aos programas que
executam neste computador. Estas idéias de que é um “programa” ou do que é a
“execução” deste programa no computador são extremamente importantes, elas formam a
própria base de toda a informática: embora possa existir uma Ciência da Computação
teórica sem computadores, não existe processamento de informações prático ou teórico sem
programas, ou de sua contrapartida mais abstrata, sem algoritmos. Apesar disso, a definição
mais precisa destes termos será deixada para um próximo capítulo, por agora, basta a idéia
intuitiva de que um programa ou algoritmo é uma sequência de passos que executam
alguma função. A diferença entre um algoritmo e um programa é que este último deve
poder ser executado por um computador real.

Informação e Dado

Se olharmos para um dicionário veremos que o termo informação designa tudo aquilo que
permite adquirir conhecimento, ou seja, informação é aquilo que dá a conhecer alguma
coisa que se desconhecia anteriormente. Dito desta forma o conceito parece muito abstrato
e filosófico e realmente o é, uma vez que fundamenta o conceito de informação sobre um
outro conceito muito mais complexo: o “conhecimento”.

8 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

O problema aqui é que, se quisermos que a computação ou a engenharia possam trabalhar


com a informação é necessário ter um conceito mais preciso e “quantificável” (numérico)
sobre o que é informação. A solução encontrada, para evitar uma discussão filosófica
praticamente interminável sobre o que seria ou o que não seria informação, dada a natureza
particularmente intratável de se definir o que é conhecimento, foi tomada através de um
caminho “lateral” que permitiu definir precisamente e quantitativamente o que seria
informação.

A definição quantitativa inicial para o conceito de informação é devida ao pesquisador e


engenheiro Claude Shannon, que definiu este conceito ainda na década de 1940. Na
definição de Shannon, não interessa muito o que é ou seria o conhecimento em si, mas
interessa o fato de que a informação permite “transferi-lo” e “aumentá-lo”, ou seja,
informação é um veículo de transporte de conhecimentos de um “lugar” para outro.
Seguindo por este caminho (e usando uma pequena ajuda da Física Moderna) é possível se
chegar até a quantização da informação, que é o objetivo final da Engenharia: geralmente
se considera, em Engenharia, que somente é possível se trabalhar apropriadamente sobre
um determinado conceito quando ele pode ser completamente matematizado e quantizado.

Seguindo este raciocínio, Shannon chegou a menor unidade possível de transporte e


manipulação de informação, o menor “pedaço” de informação ou conhecimento que se
pode ter sobre qualquer tipo de tema. A pressuposição filosófica implícita neste raciocíonio
é que, quando questionado sobre algum tema, ou você sabe a resposta e responde
afirmativamente (representado por “sim”, por “verdadeiro” ou pelo número 1) ou você não
sabe e responde negativamente (representado por “não”, por “falso” ou pelo número 0). De
qualquer forma, após a resposta ter sido dada, certamente quem perguntou tem mais
conhecimento do que tinha antes. Ele na verdade resolveu uma dúvida, ele não sabia de
algo e agora passou a saber, portanto teve seu conhecimento “aumentado” na menor
“quantidade” possível: uma resposta sim ou não.

O nome dado a este menor pedaço de informação ou dado, tanto na Ciência da Computação
quanto na Engenharia ou Telecomunicações é bit, que é a contração para a expressão
inglesa “binary digit” ou “dígito binário” que pode valer, portanto, 0 ou 1 (e pode
representar, portanto, qualquer tipo de resposta sim/não, verdadeiro/falso, etc.).

Quanto aos dados, se a informação pode ser pensada como um tipo de “veículo” para
transportar conhecimentos, então um dado pode ser compreendido como a instanciação
concreta deste veículo. Um dado é, portanto, o elemento concreto (um símbolo concreto)
responsável pelo próprio transporte (ou armazenamento) da informação. Qualquer símbolo
concreto pode ser usado: uma letra, um antigo hieroglifo egípcio, um trecho de música num
disco de vinil, um arquivo na memória de um computador, um texto gravado sobre um
disco de CD, etc. podem ser considerado como dados.

É redundante afirmar que na Ciência da Computação os únicos tipos de dados que são
realmente tratados são os dados armazenados em bits, isto é, os dados binários. Todos os
demais tipos de dados são construídos sobre estes bits.

9 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

1.3. Programas, Algoritmos e Programação


Programa

Provavelmente o conceito individual mais importante na Ciência da Computação é o de um


programa de computador ou simplesmente programa. A compreensão correta deste
conceito é fundamental para se avançar no estudo desta ciência. Posto de uma forma bem
intuitiva um programa nada mais é do que um conjunto de comandos ou instruções que
uma máquina ou computador deve executar para resolver um problema. Esta idéia apesar
de simples é incompleta porque, embora traga a baila os pontos mais significativos do
conceito de programa: conjunto de comandos, computador, execução e resolução de
problemas (automatizada), lhe faz falta detalhar algumas facetas e aspectos importantes
destes:

• A estrutura e organização do conjunto de instruções é um fator extremamente


importante para que um programa funcione corretamente: esta estrutura e
organização definem a “lógica” do programa, ou seja aquilo que define a razão do
seu próprio funcionamento;
• Um programa deve definir claramente e com todo o detalhamento necessário como
um problema deve ser resolvido num computador, ou seja, não basta detalhar as
características ou descrições do que o problema é ou de porque o problema é
importante, mas, isto sim, é necessário saber claramente como ele pode ser
resolvido tanto do ponto de vista puramente lógico mas também do ponto de vista
do que pode ser executado no computador;
• Em relação ao ítem anterior, também é usual se separar os conceitos de sequência
de passos “lógicos” que devem ser empregados para resolver um problema, que
normalmente é denominado de algoritmo e a sequência similar de passos ou
instruções que já estão prontos para serem executados por algum computador ou
máquina, que é um programa propriamente dito.
Um comentário final: um programa deve comandar a operação de um dado computador, ou,
dito de outra forma um computador deve executar as instruções ou comandos do programa,
ou mais simplesmente, um computador deve executar um programa.

Programação e Análise

Embora as vezes seja usada num contexto mais limitado, do ponto de vista mais geral o
termo programação designa a atividade humana de buscar soluções para problemas
através da criação de programas de computador. A programação está intimamente
relacionada e as vezes até se confunde ou entra em conflito com uma outra atividade,
denominada de análise, que geralmente designa o processo de se tentar compreender e
dissecar um problema em partes menores que possam ser resolvidas diretamente, isto é, em
problemas menores cujo algoritmo que os resolve já é conhecido. Em geral faz mais sentido
tentar compreeder ambas atividades como complementares e necessárias para a busca de
soluções e o desenvolvimento de programas, do que tentar buscar algum conflito na
interação entre as duas.

10 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

O primeiro passo para a elaboração de qualquer programa é, justamente, proceder com a


cuidadosa análise do programa em questão. O resultado deste trabalho deve ser uma
especificação lógica e precisa de como ele pode ser resolvido, um algoritmo em outras
palavras. De posse deste algoritmo pode-se passar para a tarefa mais especializado de fazer
com que um computador compreenda e seja dirigido por este algoritmo, ou seja, a tarefa de
programação.

Testes e Depuração

Isto entretanto não é tudo, porque no mundo real os erros são praticamente inevitáveis (isto
é denominado pelos programadores e analistas com a famosa “Lei de Murphy”, que afirma
que se alguma coisa tem a chance de dar errado ela vai dar errado e provavelmente isto
acontecerá no pior momento possível). Sendo assim é muito provavel que um programa não
funcione corretamente, ou seja, não resolva o problema, na primeira vez que for executado
(se diz também “rodado”). Normalmente um programador pode ser considerado experiente
não porque seus programas funcionam bem desde o início, muito antes pelo contrário, um
programador é realmente experiente quando sabe perfeitamente que as primeiras versões de
qualquer programa que produzir certamente não irão funcionar a contento e está
plenamente preparado para isto, pronto para testar o programa e corrigir seus erros até
resolver a situação.

Dessa forma logo após a construção de um programa, existe toda uma série de atividades,
direcionadas primeiro em fazer com que o programa funcione corretamente e
posteriormente para estender as capacidades deste programa para atender a novas
requisições.

Em geral se denominam a fase em que se tenta fazer com que um programa uncione de fase
de teste e depuração do programa. Nesta fase o programa é testado, sendo confrontado
com o problema que pretende resolver. Cada vez que um erro é detectado ele deve ser
depurado, ou seja, devem ser feitas adaptações e correções para garantir o seu bom
funcionamento. Muitas vezes também são feitos testes do programa por pessoas não
relacionadas ao seu desenvolvimento, isto é chamado normalmente de validação do
programa. Quando esta validação é deixada a cargo do mercado, sendo feita por pessoas de
fora da organização ou empresa responsável pelo programa isto é, as vezes, denominado de
fase de testes em beta ou apenas de beta-teste do programa.

Manutenção

Uma vez que os problemas sejam encontrados e corrigidos, então o programa entra em fase
“de produção” podendo ser usados pelas pessoas que necessitam resolver o problema que
motivou a construção do programa. Pessoas que usam programas de computador
(computadores em geral) para executar alguma tarefa ou resolver algum problema são
usuários de computador ou simplesmente usuários.

Mesmo quando um programa já está em uso há bastante tempo, sempre existe alguma
atividade de teste e programação associada a ele. Não só as situações, problemas e tarefas
para qual o programa foi projetado podem mudar com o passar do tempo, como também
novas possibilidades e características podem ser vislumbradas para a sua utilização.

11 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

Quando isto ocorre e são necessárias modificações no programa, se diz que o programa
deve passar por uma manutenção. Além disso, exceto em casos triviais, nunca é possível
se estar 100% certo de que todos os erros foram encontrados e depurados, logo quando
novos erros forem encontrados e tenham que ser corrigidos o programa também entrará em
manutenção.

1.4. Estrutura de um Programa


Um programa de computador não é uma estrutura isolada dentro do computador,
independente do mundo externo. Muito pelo contrário, para resolver o problema que lhe
deu origem, para executar alguma tarefa em particular normalmente é necessário que exista
uma forte interação deste programa com o mundo externo, que exista um processo contínuo
de troca de informações entre o programa e o ambiente externo. Desta forma, do ponto de
vista funcional, um programa pode ser subdivi em três grandes partes:

• Entrada de Dados ou Informações: formada por todas as instruções que obtém


dados do ambiente externo, guardando estes dados numa área de armazenamento
(uma memória) interna para que possam ser processados posteriormente.

• Processamento das Informações: parte do programa responsável por resolver o


problema a partir dos dados recém obtidos pela entrada de dados. Além das
instruções e operações executadas sobre estes dados de entrada, o processamento de
informações também faz uso da memória para guardar informações temporárias e
para preparar informações para saída.

• Saída de dados ou informações: uma vez que existam resultados que possam ser
apresentados externamente, então eles devem ser preparados e enviados para o
ambiente externo, através de um conjunto específico de instruções.

É importante salientar que estes três elementos básicos de qualquer programa não estão
necessariamente separados e identificados claramente dentro do programa. O normal,
inclusive, é encontrar as operações e instruções referentes a cada parte misturadas entre si,
de acordo com a necessidade que o programa tenha de obter novos dados ou de informar
algum resultado intermediário. Apesar disto sempre se pode analisar e decompor qualquer
problema, identificando os trecho correspondentes a entrada de informações, ao
processamento destas e a saída destas informações para o ambiente externo.

Em relação ao ambiente externo e a forma como se dá esta interação, valem algumas


considerações:

• O objetivo da entrada e saída de dados é geralmente a comunicação com algum


usuário.
• Apesar disso deve-se ter em mente que tanto a entrada quanto a saída de
informações é mediada através de dispositivos especiais de entrada e saída, ou
periféricos de entrada e saída.

12 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

• Também não é incomum que um programa se comunique apenas com um


dispositivo, por exemplo um sensor externo, sem a intervenção de nenhum usuário.
Isto é comum em controle de processos industriais, por exemplo.
• Outra possibilidade que existe é que um programa se comunique com outro
programa através da entrada e saída de dados. As Redes de Computadores e os
Sistemas Distribuídos são os exemplos mais evidentes deste tipo de interação.

Uma outra estruturação que se faz comumente em relação aos programas tem a ver com a
metodologia empregada para organizar logicamente as solução de problema através de
computadores. Dada as características que os computadores tem para manipular dados e
informações, cada programa deve ser dividido em duas grandes partes:

• Especificação dos Dados: onde se define como serão estruturados os dados a serem
usados pelo programa, ou seja, onde se declara como serão as estruturas de dados
usadas pelo programa.

• Especificação do Algoritmo: onde se define o conjunto de instruções que o


computador terá que executar para resolver o problema.

Resumindo um programa é a soma das suas estruturas de dados com o seu algoritmo,
escritos em alguma linguagem própria para ser executada por um computador (uma
linguagem de programação).

1.5. Linguagens de Programação


Como visto anteriormente, um programa é um conjunto de instruções que dirigem a
operação de um computador. Porém existem muitas maneiras de se construir um programa,
porque existem inúmeras formas como este conjunto de instruções pode ser organizado e
estruturado e também existem muitos tipos distintos de instruções, operações ou comandos
que podem ser executados por um computador. O ramo da computação que estuda como os
programas podem ser construídos é o de Linguagens de Programação.

Linguagem, Símbolos e Gramática

Uma linguagem é uma notação ou convenção que se pode usar como meio de comunicação
de informações, conhecimentos, sentimentos, etc. entre seres humanos. Uma linguagem de
programação é, então, uma forma de notação que nos permite comunicar a um computador
os comandos e instruções que ele deve executar. Dito desta forma é evidente que qualquer
programa deve ser escrito em algum tipo de linguagem de programação, ou seja, em
alguma linguagem que possa ser compreendida por um computador (embora evidente, as
vezes é esquecido, principalmente por programação mal feita, um programa também deve
ser compreendido por um ser humano).

Qualquer linguagem contém um conjunto de simbolos básicos e atende um conjunto de


regras precisas que diz como estes símbolos são organizados em expressões, frases,
orações, etc. Estas regras definem a gramática da linguagem. Uma grande diferença entre

13 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

as linguagens usadas pelos seres humanos e as linguagens de programação, é que as


linguagens humanas, também denominadas de linguagens naturais, embora certamente
contenham símbolos (letras, palavras, fonemas, ideogramas, etc) e sigam regras
gramaticais, nem sempre tem estes elementos precisamente identificados ou formalmente
definidos, isto é, linguagens naturais não são completamente formais. Por outro lado as
linguagens de programação devem, pela própria natureza formal e “mecânica” dos
computadores ser linguagens formais, com regras precisamente definidas de como as
expressões linguísticas podem ser construídas.

Sintaxe e Semântica

De agora em diante, se nada for dito em contrário, o termo linguagem se referirá


exclusivamente às linguagens de programação. A regras gramaticais de uma linguagem
definem a sintaxe destas linguagem que é a forma como se pode construir expressões
corretas da linguagem. O fato de se ter uma expressão correta gramaticalmente, isto é, com
uma sintaxe correta não quer dizer necessariamente que a expressão tem algum sentido ou
significado. O significado atribuído a uma dada construção linguística é denominado de
semântica desta construção. Embora a semântica das linguagens naturais seja
extremamente complexa e relacionada ao pensamento, à razão, as emoções, sentimentos e
inúmeras outras características humanas, a semântica das linguagens de programação não
segue um território tão perigoso. Todas as construções de uma linguagem de programação
tem seu significado precisamente fundamentado sobre as operações e instruções básicas
que um computador pode executar.

Entretanto, o principal objetivo de pesquisa da área de linguagens de programação é de


buscar formas de se escrever programas que sejam cada vez mais parecidas ou similares
com a nossa forma de se comunicar e trocar idéias e informações, isto é, o objetivo é tentar
trazer as linguagens de programação o mais próximo possível das linguagens e conceitos
usados por nós.

Níveis de Linguagens

Para tanto o usual é dividir este problema, considerando que as linguagens possam ser
classificadas em diferentes níveis de linguagens, sendo os níveis inferiores mais próximos
das máquinas e os níveis superiores mais próximos dos seres humanas. Assim linguagens
dos níveis inferiores, as linguagens de baixo nível, teriam suas construções linguísticas
mais próximas das operações e estruturas de dados disponibilizados pelos computadores,
enquanto que as linguagens superiores ou de alto nível, teriam suas construções mais
próximas da linguagem natural ou pelo menos de mais fácil compreensão pelas pessoas.

Uma outra diferença importante seria a de que as linguagens de alto nível conseguiriam
atingir independência em relação à máquina, podendo um mesmo programa ser utilizado
em diferentes equipamentos com a única condição de disporem de um programa de
tradução compatível com a linguagem. Esta característica, idealmente, permitiria isolar ou
diminuir a necessidade de se ter conhecimento do hardware para se programar o sistema.

Embora ainda existam alguns defensores para o desenvolvimento de aplicações em


linguagens de máquina, tendo como argumento principalmente questões de desempenho e

14 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

uso de recurso de máquina que supostamente poderiam ser melhor administrados


diretamente em linguagem de baixo nível do que em linguagens de alto nível, a verdade é
que atualmente essa questão já está praticamente resolvida em favor da programação em
alto nível.

Primeiro, existem vários pontos desejáveis atendidos pelas linguagens de alto nível, quando
estas são comparadas às de baixo nível:

• Facilidade de programação, por usar conceitos mais próximos das pessoas do que
das máquinas.
• Independência de arquitetura de máquina: no caso das linguagens de baixo nível
existe uma linguagem para cada tipo distinto de computador ou máquina.

Além disso é muito importante salientar que, no caso de certas arquiteturas de computador
(como as arquiteturas RISC), simplesmente não é mais humanamente possível gerar um
“bom” código de máquina, um código bem otimizado, pela quantidade de efeitos colaterais
associados a execução de cada instrução e a relação destes efeitos com as 4 ou 5 instruções
anteriormente executadas e as outras tantas possíveis instruções a serem executadas.
Somente um compilador consegue manter o registro destes efeitos de forma precisa, tirando
o máximo possível da máquina.

Apesar disso, a programação em linguagem de montagem ainda é necessária em alguns


casos específicos, como nos estágios iniciais de carga e inicialização de um compuador e
também quando se deve programar a comunicação com alguns periféricos de entrada e
saída.

1.6. Tradução de Linguagens


Afora programas escritos diretamente em linguagem de máquina, que são código objeto
pronto para execução, todos os demais tipos de programas precisam de algum tipo de
processo de tradução para poderem ser executados num computador.

A tradução de um programa de alto nível pode ser feita de duas grandes formas, através da
compilação deste programa ou através da interpretação do mesmo. Já um programa em
linguagem de montagem passa por um processo similar ao da compilação, denominado de
montagem.

Compilação

A compilação de um programa é um processo que traduz um programa escrito numa


linguagem origem, denominado de programa fonte, para um outro programa escrito numa
linguagem objeto, denominado de programa ou código objeto. Idealmente o código objeto
pode ser executado diretamente pelo computador, porém geralmente existem ainda alguns
passos posteriores que devem ser executados para que o programa objeto possa ser
executado. O programa que realiza este processo de tradução é denominado de compilador.

15 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

Liguagens que são tipicamente implementadas através de compiladores são: Pascal, C,


C++, Java, Fortran, etc.

Interpretação

Quando um programa fonte não é traduzido integralmente, “de uma só vez” para um outro
programa equivalente objeto, mas ao invés, cada expressão ou comando deste programa é
traduzido e interpretado num conjunto de instruções de linguagem de máquina que são
prontamente executadas, denomina-se este tipo de tradução de interpretação. Os programas
que interpretam uma linguagem são denominados de interpretadores. Linguagens de alto
nível que são tipicamente interpretadas são as interfaces (shells) de comando dos sistemas
operacionais, a linguagem BASIC e as linguagens script usadas na programação para
sistemas da Web: ASP, PHP, JavaScript

Montagem

A montagem é um processo muito parecido com a compilação, a diferença é apenas que o


programa fonte está escrito em linguagem de montagem e não numa linguagem de alto
nível. Este programa fonte em linguagem de montagem também é traduzido para um
programa em código objeto.

Embora certos tipos de linguagens sejam normalmente implementadas através de


compiladores e outros através de interpretadores, esta divisão não é absoluta ou irreversível,
ou seja, o fato de se implementar uma linguagem por compilação ou interpretação está mais
associado ao uso que se pretende dar a linguagem e com características de desempenho,
capacidade e flexibilidade do que com algum tipo de características intrísecas da mesma.
Por exemplo, embora a maior parte das implementações de C e C++ seja através de
compiladores, teoricamente nada impede que se implemente estas linguagens através de
compiladores. A questão aqui é que, embora não exista empecilho teórico para a
implementação através de interpretadores, se espera que a implementação compilada atinja
melhores índices de desempenho e gaste menos recursos que uma versão interpretada e
para o uso pretendido para sistemas escritos em C e C++ estes objetivos de desempenho e
capacidade são mais importantes do que a flexibiliade em se modificar ou atualizar um
sistema. Por outro lado para sistemas e aplicações escritas em linguagens interpretadas,
como por exemplo sistemas vinculados a páginas da Web, pesam mais os ítens como
flexibilidade e facilidade de implantação e atualização, que são melhor servidos por
interpretadores.

1.7. Carga e Execução de Programas


A execução dos programas interpretados é direta, sendo feita pelo próprio programa
interpretados. Por outro lado os programas compilados e montados, tem um caminho um
pouco mais tortuoso. Em primeiro lugar o código objeto gerado tanto pelos compiladores
quanto pelos montadores normalmente não está “pronto” para ser executado pela máquina,
sendo necessário mais dois processos auxiliares antes desta execução: a ligação e a carga do
programa.

16 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 1 -Introdução

Um programa final que será executado numa máquina não precisa estar contido em apenas
um único programa fonte, ou seja, o programa executável final pode ser o produto de vários
programas fontes distintos. O processo que “pega” estes vários programas fontes distintos,
já compilados, e junta num grande executável final é denominado de ligação (em inglês
linking). O programa que faz isto é denominado de ligador (linker). Apesar de parecer
laborioso, este processo é vantajoso sob vários pontos de vista, porque permite que
programas que foram criados para outros fins possam ser reaproveitados e permite também
que se possa criar verdadeiras bibliotecas de programas, procedimentos e funções que
podem posteriormente ser utilizadas por uma grande variedade de programas. Basta ligar a
biblioteca previamente compilada ao seu programa que você pode usar as rotinas desta
biblioteca nele.

Um último processo, aparentemente invisível e executado pelo Sistema Operacional é o


processo de carga do programa e posterior execução deste. Este processo é um pouco mais
complicado do que parece porque implica não apenas em trazer o código executável de um
arquivo para a memória principal, mas também de se alocar ou reservar previamente os
recursos (memória, dispositivos de entrada e saída, arquivos de disco, terminais de teclado,
mouse, vídeo, etc.) que poderão ser usados pelo programa.

Após a carga do programa pelo Sistema Operacional começa o processo de execução


propriamente dito, que é feito, evidentemente, diretamente pelo processador. Existem
alguns casos, entretanto, quando o sistema operacional poderá entrar novamente em ação:
quando for necessário executar uma operação de entrada de dados ou de saída de dados,
que são usualmente executadas através de chamadas do Sistema Operacional, quando for
necessário usar algum recurso novo dinâmico, não previsto quando o programa foi
compilado, tipicamente mais memória. Além disso no caso de sistemas multi-tarefa a
execução do programa poderá ser interrompida arbitrariamente pelo Sistema Operacional
para permitir que algum outro programa ou tarefa possa ser executado concorrentemente.

17 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 2 - Algoritmos e Linguagem C

Capítulo 2
Algoritmos e Linguagem C

2.1. Algoritmos
Para se definir o conceito de algoritmo de forma precisa é necessário estabelecer antes o
conceito de ação finita ou apenas ação:

Uma ação é um determinado acontecimento que, a partir de um dado estado inicial


e após um período de tempo finito, produz um um estado final que é previsível e
também bem definido.

Também se deve definir o conceito de comando:

Um comando é o ato de ordenação da execução de uma ação, ou seja, um


comando força a execução de uma ação (note que uma ação é um acontecimento e
acontecimentos podem ser fortuitos ou não, isto é, podem acontecer ao acaso ou
serem comandados).

Um algoritmo fica caracterizado então como:

Um algoritmo é a descrição de um conjunto de comandos que, se obedecidos,


resultam numa sucessão finita de ações.

Alguns pontos importantes devem ser salientados da definição acima:

(1) Um algoritmo deve ser finito, ou seja, deve executar num tempo limitado e
obrigatoriamente parar após algum tempo.

(2) Como todas as ações tem efeitos previsíveis e bem definidos, diz-se que um algoritmo
que esteja de acordo com a definição acima é determinístico.

(3) Existe uma diferença importante entre o algoritmo e sua execução: um algoritmo é uma
descrição, ou seja, uma espécie de receita que diz que ações teriam que ser feitas para
executar alguma tarefa, porém ele não é a tarefa em si.

Exercícios:

(2.a) Um algoritmo pode conter comandos como “Escreva todos os números primos” ou
“Descubra quantas frases possíveis podem ser escritas na língua portuguesa”? Responda
sim ou não e justifique.

18 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 2 - Algoritmos e Linguagem C

(2.b) Um algoritmo pode conter comandos como “Escolha um número primo qualquer” ou
“Escolha uma palavra qualquer do dicionário”? Responda sim ou não e justifique.

(2.c) Um algoritmo pode ter comandos como “Ache algo que sirva” ou “Calcule o resultado
para mim”? Responda sim ou não e justifique.

(2.d) Um algoritmo pode ter comandos como “Conte quantas vezes a letra ‘p’ aparece nas
palavras do dicionário Aurélio” ou “Descubra todos os primos maiores que 1.000 e
menores que 1.000.000.000”? Responda sim ou não e justifique.

(2.e) Escreva um algoritmo que verifique se um número é par.

(2.f) Escreva um algoritmo que verifique se um número é primo.

(2.g) Escreva um algoritmo que verifique se uma palavra está no plural.

(2.h) Escreva um algoritmo que verifique se uma palavra é um pronome ou não?

(2.i) Escreva um algoritmo que verifique se um triângulo é obtuso, agudo ou reto.

(2.j) Escreva um algoritmo que verifique se um quadrilátero é: um quadrado, um retângulo,


um losango ou nenhum dos três casos anteriores.

2.2. Formas de Descrição de Algoritmos


Um algoritmo é uma descrição de um conjunto de comandos que deve ser feito em algum
formato específico. Existem várias maneiras de se definir um algoritmo:
1. pode-se descrevê-lo como um texto em língua portuguesa;
2. pode-se criar um diagrama gráfico que indique claramente o que deve ser feito;
3. pode-se construir uma fórmula matemática que defina como deve ser calculado
4. etc.

A única condição que esta descrição deve atender é que ela seja perfeitamente
compreendida tanto por quem escreve o algoritmo quanto por quem terá que executá-lo. No
caso genérico de algoritmos feitos para uso por seres humanos todos os esquemas acima (e
outros mais) com diversos graus de detalhamento já foram empregados.

2.3. A Noção de Programa


Entretanto, existe um caso particular de algoritmos que são escritos para serem executados
apenas por computadores. Estes algoritmos são denominados de programas, e a forma
principal de descrição destes programas é através de versões extremamente limitadas,
rígidas e formalizadas de línguas naturais: as linguagens de programação (normalmente
usando subconjuntos da língua inglesa).

19 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 2 - Algoritmos e Linguagem C

Todas as linguagens de programação compartilham de um conjunto básico de conceitos


elementares que dizem:
• Como são definidos os valores constantes no programa (constantes numéricas,
caracteres, textos, valores lógicos, etc.).
• Como se pode criar variáveis para o programa (como elas são identificadas, criadas e
usadas).
• Quais são os tipos de dados básicos ou primitivos da linguagem.
• Como se pode colocar comentários no programa, que esclareçam detalhes de como ele
deveria funcionar.
• Como se pode construir expressões aritméticas e expressões comparativas (relacionais).
• Como se pode construir comandos que manipulem e alterem os dados (variáveis).

Por fim, toda a linguagem de programação deve possuir comandos que permitem controlar
o fluxo de execução do programa.

Além disso as linguagems estruturadas modernas apresentam também ferramentas


(construtores de tipos de dados) que permitem definir novos tipos de dados a partir dos
tipos de dados primitivos ou básicos.

Estes conceitos serão apresentados nas seções e capítulos a seguir. Antes, entretanto,
faremos uma breve apresentação das características gerais da linguagem C.

Exercício:

(2.k) Existe algum empecilho mais sério que um diagrama gráfico possa ser empregado
também para comunicar um algoritmo para um computador, ou seja, poderia se usar
diagramas gráficos para programar computadores. Discuta a questão.

2.4. A Linguagem C
A linguagem C foi criada por Dennis Ritchie, em 1972, no centro de Pesquisas da AT&T
Bell Laboratories, como sucessora da linguagem de programação de sistemas B (por isso o
nome C). Sua primeira utilização importante foi a reescrita do Sistema Operacional UNIX,
que até então havia sido escrito em assembly pelo pesquisador Ken Thompsom, também do
AT&T Bell Labs. Em meados de 1970 o UNIX saiu do laboratório para ser liberado para as
universidades. Foi o suficiente para que o sucesso da linguagem atingisse proporções tais
que, por volta de 1980, já existiam várias versões de compiladores C oferecidas por várias
empresas, não sendo mais restritas apenas ao ambiente UNIX, porém compatíveis com
vários outros sistemas operacionais.

A linguagem C é uma linguagem de propósito geral, sendo adequada à programação


estruturada. Ela pode ser considerada uma linguagem de “nível-médio”, porque, embora
possua todas as características de estruturação de dados e programa em linguagens de alto
nível, ela permite o acesso direto as estruturas de hardware de uma máquina, se isto for
necessário. Ela é principalmente utilizada para escrever sistemas de software básico tais
como o próprio sistema operacional, seus compiladores, analisadores léxicos, gerenciadores

20 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 2 - Algoritmos e Linguagem C

de bancos de dados, drivers de periféricos e dispositivos de estrada e saída, editores de


texto, etc. A principais características características de C são: portabilidade, modularidade,
compilação separada, recursos de baixo nível, geração de código eficiente, confiabilidade,
regularidade, simplicidade e facilidade de uso.

21 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

Capítulo 3
Elementos Básicos de C

Este capítulo apresenta os elementos básicos que compõem um programa em C:


• Constantes numéricas, caracteres, textuais e valores lógicos.
• Variáveis
• Tipos de dados básicos (ou primitivos).
• Comentários.
• Expressões aritméticas, relacionais e lógicas
• Expressões (comandos) de atribuição de valor a variável

3.1. Constantes
Uma constante é um determinado valor fixo que não se modifica ao longo do tempo,
durante a execução do algoritmo ou programa. As constantes podem ser de diversos tipos,
dependendo se ela for um número inteiro, um número real, um valor lógico (verdadeiro ou
falso), um caracter ou então se for uma sequência (também chamada de cadeia ou string) de
caracteres.

Tabela 1 - Exemplos de Constantes


Inteiro Real Lógicas Caracter String
0 0.0 TRUE ‘a’ “palavra”
-12 -2.23 FALSE ‘B’ “Uma pequena frase.”
0x1b2 2e4 ‘\n’
4.3e-2 ‘\x00’

Exercício:

(3.a) Identificar o tipo de cada uma das constantes abaixo:


0xFACA “TRUE”
“0x12” 3.0
1e3 0x1e3
‘4’

3.2. Variáveis
Na matemática uma variável representa um elemento qualquer de um dado conjunto dentro
de uma fórmula ou expressão matemática.

22 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

Na computação, entretanto, uma variável irá corresponder a uma posição de memória que
poderá variar de conteúdo durante a execução do programa. Uma variável pode armazenar
um valor nesta posição de memória. Este valor é o conteúdo da variável. Embora uma
variável possa assumir diferentes valores, ela somente pode assumí-los um de cada vez, ou
seja, ela só pode armazenar um valor de cada vez.

Toda variável deve ter um identificador ou nome que começa com uma letra (A,B,C,...Z
ou a,b,c,...,z) seguido de uma sequência de letras, dígitos ou do caracter especial de
sublinhado (‘_’). Não são aceitos caracteres acentuados. Exemplos: Valor_maximo,
TotalPorAluno, TAXA_DE_CAMBIO.

Exercício:

(3.b) Quais termos abaixo são identificadores válidos:


VALOR SALARIO-LIQUIDO B24S
X2 Nota_do_Aluno A1b3C4
2*4 K/H “NOTA”
XYz NOMEDAEMPRESAQUEVENDEUOPRODUTO M(A)

Importante: na linguagem que usaremos na disciplina faz-se diferença entre letras


maiúsculas e minúsculas no nome das variáveis, sendo assim os seguintes nomes de
variáveis são todos diferentes: ABC, ABc, AbC, Abc, aBC, ABc, abC e abc. Portanto eles
correspondem a variáveis diferentes dentro do programa. Da mesma forma o caracter
sublinha é levado em conta, sendo assim, varíaveis como: Custo_Do_Produto e
CustoDoProduto são diferentes.

3.3. Tipos de Dados Primitivos


Além disso toda variável, da mesma forma que as constantes, também tem que ter um tipo
de dados: inteiro, real, lógico, caracter ou string. Embora não seja obrigatório em todas as
linguagems de programação, geralmente as variáveis tem que ser declaradas antes de ser
usadas. A declaração serve para avisar (ao computador) que uma dada variável com um
dado nome e tipo de dados deve ser criada. Na nossa disciplina estaremos assumindo que
todas as variáveis tem que ser declaradas antes de ser usadas.

As declarações de variáveis são expressões na forma:


<tipo-de-dados> <nome-da-variável> , ... , <nome-da-variável>. ;

Onde <tipo-de-dados> e <nome-da-variável> são termos que podem ser substituidos por
palavras que definem, respectivamente, o tipo de dados da variável e o nome da variável.
Como pode ser visto na definição acima, também pode-se definir mais de uma variável por
vez, bastando separar seus nomes por vírgulas. O caracter ponto-e-vírgula no fim da
declaração é obrigatório.

O termo <tipo-de-dados> pode ser substituído por:

23 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

int para indicar que será uma variável de tipo numérico inteiro

float para indicar que será uma variável de tipo numérico real

char para indicar que será uma variável para um caracter

bool para indicar que será uma variável lógica (booleana).

As variáveis string são definidas através do tipo caracter (char), dizendo-se qual o tamanho
do string (ou cadeia) de caracteres. A definição é um pouco diferenta da apresentada acima:

char <nome-da-varável> [<tamanho-do-string>] ;

onde os abre e fecha colchetes são obrigatórios e onde <tamanho-do-string> é uma


constante numérica inteira.

Exemplos:
int nota,codigo,X1;
bool teste,sim;
char c1,c2;
char nome[50], cargo[50], descricao_do_produto[200];

3.4. Comentários
Um comentário, num algoritmo ou programa, é um trecho de texto que serve apenas para
aclarar para um leitor humano, detalhes do que está sendo feito. Os comentários poderão
ser colocados nos programas/algoritmos de duas formas:
/* entre barra-asterisco e asterisco-barra */
ou
// após duas barras e até o fim da linha.
no caso de comentários entre /* e */ eles podem se estender por várias linhas:
/*
Este e’ um comentario
que se estende por
cinco linhas distintas
*/

3.5. Expressões Aritméticas


Denomina-se expressão aritmética aquela cujos operadores são aritméticos e cujos
operandos são constantes e/ou variáveis numéricas. O conjunto básico de operadores é o
tradicionalmente empregado na matemática, apenas que adaptado aos teclados e caracteres
disponíveis nos computadores:

24 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

Tabela 2 - Operações Aritméticas Binárias


operação símbolo (caracter)
soma +
subtração -
multiplicação *
divisão /
resto da divisão inteira %
O operador / serve tanto para a divisão de números inteiros quanto reais. O operador %
quando usado entre números inteiros retorna o resto da divisão inteira e quando usado entre
reais retorna apenas o resultado da divisão (igual ao operador /). Exemplos:
A + 12 x - 25 0.23 * 44
12 / 13 17 % 3 2.3 + 8.2 * y
6 + b / 4 - 34

A precedência é a usual da matemática: primeiro são feitas as operações multiplicativas: *,


/ e %, depois são feitas as aditivas: +. -.

Quando várias operações aritméticas de mesmo nível de precedências são postas em


sequência, normalmente se considera que a execução das operações começa da esquerda
para a direita, isto é, a associatividade usual das operações aritméticas é da esquerda para a
direita.

O uso de parênteses é livre, devendo ser utilizados quando se quer forçar uma precedência
ou associtiavidade não usual ou quando se quer deixar a expressão mais clara e legível.
Podem existir expressões entre parênteses aninhadas dentro de outras expressões entre
parênteses. A única restrição é que os parêntese estejam “pareados”, ou seja, que nunca
falte parênteses a esquerda ou a direita. Exemplos:
(2+3) * X Y/(2 * (Z+44 % C)) ((indice+1)/(total*2)) % valor_maximo

Exercícios:

(3.c) Descubrir a ordem em que são feitas as operações nas seguintes expressões:
8 * 23 % 14 + 86 - 23 + 78 / 12
14 * 8 - 4 + 7 * 17

(3.d) Coloque parênteses nas duas expressões da questão (a) para explicitar como é a
precedência e associatividade usual.

(3.e) Coloque parênteses nas duas expressões da questão (a) de forma a forçar que as
operações aditivas sejam feitas antes das multiplicativas.

Além das operações que necessitam de dois operandos (operações binárias), existem
também as operações unárias (com apenas um operando):

25 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

Tabela 3 - Operações Aritméticas Unárias


operação símbolo (caracter)
número positivo +
número negativo -
incremento ++
decremento --
Os símbolos - e + podem ser usados apenas na frente de uma constante numérica para
indicar o sinal da constante (se é positiva ou negativa). Dessa forma as expressões abaixo
(como nas fórmulas matemáticas comuns) são perfeitamente corretas:
2 + -4 -3 * +4 -4 * (16 + -2)

Os símbolos ++ e -- são usados para executar operações de, respectivamente, incremento


(somar 1) e decremento (subtrair 1) sobre uma variável. Estes operadores podem ser usados
tanto na frente (pré-fixados) quanto atrás (pós-fixados) de um operando. Exemplos:
3 * ++n 4 + i--

Importante:

• Note que os símbolos + e - somente podem ser usados como sinais de positivo e
negativo se estiverem na frente do número (são operadores unários pré-
fixados).
• Os símbolos ++ e -- são formados por 2 caracteres ‘+’ ou ‘-’ sem nenhum
espaço em branco entre eles. Caso contrário poderia haver confusão entre o
sinal de um número e as operações de incremento/decremento.
• Não se esqueça que estes operandos somente podem ser usados com variáveis e
não com constantes numéricas.
• A precedência tanto dos operadores unários +, -, ++ e -- é mais alta que a dos
operadores binários. Na verdade é a mais alta de todos os operadores, exceto se
se utilizar os parênteses para mudar a precedência.
• Existem uma pequena diferença entre usar os operadores de
incremento/decremento pré-fixados ou pós-fixados, esta diferença tem a ver
com a operação de atribuição e será vista quando a atribuição for descrita.

Exercícios:

(3.f) Escreva uma expressão onde a variável n tem que ser incrementada e usada para
multiplicar a soma de um número negativo com um número positivo.

(3.g) Determine o valor das seguintes expressões aritméticas:


15 / 12 15 % 12
24 / 12 24 % 12
123 / 100 123 % 100
200 / 100 200 % 100

(3.h) Qual o valor das seguintes expressões:

26 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

15 * 14 - 3 * 7 3 + 4 *(8 * ( 4 - (9 + 3) / 6))
- 4 * 5 * 2 4 * 3 * 5 + 8 * 4 * 2 - 5
(24 + 2 * 6) / 4 4 - 40 / 5
a / a / a * b (-5) % (-2)

(3.i) Escreva as seguintes expressões matemáticas em notação de linguagem de


programação:

x b xy
+1
y c+d 1 − 4 Zx

x+ y c xy
( a + b)
x−y d mn

y
x+
z [( a + b) 2 ]2 ( x + y ) 2 .(a − b)
y
x−
z

3.6. Expressões Relacionais


Uma expressão relacional é uma expressão que compara dois outros valores e retorna
verdadeiro ou falso dependendo do resultado da comparação.

Observação: na linguagem C os valores booleanos verdadeiro e falso são representados


por números inteiros de forma que o número 0 (zero) represente o valor falso e qualquer
número inteiro diferente de 0, represente o valor verdadeiro. Sendo assim uma expressão
relacional retornará 0 para indicar falso e um valor diferente de zero (normalmente 1) para
indicar verdadeiro.

As expressões relacionais tem o formato:


<expressão-1> <operador-relacional> <expressão-2>

onde <expressão-1> e <expressão-2> são expressões aritméticas e <operador-relacional> é


um dos seguintes operadores de comparação:

Tabela 4 - Operadores Relacionais


Operador Significado Exemplo
== Igual a x == y
!= Diferente de (não igual a) a != b
> Maior que m > n
< Menor que m < n
>= Maior ou igual a a >= b
<= Menor ou igual a x <= y

27 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

Nota: estes operadores são usados tipicamente em comandos de seleção e de iteração (a ser
vistos mais a seguir), entretanto nada impede de serem utilizados em comandos de
atribuição (também a ser visto logo a seguir), de forma a guardar o resultado de uma
comparação em uma variável inteira. Esta variável inteira passará a se comportar como uma
verdadeira variável booleana (com apenas valores verdadeiro ou falso). Sendo 0 o valor
equivalente a falso e 1 o valor equivalente a verdadeiro.

Exemplos:

Supondo x, a e b variaveis float, numero variável int e inicial do tipo char, as seguintes
expressões relacionais são válidas:
x < 5.75
b*b >= 5.0*a*c
numero == 100
inicial != ‘5’

Os operadores relacionais tem precedência menor que os operadores aritméticos, além disso
sua associatividade é da esquerda para a direita. Por exemplo:

m+5 <= 2*n equivale a (m+5) <= (2*n)

3.7. Expressões Lógicas


As expressões relacionais (ou variáveis lógicas) podem ser combinadas em expressões
lógicas mais complexas através dos operadores tradicionais da lógica booleana.

A linguagem C oferece os três operadores clássicos da lógica booleana: a negação lógico (o


NÃO ou o NOT lógico), a conjunção lógica (o E ou AND lógico) e a disjunção lógica (o
OU ou OR lógico):

Tabela 5 - Operadores Lógicos


Operador Operação Lógica Exemplo
! Negação lógica (NOT) !(x==y)
&& Conjunção lógica (AND) m<n && i>j
|| Disjunção lógica (OR) m==5 || n!=10

Considendo que variáveis lógicas em C, são variáveis inteiras cujo valor igual a 0 ou
diferente de 0 indica, respectivamente, a falsidade e a verdade, tem-se as seguintes tabelas
verdade para a aplicação dos operadores lógicos.

Tabela 6 - Tabela Verdade do NOT (!)


a !a
V(!=0) F(==0)
F(==0) V(==1)

28 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

Tabela 7 - Tabela Verdade do AND(&&) e do OR(||).


a b a && b a || b
V(!=0) V(!=0) V(==1) V(==1)
V(!=0) F(==0) F(==0) V(==1)
F(==0) V(!=0) F(==0) V(==1)
F(==0) F(==0) F(==0) F(==0)

Uma expressão relacional ou uma variável lógica (variável numérica inteira) são os
elementos mais básicos que também podem ser consideradas expressões relacionais, isto é,
uma <expressão-lógica> pode ser uma:
<expressão-relacional>
ou uma:
<variável-lógica>

Além disso as expressões lógicas básicas podem ser combinadas para formar expressões
mais complexas pelos operadores booleanos. O operador de negação (NÃO lógico) pode
ser aplicado da seguinte forma:
!<expressão-lógica>

Enquanto que os operadores de conjunção (E lógico) e disjunção (OU lógico) são


aplicados, respectivamente como:
<expressão-lógica> && <expressão-lógica>
e como:
<expressão-lógica> || <expressão-lógica>

A precedência dos operadores lógicos é a menor de todos os operadores vistos até agora: os
operadores aritméticos tem precedência sobre os relacionais, e os relacionais tem
precedência sobre os lógicos. Assim a expressão:
vendas < sal_min * 3 && taxa > 10 * coef_ipi
equivale a:
(vendas < (sal_min * 3)) && (taxa > (10 * coef_ipi))

Exemplos, supondo a, b e c variáveis inteiras (int), x e y variáveis reais (float):


(a<b) && (b<c)
(x>=5.0) || (x<=10.0)
!((a>10) && (b!=5))
y>2.45 || x==1.0

3.8. Tipos de Dados das Expressões


Toda expressão da linguagem C, seja ela aritmética, relacional ou lógica resulta num tipo
de dados. As expressões aritméticas podem resultar tanto no tipo caracter (char), inteiro
(int) quanto no tipo ponto-flutuante (float), dependendo dos tipos de dados das variáveis e
constantes empregadas.

As expressões relacionais resultam sempre no tipo inteiro. Da mesma forma uma expressão
lógica também é uma expressão de tipo inteiro, porque em C considera-se como um valor

29 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

verdadeiro dentro de um if ou while qualquer valor diferente de zero. Somente o valor zero
(0) é considerado falso.

No caso das expressões aritméticas, se forem usadas constantes e variáveis inteiras a


expressão será inteira, se forem usadas variáveis e constantes ponto-flutuante então a
expressão também será ponto-flutuante. Porém se forem misturadas variáveis ou constantes
de tipos distintos numa mesma expressão o compilador tratará de fazer a conversão
automática destes tipos de uma forma pré-definida. Nem sempre, entretanto, o tipo de
conversão de tipo feito pelo compilador é adequado.

Para forçar uma conversão explícita do tipo de uma expressão (pode ser uma variável ou
constante) a linguagem C oferece o operador de conversão de tipo (typecast). Este operando
tem a seguinte sintaxe:
( <novo-tipo> ) <expressão>

O campo <novo-tipo> define o novo tipo de dados da expressão contida em <expressão>.

Exemplo:
int i;
float f;

f = (float) (i/5);
printf( “f=%f\n”, f);

Um outro operador que se pode utilizar em C é o operador sizeof que retorna o tamanho do
tipo de dados ou da variável que lhe foi passado como parâmetro:

Exemplo:
char c;
int i;
float f;

printf( “O tamanho de um char eh:%d\n”, sizeof(char));


printf( “O tamanho da um int eh:%d\n”, sizeof(int));
printf( “O tamanho da variavel f eh:%d\n”, sizeof(f));

3.9. Expressões (Comandos) de Atribuição


Na linguagem C a atribuição de um novo valor a uma variável é feita através de um tipo
especial de expressão, chamada de expressão de atribuição.

Esta expressão tem o seguinte formato:

<variável> <operador-de-atribuição> <expressão>

Onde <variável> deve ser o nome de uma variável de mesmo tipo de dados (int,float,char)
que a expressão <expressão>.

30 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 3 - Elementos Básicos de C

A linguagem C possui vários tipos distintos de operadores de atribuição. O mais simples


operador de atribuição é o operador ‘=’, com a seguinte sintaxe:
<var> = <expressão>

Este operador simplesmente copia o valor calculada para a <expressão> para dentro da
variável <var>. Por exemplo, supondo que x seja uma variável inteira, então a expressão:
x = 5
copia armazena um novo valor (igual a 5) dentro da variável x.

Importante: deve se ler a expressão:


x = 5
não como “x igual a 5” mas como “(a variável) x recebe 5”. É muito importante recordar
isto: o operador = não é de igualdade mas de atribuição, qualquer valor antigo que
estivesse armazenado em x seria irremediavelmente perdido e x passaria a armazenar o
novo valor que é 5. O significado é completamente diferente no caso da expressão x==5
que testa se x é igual a 5 retornando verdadeiro se isto for verdade e falso caso contrário,
mas sem alterar de nenhuma forma o valor armazenado em x.

O operador de atribuição simples é associativo pela direita e pode ser usado várias vezes
numa mesma expressão. Sendo assim a expressão:
a = b = c = d = 0
equivale a:
a = (b = (c = (d = 0)))
e faz com que as variáveis a, b, c, d recebam o valor 0 (zera estas 4 variáveis).

Além disso diversas expressões de atribuição podem ser combinadas numa mesma
expressão maior através do operador vírgula ‘,’. Por exemplo, se quisermos podemos
inicializar as variáveis a,b,c com 10,20 e 30 por meio da seguinte expressão:
a=10, b=20, c=30
a precedência deste operador ‘,’ é a menor de todos, assim a expressão acima é lida como:
(a=10), (b=20), (c=30)

Além deste operador simples de atribuição a linguagem C oferece uma série de operadores
“extras” de atribuição que servem para abreviar expressões de atribuição utilizadas com
muita frequência:

Tabela 8 - Expressões Abreviadas


Operador Sentença abreviada Sentença não-abreviada equivalente
+= m += n m = m + n
-= m -= n m = m - n
*= m *= n m = m * n
/= m /= n m = m / n
%= m %= n m = m % n

Assim em vez de escrever


numero_de_dias = numero_de_dias + (i*2)
basta escrever:
numero_de_dias += i*2

31 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

Capítulo 4
Comandos Simples e Bloco de Comandos

Este capítulo apresenta os comandos mais simples que se podem usar na programação em
C, além de demonstrar como eles podem ser combinados para construir um programa C
elementar.

4.1. Comando de Atribuição e Sequência de Comandos


Uma expressão de atribuição pode ser usada diretamente como um comando ou sentença da
linguagem C. Para tanto basta escrever a expressão de atribuição e finalizá-la com o
marcador de fim de comando, o caracter ‘;’ (ponto-e-vírgula).

Os comandos simples e as declarações de variáveis são finalizados pelo caracter ‘;’. Estas
declarações e comandos podem ser postos em sequência num trecho de programa C, sendo
executadas pelo computador na sequência em que estão escritos no programa.

Existem, entretanto, algumas regras básicas que sempre devem ser seguidas:
• As declarações devem sempre aparecer antes do uso das variáveis, agrupadas no
início do programa (ou do trecho de programa sendo escrito).
• Mais de um comando ou declaração podem ser escritos na mesma linha do
programa fonte, basta separá-los apropriadamente por ‘;’ que não há problemas.
• Um comando ou uma declaração pode se estender por mais de uma linha de texto do
programa fonte, isto normalmente não é problema, porque o que indica para o
computador o fim do comando é o caracter ‘;’. Porém nomes de variáveis não
podem ser quebrados entre duas linhas.
• Além disso, um cuidado extra deve ser tomado com os literais string (“texto entre
aspas”). Normalmente estes literais string não podem simplesmente “saltar de linha”
que o compilador acusará um erro. Para evitar isto, basta colocar o caracter ‘\’ no
fim do string na linha onde haverá a quebra e continuar o string normalmente na
primeira coluna da próxima linha.

A execução dos comandos será feita pelo computador justamente na sequência normal em
que foram escritos: começando das primeiras linhas do programa e seguindo adiante um
comando por vez.

Importante: dentro de uma expressão pertencente a um comando a execução (ou cálculo)


atende as regras de precedência, associatividade e parênteses da expressão.

32 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

Exemplos:
// Declaracao das variaveis inteiras i,j,k, das variaveis
// reais x e y, e das variaveis caracter c1 e c2
int i,j,k;
float x,y;
char c1,c2;

// Atribuição de valores iniciais para estas variáveis


i = 1 ;
j = k = 0 ;
x = 3.1415 ;
y = 2.718 ;
c1 = ‘a’ ;
c2 = ‘\x2f’ ;

// Mais algumas atribuicoes


x /= y;
j += k + 1;
i *= 2;

Exercícios:

(4.a) Escrever um trecho de programa que faça a troca dos valores de 2 variáveis distintas.
Declare todas as variáveis necessárias.

(4.b) Supondo um valor real correspondente a largura e outro correspondente ao


comprimento de uma sala, escreva um trecho de programa que calcule a área desta sala.
Defina (declare) todas as variáveis necessárias.

(4.c) Escreva um programa que, a partir de um número dado de segundos, obtenha o


número equivalente em horas, minutos e segundos.

4.2. Comandos de Entrada e Saída

4.2.1. A Função scanf

Os comandos de entrada e saída nos permitem construir programas que interagem com o
usuário. Para que um programa possa receber dados do usuário e armazená-los em
variáveis, a linguagem C possui um conjunto de comandos que permitem que o operador
interaja com o programa fornecendo os dados quando estes forem necessários.

O comando que serve para fazer a leitura de dados pelo teclado é implementado através da
rotina (ou função) scanf:
scanf( “string de formatação”, <lista-de-parâmetros>);

onde o string de formatação será especificado abaixo.

33 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

A lista de parâmetros deve conter exatamente o mesmo número de parâmetros quantos


forem os comandos de formatação do string de formatação. Cada parâmetro desta lista é
formado por um endereço de variável, isto é feito simplesmente se colocando um & antes
do nome da variável.

Cada comando de formatação de entrada começa por um %, seguido por uma letra que
identifica o formato de dados que deve ser lido (por exemplo 'd' para número decima, 's'
para string, etc.).

Um caracter não branco no string faz com que scanf leia e desconsidere um caracter
coincidente. Por exemplo, o string de controle “%d,%d” faz com que scanf primeiro leia
um inteiro, depois leia e desconsidere um vírgula e, finalmente, leia outro inteiro. Se não
houver ‘,’, scanf será encerrado.

O comando scanf deve receber valores passados por seus endereços. Devemos lembrar que
C trabalha desta forma para chamadas por referência, permitindo que uma função altere o
conteúdo de um argumento, como em scanf(“%d”, &num). Para leitura de strings (que
são matrizes unidimensionais ou vetores de caracteres), o nome do string sem índice
informa o endereço do primeiro elemento do string, ou seja um ponteiro, portanto não
devemos usar &, como em scanf(“%s”, texto).

Exemplos de uso do scanf:


// Declara 2 variaveis numericas inteiras
int num1, num2;

// Declara uma variavel caracter


char c;

// Declara uma variavel string (isto é texto) de


// no maximo 100 caracteres
char texto[100];
// Declara uma variável ponto-flutuante (isto é numérica real)
float x;

// Agora le dados para estas variaveis


scanf(“%d %d”, &num1, &num2);
scanf(“%s”, texto);
scanf(“%c”, &c);
scanf(“%f”, &x);

4.2.2. A Função printf

O comando printf serve para a apresentação de dados textuais na tela do monitor:


printf(“string de formatação”, <lista-de-parâmetros> );

O string de formatação será impresso na tela, independente de conter algum comando de


formatação de saída ou não. Os comandos de formatação de saída, que também começam
com %, servem apenas para imprimir o conteúdo das variáveis do programa.

Da mesma forma que no scanf um comando de formatação do printf é indicado através de


um caracter % seguido de uma letra que indicará o formato (tipo de dados) da variável (são

34 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

usados as mesmas letras identificadoras de formato do scanf). Também igual que no scanf,
deve-se necessariamente ter tantos parâmetros quantos forem os comandos de formatação
no string de controle. Se isto não ocorrer, a tela exibirá sujeira ou não exibirá qualquer
dado.

Entretanto, diferente do scanf, no printf não se deve preceder as variáveis por um &.

Além dos comandos formatadores de entrada, alguns caracteres de controle especiais


podem ser usados nos strings a ser impressos pelo printf. Estes são os caracteres especiais
suportados por printf():

Tabela 9 - Caracteres Especiais do printf


Código Significado
\b Retrocesso (BackSpace)
\f Salto de Página (Form Feed)
\n Linha Nova (Line Feed)
\r Retorno do Carro (cr)
\t Tabulação Horizontal (TAB)
\’ Caracter com apóstrofo
\0 Caracter Nulo ou Fim de String (Seqüência)
\x Representação de byte na base hexadecimal

Por exemplo, printf(“\x41”); causa a impressão da letra A na tela.

Exemplos de uso do printf:

// Usando as mesmas variaveis dos exemplo de scanf


// Exemplos de printf

printf(“Lista de variaveis e valores:\n”);


printf(“num1=%d num2=%d\n”, num1, num2);
printf(“texto=%s\n”, texto);
printf(“c=%c\n”, c);
printff(“x=%f\n”, &x);

4.2.3. Comandos de Formatação de scanf e printf

Os principais comandos de formatação usados nas rotinas scanf e printf são apresentados
na tabela a seguir:

35 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

Tabela 10 - Comandos de Formatação do scanf e printf


Comando Descrição Observação
%c lê/imprime um único caracter
%d lê/imprime um um número inteiro decimal
%i lê/imprime um número inteiro decimal
%e lê/imprime um número com ponto flutuante Notação Científica
%f lê/imprime um número com ponto flutuante
%h lê/imprime um número inteiro curto
%o lê/imprime um número octal
%s lê/imprime um string (texto terminado por \0)
%x lê/imprime um número hexadecimal
%p lê/imprime um ponteiro
receber um valor inteiro igual ao número de caracteres
%n
lidos até o momento
especificará o total de caracteres a serem lidos para um
número
campo qualquer
%g Usa %e ou %f, aquele que for menor Somente printf
%% Imprime % Somente printf
%u Imprime um número decimal sem sinal Somente printf
h Modificador short
l Modificador long

Mais exemplos:
/* Exemplo Le e Mostra Idade */
main()
{
int idade;
char nome [30];
printf( “Digite sua Idade: “);
scanf( “%d”,&idade);
printf( “Seu Nome: “);
scanf( “%s”,nome); /* Strings não utilizar ‘&’ na leitura */
printf( “%s Sua idade e’ %d anos. \n”, nome, idade);
}

/*Exemplo: dado um número, calcular seu quadrado.*/

main()
{
int numero;
printf( “Digite um Numero: “);
scanf( “%d”,&numero);
printf( “O %d elevado ao quadrado resulta em %d. \n”,
numero, numero*numero);
}

36 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

4.3. Blocos de Comando


Um bloco de comandos em C é simplesmente um conjunto de comandos inseridos dentro
de abre e fecha chaves (dentro de ‘{’ e ‘}’):
{
<comando1>;
<comando2>;
.
.
.
<comandon>;
}

Não esqueça, que como na sequência normal de comandos, cada comando deve ter um
ponto-e-vírgula (‘;’) ao seu final;

Num bloco de comandos primeiro é executado o <comando1>, depois o <comando2> e


assim sucessivamente até o último <comandon>.

4.4. Um Programa C Básico


Até agora temos apresentado trechos de programa em C. Um programa C, entretanto,
requer mais elementos do que foi visto até agora para poder ser compilado e executado.

O processo de criação de um programa, começa com a criação do programa fonte C. Um


programa fonte é um arquivo texto que não tem caracteres especiais, criado por um editor
de arquivos texto (similar ao bloco de notas (notepad) do Windows ou ao editor vi do
Unix/Linux).

A geração do programa executável a partir do programa fonte obedece a uma seqüência de


operações antes de tornar-se um executável. Depois de escrever o módulo fonte em um
editor de textos, o programador aciona o compilador que no LINUX (ou no ambiente de
programação DJGPP/Windows) é chamado pelo comando gcc. Essa ação desencadeia uma
seqüência de etapas, cada qual traduzindo a codificação do usuário para uma forma de
linguagem de nível inferior, que termina com o executável criado pelo ligador (linker em
inglês):

37 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

Arquivo Fonte (.c)

Pré-Processador

Arquivo Fonte Expandido (.c)

Compilador

Arquivo Objeto (.o ou .obj) Bibliotecas (.lib)

Ligador

Arquivo Fonte
Figura 1 -Compilação de um Programa em C
Usualmente os programas fontes em C são identificados através da extensão no fim do
nome do arquivo, que deve ser “.c”. Abaixo é apresentado um pequeno programa fonte em
C, denominado de estou-vivo.c, que pode ser realmente compilado e executado:
/***************************
* estou-vivo.c *
* Um pequeno programa em C *
* que escreve uma mensagem *
* simples na tela. *
* Autor: Joao Gluz *
***************************/

#include <stdio.h>

int main()
{
printf(“Estou Vivo!\n”);
}

Este programa é extremamente simples, apenas exibe na tela a mensagem “Estou Vivo!”.
Apesar disso, algumas das principais características de um programa completo já aparecem
nele:

Embora não obrigatório é questão de bom-senso e boa prática de programação deixar claro
no inicio do programa, no mínimo:

38 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 4 - Comandos Simples e Bloco de Comandos

• o nome do programa (ou o nome do arquivo fonte correspondente),


• para que que ele serve (qual a sua função, utilidade ou objetivo dele) e
• o autor ou responsável por ele.

Logo a seguir, aparece um comando não visto até agora, o comando de pré-processador:
#include <stdio.h>

O comando #include do pré-processador é uma ordem para que o compilador inclua no


código fonte, antes de compilar o programa todo, o arquivo stdio.h (que deve ser
encontrado num diretório do sistema operacional). Os arquivos com a terminação “.h”
contém cabeçalhos de bibliotecas de rotinas ou funções que podem ser usados pelo
programa. No caso o arquivo stdio.h contém as definições da biblioteca de funções básicas
de entrada e saída da linguagem C (“std” é abreviação de standard, ou seja “padrão” em
português, e “io” é abreviação de input/output, que significa “entrada/saída” em português).

Logo a seguir é definida uma função importante que deve ser obrigatoriamente definida
num programa C, a função main:
int main()

Esta é a função principal do programa (main significa “principal” em português) e deve ser
definida para que o programa possa ser executado. A execução de um programa C, sempre
começa pela função main.

Mais a seguir será tratado com mais detalhes como podem ser definidas e usadas funções
na linguagem C, por agora basta dizer que uma função em C (aliás com em qualquer
linguagem ou também na matemática) precisa de alguns elementos para poder ser
completamente definida:
• qual tipo de dados ela retorna (o int na frente de main define isto),
• qual o nome da função (main), se tem alguns parâmetros adicionais (no caso main não
tem, visto que é seguida apenas de “()” ) e, por fim,
• como ela é implementada, que é definido pelo corpo da função. Em C o corpo da
função é sempre um bloco de comandos (e declarações).

Mais a seguir aparece o corpo da função que, neste caso, é um bloco de comandos simples:
{
printf(“Estou Vivo!\n”);
}

39 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 5 - Comandos de Seleção

Capítulo 5
Comandos de Seleção

Os comandos de seleção permitem escolher alternativas distintas para o fluxo de


execução de comandos. Eles podem mudar a direção do fluxo de execução das
instruções de acordo com algum teste (uma expressão relacional ou uma expressão
lógica). A linguagem C oferece três tipos básicos de comandos de seleção, que serão
vistos logo a seguir:
• Comando condicional if
• Comando de seleção if ... else
• Comando de seleção switch

5.1. Comando Condicional if


O comando mais simples de seleção de alternativas para a execução de comandos é o
comando condicional if (“se” em português). Este comando testa uma condição e se ela
for verdadeira executa o comando (ou bloco de comandos) associado ao if. Caso a
condição não seja verdadeira a este comando associado ao if não é executado e a
execução segue normal a partir do próximo comando após o if. O formato (sintaxe)
básica do if é:
if (<condição-if>)
<comando-if>;

ou, no caso de bloco de comandos, também pode-se ter:


if (<condição-if>)
{
<comando-if-1>;
<comando-if-2>;
.
.
.
<comando-if-n>;
}

A <condição-if> pode ser qualquer expressão relacional ou lógica apropriada, enquanto


que o <comando-if> (ou os <comando-if-1>, ..., <comando-if-n>) é o comando
executado se a <condição-if> for verdadeira.

Exemplo:
int n;
int m;

printf(“Entre com n e m:\n”);


scanf(“%d %d”, &n, &m);

40 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 5 - Comandos de Seleção

if (n>m)
prinf(“n e’ maior que m\n”);

5.2. Comando de Seleção if ... else


Além de uma seleção simples que permite executar um trecho de código quando uma
condição é verdadeira, seria interessante que houvesse a possibilidade de se executar
quando a condição é falsa (e somente quando ela é falsa).

Para tanto pode-se adicionar a cláusula else (“senão” em português) a um comando if. O
comando if assim modificado, chamado de if...else, testa uma condição e se ela for
verdadeira executa o comando (ou bloco de comandos) logo após a condição. Porém
caso a condição não seja verdadeira é executado o comado (ou bloco de comandos) após
o else. Somente após o comando após o if ou o comando após o else terem sido
executados é que a execução segue normal a partir do próximo comando após o if...else.
O formato (sintaxe) mais simples do if...else é:
if (<condição-if>)
<comando-if>;
else
<comando-else>;

mas também como no caso do if pode-se substituir <comando-if> ou <comando-else>


por blocos de comandos, se isto for necessário. Note que, neste caso, igual que no if não
é necessário colocar ponto-e-virgula (‘;’) após o fecha-chaves (‘}’) ).

Da mesma forma que no if a <condição-if> pode ser qualquer expressão relacional ou


lógica apropriada. O <comando-if> será o comando executado se <condição-if> for
verdadeira e o <comando-else> será o comando executado se a <condição-if> for falsa.

Exemplo:
int n;
int m;
int result;

printf(“Entre com n e m:\n”);


scanf(“%d %d”, &n, &m);
if (m==0)
prinf(“Impossivel dividir n por m, m e’igual a zero\n”);
else {
result = n/m;
printf(“O resultado da divisao de n por m e’%d\n”,result);
}

Sequencias encadeadas de if...else

Os comandos if...else podem ser encadeados para testar várias condições distintas e
executar apenas o comando correspondente a condição verdadeira. Note que este
encadeamento não trás nenhum comando novo, é apenas uma forma de se escrever uma
sequências de comandos if...else. A forma usual destas sequências é:
if (<condiçao-if-1>)
<comando-condição-if-1>;

41 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 5 - Comandos de Seleção

else if (<condição-if-2>)
<comando-condição-if-2>;
.
.
.
else if (<condição-if-n>)
<comando-condição-if-n>
else // opcional
<comando-executado-se-todas-condições-falsas>; // opcional

Exemplo:
int dia_da_semana;

printf(“Entre com o dia da semana (1=dom, 2=seg, ...):\n”);


scanf(“%d”, &dia_da_semana);
if (dia_da_semana==1)
printf(“Hoje e’ domingo\n”);
else if (dia_da_semana==2)
printf(“Hoje e’ segunda\n”);
else if (dia_da_semana==3)
printf(“Hoje e’ terca\n”);
else if (dia_da_semana==4)
printf(“Hoje e’ quarta\n”);
else if (dia_da_semana==5)
printf(“Hoje e’ quinta\n”);
else if (dia_da_semana==6)
printf(“Hoje e’ sexta\n”);
else if (dia_da_semana==7)
printf(“Hoje e’ sabado\n”);
else
printf(“Dia invalido\n”);

5.3. Comando de Seleção switch


O comando switch é usado para selecionar um dentre vários valores possíveis de uma
expressão numérica inteira (int) ou de uma expressão que retorne um caracter (char).
Este comando não serve para expressões numéricas reais ou ponto-flutuante (float),
nem para strings. O resultado da expressão é usado como um seletor que irá escolher
qual trecho de código deve ser executado.

A sintaxe do switch é:
switch(<seletor>)
{
case <valor-1>: <lista-comandos-valor-1>;
break;
case <valor-2>: <lista-comandos-valor-2>;
break;
.
.
.
case <valor-n>: <lista-comandos-valor-n>;
break;
default: <lista-comandos-valor-default>; // opcional
}

42 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 5 - Comandos de Seleção

O <seletor> é uma expressão numérica inteira ou caracter, o valor calculado para esta
expressão será usado para escolher qual lista de comandos será executada, da seguinte
forma: se o resultado for igual a <valor-1> então a <lista-comandos-valor-1> será
executada, porém se o resultado for igual a <valor-2> então a <lista-comandos-valor-2>
será executada, e assim por diante até ser testado o último valor <valor-n>.

Como o resultado somente pode ser igual a um dos valores, somente uma das listas de
comados será executada.

Porém se o resultado de <seletor> não for igual a nenhum dos valores, então pode-se
usar a cláusula default: para executar uma lista de comandos nesta condição. O uso
desta cláusula default: num comando switch é opcional e deixado a cargo do
programador.

Pode-se colocar vários comandos em qualquer uma das <lista-comandos-valor-1> até


<lista-comandos-valor-n>, sem a necessidade de agrupá-los num bloco (entre ‘{’ e ‘}’).

O comando break ao fim de cada uma das listas de comandos é obrigatório e garante
que após a execução da lista correspondente a execucação passe para o próximo
comando após o switch.

Exemplo:
int dia_da_semana;

printf(“Entre com o dia da semana (1=dom, 2=seg, ...):\n”);


scanf(“%d”, &dia_da_semana);
switch(dia_da_semana)
{
case 1: printf(“Hoje e’ domingo\n”);
break;
case 2: printf(“Hoje e’ segunda\n”);
break
case 3: printf(“Hoje e’ terca\n”);
break;
case 4: printf(“Hoje e’ quarta\n”);
break;
case 5: printf(“Hoje e’ quinta\n”);
break;
case 6: printf(“Hoje e’ sexta\n”);
break;
case 7: printf(“Hoje e’ sabado\n”);
break;
default: printf(“Dia invalido\n”);
}

5.4. Usando Comandos Condicionais


A seguir apresentamos um outro programa, um pouco mais complicado que introduz
alguns comandos condicionais de um programa C:
/***************************
* calc-raio-esfera.c *
* Um programa que descobre *
* o raio de uma esfera a *

43 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 5 - Comandos de Seleção

* a partir da area desta. *


* Autor: Joao Gluz *
***************************/

// Bibliotecas de Rotinas usadas pelo programa


#include <stdio.h>
#include <math.h>

// Constantes (defines)do programa


#define CONSTANTE_PI 3.1415
#define AREA_ZERO 0.0

// Variaveis globais do programa


float area;

int main()
{
float raio;

printf(“Entre com a area da esfera:\n”);


scanf(“%f”, &area);

if (area <= AREA_ZERO)


printf(“Erro, area negativa ou zero!\n”);
else {
// A partir do raio r, pode-se calcular a
// area A de um esfera por meio da formula:
// 2
// A = 4 * pi * r
//
// para descobrir o raio em funcao da area
// basta evidenciar r:
//
// r = rquad( A / (4*pi))
//
// que em C fica:

raio = sqrt( area / (4 * CONSTANTE_PI));


printf(“Uma esfera de area %f tem raio %f\n”, area, raio);
}

return 0;
}

Em primeiro lugar, pode-se usar mais de uma biblioteca de funções num programa:
#include <stdio.h>
#include <math.h>

Neste programa é incluido além do cabeçalho da biblioteca padrão a biblioteca de


funções matemáticas (<math.h>).

Além disso este programa, define algumas constantes para uso global:
#define CONSTANTE_PI 3.1415
#define AREA_ZERO 0.0

Em C as constantes são definidas através de uma outro comando de pre-processador: o


comando #define. Este comando faz com que todas as vezes que o símbolo definido

44 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 5 - Comandos de Seleção

logo a seguir ao #define (no caso CONSTANTE_PI ou AREA_ZERO) seja encontrado


no programa ele seja automaticamente substituído pelo texto logo a seguir (no caso a
constante numérica 3.1415 ou 0.0). Um programa pode ter várias constantes.

Depois é definida uma variável global do programa :


float area;

Esta variável poderá ser usada por todas as rotinas ou funções definidas dentro do
programa fonte. No caso só existe uma função neste programa, a função main, mas se
houverem mais funções então todas elas terão acesso a estas variáveis, dentro de um
mesmo arquivo fonte.

Também é definido, no corpo da função main, uma variável que somente poderá ser
usada localmente dentro da função (a variável raio)
int main()
{
float raio;
...

Por fim, a função main é finalizada através de um comando return:


...
return 0;
}

Este comando serve para indicar qual o valor a ser retornado por uma função. No caso
main foi declarada como retornando um valor inteiro (int), enão deve-se retornar um
valor deste tipo. Por definição, o valor retornado por main serve para indicar como foi a
execução do programa. Quando este retorna 0, isto indica que o programa executou sem
nenhum problema, que é justamente o valor retornado no fim de main no programa
acima.

Exercícios:

(5.a) Escreva um programa que determine e apresente o maior de três número lidos.

(5.b) Escreva um programa que verifique se três número lidos estão em ordem
crescente.

(5.c) Escreva um programa que escreva o conceito equivalente a um valor numérico


entre 0 e 100, de acordo com a seguinte classificação:
0 “F”
de 1 até 49 “D”
de 50 até 69 “C”
de 70 até 89 “B”
de 90 até 100 “A”

(5.d) Quais são os erros de sintaxe do seguinte comando:


if x > 25.0
y = x
else

45 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 5 - Comandos de Seleção

y = z;

(5.e) A relação entre os lados (a,b) de um triângulo retângulo e a hipoteusa é dada pela
fórmula (Teorema de Pitágoras):
a 2 + b2 = h 2
Escreva um programa que leia os valores dos lados a,b e calcule e imprima o valor da
hipoteusa.

(5.f) A área A de um triâgulo cujos lados são a,b,c pode ser calculada pela fórmula:
A = p ( p − a )( p − b)( p − c )
onde p = (a+b+c)/2. Escreva um programa que leia os valores dos 3 lados do triâgula e
calcule e imprima a área deste.

(5.g) Escreva um programa que descubra se um ano lido é bissexto. Um ano é bissexto
se é múltiplo de 4, exceto quando ele é múltiplo de 100. Os anos múltiplos de 100
somente são bissextos quando são múltiplos de 400 (por exemplo 1800 não é bissexto,
mas 2000 é).

(5.h) Escreva um programa que simula uma calculadora simples. Devem ser lidos 2
números ponto-flutuante e um caracter. Se o caracter for ‘+’ se imprime a soma dos 2
números, se for ‘-’ a subtração, se for ‘/’ a divisão, se for ‘*’ a multiplicação. Use o
comando switch.

(5.i) Escreva um programa que leia um ano em formato decimal e o converta para
numeração romana. O ano lido deve estar no intervalo de 1000 até 2000 (inclusive).

Algumas regras de montagem de numerais romanos:


• Numerais romanos: I=1, V=5, X=10, L=50, C=100, D=500 e M=1000.
• Dois ou três numerais podem ser justapostos para, respectivamente, dobrar ou
triplicar o valor, assim: II=2, III=3, XX=20, XXX=30.
• Um numeral romano de valor menor à esquerda de um numeral de valor maior,
subtrai deste numeral maior, assim: IV = 4, XL=40, CM=900.
• Um, dois ou três numerais de valor menor à direita de um numeral de valor menor
somam, assim: VI=6, VII=7, VIII=8, DC=600, DCC=700, DCCC=800.
• Pode-se combinar um numeral maior com uma seq. de numerais cujo valor total é
menor, assim: MCM=1900, MCML=1950, MCMLX=1960, MCMXL=1940,
MCMLXXXIX=1989.

46 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 6 - Comandos de Repetição

Capítulo 6
Comandos de Repetição

Muitas vezes durante o desenvolvimento de um programa somos obrigados a executar


tarefas repetidas vezes. Quando o número de vezes é fixo, pequeno e conhecido de antemão
as vezes até é possível simplesmente se escrever um código que repita a operação que se
quer fazer tantas vezes quanto necessário.

Por exemplo, se queremos totalizar a soma dos 10 primeiros numeros naturais em uma
variável, nós sempre podemos escrever um código similar ao seguinte:
// A variavel s sera’ usada para acumular o resultado
int s;

// Agora faz a soma


s = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10;

Porém se o número de repetições é muito grande (por exemplo que tal somar os 100
primeiros naturais ou os 1000 primeiros?), ou então, quando o número de repetições tem
que ser lido de fora do programa ou calculado de alguma outra forma, ou seja, não é
conhecido de antemão, então simplesmente repetir os comandos não serve mais como
solução (isto sem levar em conta o tamanho do programa, que pode ficar exagerado com
tanta cópia e repetição de trechos de código).

Felizmente as linguagens de programação tem comandos específicos para tratar da


repetição de tarefas ou comandos. Estes comandos são denominados de Comandos de
Repetição e servem especificamente para efetuar tarefas repetitivas dentro de um
programa. Tais comandos também são denominados de Comandos Iterativos, onde
iteração (assim mesmo, sem o “n” de interação) é essencialmente um sinônimo de
repetição.

Estes comandos também são chamados de Comandos de Laço, porque, o fluxo de


execução de instruções que o programa está percorrendo, forma efetivamente um “laço”, de
forma que o programa volta para trás para executar novamente um conjunto de instruções
ou comandos.

A linguagem C oferece três destes comandos:


• o comando while,
• o comando for e
• o comando do ... while

Tais comandos serão vistos logo a seguir.

47 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 6 - Comandos de Repetição

6.1. Comando de Repetição while


O comando while (“enquanto” em português) serve para repetir um trecho de programa
enquanto uma dada condição permanecer verdadeira. Este trecho de código associado ao
while será executado tantas vezes quanto necessário, até que a condição seja falsa.

Este comando primeiro testa uma condição e se ela for verdadeira executa o comando (ou
bloco de comandos) associado ao while. Caso a condição não seja verdadeira no primeiro
teste então o comando associado ao while simplesmente não é executado nenhuma vez e a
execução segue normal a partir do próximo comando após o while.

Porém se o comando associado ao while for executado uma vez, então após sua execução a
condição do while é novamente testada. Se for falsa desta vez, então, como comentado
acima, a execução segue normal a partir do próximo comando após o while. Este processo
continuará até que a condição do while seja falsa.

Importante: se, por algum erro de programação, esta condição não se tornar falsa então o
programa simplesmente nunca mais parará, ele irá permanecer em laço para sempre (ou até
ser interrompido de outra forma pelo sistema operacional - um CTRL-C ou, no pior dos
casos, um CTRL-ALT-DEL por exemplo).

O formato (sintaxe) básica do while é:


while (<condição-while>)
<comando-while>;

ou, no caso de bloco de comandos, também pode-se ter:


while (<condição-while>)
{
<comando-while-1>;
<comando-while-2>;
.
.
.
<comando-while-n>;
}

A <condição-while> pode ser qualquer expressão relacional ou lógica apropriada, enquanto


que o <comando-while> (ou os <comando-while-1>, ..., <comando-while-n>) é o comando
executado enquanto a <condição-while> for verdadeira.

Exemplo:

Uma primeira versão de um programa que calcula a soma dos n primeiros naturais, para um
n lido:
/***************************
* calc-soma-n-v1.c *
* Um programa que calcula *

48 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 6 - Comandos de Repetição

* soma dos n primeiros *


* naturais (n lido) *
* Autor: Joao Gluz *
***************************/

#include <stdio.h>

int main()
{
int n, s, i;

printf(“Entre com o numero de naturais:\n”);


scanf(“%d”, &n);

if (n <= 0)
printf(“Somente trabalho com naturais!\n”);
else {
s = 0;
i = 0;
while (i<=n)
{
s = s+i;
i = i+1;
}
printf(“A soma dos primeiros %d naturais e’: %d”,
n, s );
}

return 0;
}

Agora, uma nova versão deste programa que usa mais recursos de expressão da linguagem
C (uma versão um pouco mais similar ao que você encontraria num ambiente normal de
programação C):
/***************************
* calc-soma-n-v2.c *
* Um programa que calcula *
* soma dos n primeiros *
* naturais (n lido) *
* Autor: Joao Gluz *
***************************/

#include <stdio.h>

int main()
{
int n, s, i;

printf(“Entre com o numero de naturais:\n”);


scanf(“%d”, &n);

if (n <= 0)
printf(“Somente trabalho com naturais!\n”);
else {
s = i = 0;

49 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 6 - Comandos de Repetição

while (i<=n)
{
s += i;
i++;
}
printf(“A soma dos primeiros %d naturais e’: %d”,
n, s );
}

return 0;
}

Note o uso de atribuição múltipla:


s = i = 0;
o uso do operador combinado de soma e atribuição:
s += i;
e o uso do operador de incremento de variáveis:
i++;
que são muito comuns na programação C.

6.2. Comando de Repetição for


É muito comum que os trechos de código envolvendo repetição usando o comando while
recaiam numa estrutura similar a seguinte:
<inicialização das variáveis de controle do laço>
while ( <condição envolvendo as variáveis de controle do laço>
{
<comandos a serem executados repetidas vezes>;
<atualização dos valores das variaveis de controle do laço>;
}

Ou seja, primeiro as variáveis que serão usadas para controlar o laço são inicializadas,
depois o teste do while é construído de forma usar estas variáveis. Se a condição for
verdadeira, então o trecho de código do while é executado. Porém este trecho está
estruturado de forma que primeiro são executadas os comandos a serem repetidos e por fim
(no fim do laço) é feita a atualização das variáveis de controle para preparar a nova etapa
do laço.

Esta estrutura é tão comum que a linguagem C oferece um comando de laço específico para
utilizá-la: o comando for, que tem a seguinte estrutura:
for (<inicialização-das-variáveis>;
<condição-do-laço>;
<atualização-das-variáveis> )
<comando-repetido>;

(como em todos os outros comandos, também existe a opção do <comando-repetido> ser,


na verdade, um bloco inteiro de comandos).

50 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 6 - Comandos de Repetição

A expressão <inicialização-de-variáveis> é uma expressão de atribuição que coloca valores


iniciais apropriados nas variáveis de controle do laço. Pode-se usar o operador vírgula (‘,’)
para executar atribuições múltiplas, se isto for necessário.

A expressão relacional ou expressão lógica <condição-do-laço> é a condição que testa as


variáveis de controle do laço, verificando se o laço deve prosseguir ou não.

O comando <comando-repetido> é o trecho de código a ser repetido pelo laço.

Por fim a expressão <atualização-das-variáveis> é uma expressão de atribuição que atualiza


os valores das variáveis para a próxima etapa do laço (também pode se usar a vírgula (‘,’)
para separa atribuições múltiplas).

Exemplo:

Mais uma nova versão do programa somatório de naturais, somente que desta vez usando o
laço for (mais similar ainda a programação usual em C):
/***************************
* calc-soma-n-v3.c *
* Um programa que calcula *
* soma dos n primeiros *
* naturais (n lido) *
* Autor: Joao Gluz *
***************************/

#include <stdio.h>

int main()
{
int n, s, i;

printf(“Entre com o numero de naturais:\n”);


scanf(“%d”, &n);

if (n <= 0)
printf(“Somente trabalho com naturais!\n”);
else {
for (i=0,s=0; i<=n; i++)
s += i;
printf(“A soma dos primeiros %d naturais e’: %d”,
n, s );
}

return 0;
}

Note a expressão de inicialização das variáveis, incluindo a variável de controle i e a


variável somatório s:
i=0,s=0
a condição de teste do laço, com a variável de controle i:
i<=n
e a expressão de atualização (um simples incremento) da variável de controle i:

51 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 6 - Comandos de Repetição

i++

O comando a ser repetido, por sua vez, se torma apenas a soma dos naturais:
s += i;

6.3. Comando de Repetição do...while


Todos os comandos de repetição vistos até agora sempre testam uma condição antes de
entrar no laço, ou seja, tanto o while quanto o for primeiro testam sua condição e somente
depois, se a condição for verdadeira, executam alguma coisa do laço.

Porém as vezes será necessário executar o próprio trecho de código do laço pelo menos
uma vez, sem necessariamente testar a condição. Somente depois que este trecho é
executado é que a condição é testada e aí valem as mesmas regras vistas até agora, se ela
for verdadeira então o trecho de programa do laço volta a ser repetido, caso contrário o laço
para e a próxima instrução é executada.

O comando que faz isto em C é o comando do...while que tem a seguinte sintaxe básica:
do <comando-do-while>;
while (<condição-do-while>);

ou, no caso de bloco de comandos, também pode-se ter:


do {
<comando-do-while-1>;
<comando-do-while-2>;
.
.
.
<comando-do-while-n>;
}
while (<condição-do-while>);

A expressão <condição-do-while> pode ser qualquer expressão relacional ou lógica


apropriada, enquanto que o <comando-do-while> (ou os <comando-do-while-1>, ...,
<comando-do-while-n>) é o comando executado enquanto a <condição-do-while> for
verdadeira. Exceto que no caso do comando do...while, o trecho de código <comando-do-
while> é executado pelo menos uma vez, independente do valor de <condição-do-while>.

Exemplo:

Programa que imprime todas as potências de 2 entre 1 e n, para um n lido:


/***************************
* imprime-pot-2.c *
* Um programa que imprime *
* potencias de 2 de 1 a 30 *
* Autor: Joao Gluz *
***************************/

52 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 6 - Comandos de Repetição

#include <stdio.h>

int main()
{
int n, potencia;

potencia = 1;
n = 1;
do {
printf(“2 elevado a %d = %d\n”, n, potencia);
potencia = potencia*2;
n++;
}
while (n<=30);

return 0;
}

Exercícios:

(6.a) Escreva um programa que calcule a e apresente o maior, o menor e a média de N


números. O valor de N deve ser lido no inicio do programa e os demais número devem ser
fornecidos pelo usuário.

(6.b) Determine se um número lido do teclado é primo ou não.

(6.c) Calcule a soma da série 1/1 + 1/2 + 1/3 + ... + 1/N onde N é um valor lido do teclado.

(6.d) Calcule a soma dos termos da série:


1 2 3 4 n
+ 2 + 3 + 4 + ... + n
2 2 2 2 2
para um n lido do teclado.

(6.e) Fazer um programa que calcule o valor de ex através da série:


x1 x 2 x 3 x 4
e x = x0 + + + + + ...
1! 2! 3! 4!
de modo que o mesmo difira do valor calculado através da função exponencial exp de, no
máximo, 0,0001. O valor de x deve ser lido. O programa deverá escrever o valor de x, o
valor calculado através da série, o valor dado pela função exp e o número de termos
utilizados da série. Não esqueça de incluir <math.h>.

(6.f) Escreva um programa que encontre os N primeiros números perfeitos, N deve ser
fornecido pelo usuário. Um número perfeito é aquele cuja soma de seus divisores, exceto
ele próprio, é igual ao número. Ex.: 6 = 1 + 2 + 3.

(6.g) Calcule e imprima os primeiros N números primos, para um N lido do teclado.


(Sugestão: um número n será primo se nenhum dos quocientes n/2, n/3, n/4, . . . n/sqrt(n)
for inteiro.)

53 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

Capítulo 7
Funções e Módulos

Uma função é um miniprograma dentro de outro programa. Uma função contém vários
comandos sob um só nome. Esta função pode ser “chamada” de outros pontos do programa,
cada vez que se quer executar estes comandos.

Além disso uma função pode ter “parâmetros”, isto é, argumentos que podem ser indicados
junto com o nome da função de forma que sequências ou fluxos diferentes de comandos,
sejam, executadas para valores diferentes destes parâmetros.

Uma função também pode, opcionalmente, retornar um valor de saída que pode ser usado
no trecho de código que chamou esta função.

A linguagem C é uma linguagem de programação estruturada que permite dividir um


programa em diversas funções (e módulos) que irão tratar e resolver partes do problema ao
qual o programa deve solucionar.

Na verdade simplesmente é impossível programar em C sem usar funções. Por exemplo o


próprio ponto de partida na execução de um programa é definido através de uma função:
afunção main, que significal “principal” em inglês, ou seja, um programa sempre começa
sua execução através da sua função principal (a função main).

Para trabalharmos com funções é necessário executar duas tarefas distintas:


• definir ou declarar a função num ponto do programa
• usar ou chamar esta função em outro ponto deste programa

7.1. Declaração de Função


Uma função deve ser definida ou declarada antes de ser utilizada. A definição de uma
função é feita especificando-se os seguintes elementos:
• nome da função,
• qual o tipo de dados que a função retornará (se ela retornar alguma coisa),
• quais parâmetros a função poderá receber,
• qual é o “corpo da função” que é o trecho de código a ser executado quando a função é
chamada e
• dentro do corpo da função, definir qual será o valor retornado por ela (se necessário)

Estes 5 elementos distintos são normalmente estruturados de acordo com o seguinte


formato:
<tipo-da-função> <nome-da-função> ( <lista-de-parâmetros> ))

54 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

{
<declaração-de-variáveis-1>;
<declaração-de-variáveis-2>;
...
<comando-1>;
<comando-2>;
...
return <valor>;
}

O <tipo-da-função> pode ser substituído por qualquer tipo válido usado em C, por
exemplo: int, char, float, etc. Se a função não deve retornar nenhum valor então o tipo da
função deve ser void (tipo “vazio” em inglês).

O <nome-da-função> define qual será o nome desta função. Um nome de função segue as
mesmas regras de um nome de variável em C: deve começar por uma letra (ou um caracter
sublinha ‘_’), seguido de letras, digitos e caracteres sublinha. Não podem haver duas
funções com o mesmo nome, nem com o nome igual ao de uma variável global.

A <lista-de-parâmetros> é formada por uma lista, separada por vírgulas, que define os
parâmetros da função. Cada parâmetro é definido de forma igual ao da definição de uma
variável, isto é, deve ser composto de um identificador de tipo de dados seguido do nome
da variável a ser usada como parâmetro. A lista de parâmetro é opcional, se uma função não
tem parâmetros então ela não precisa ser definida. Porém os abre e fecha-parênteses no fim
do nome da função (mesmo sem nenhum parâmetro dentro) devem ser digitados.

Os <comando-1>, <comando-2>, ... são quaisquer comandos da linguagem C, inclusive


chamada de função.

O comando return encerra a execução da função no ponto onde for executado. O


parâmetro <valor> deste comando será o valor retornado pela função. Se a função não
retornar nenhum valor pode-se usar apenas return; sem especificar qualquer valor de
retorno. Além disso, a função pode encerrar normalmente sua execução se alcançar o fecha-
chaves ‘}’ final, sem passar por um return e se não tiver que retornar nenhum valor.

A seguir é apresentado um exemplo de declaração de função fatorial que irá calcular o


fatorial de um dado número:
int fatorial( int n )
{
int i;
int fat;

fat = 1;
for (i=1; i<=n; i++)
fat = fat * i;

return fat;
}

55 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

7.2. Estrutura de um Programa


Um programa em C é organizado como um conjunto de funções. Tipicamente as funções
são definidas logo após as definições de constantes, tipos de dados e variáveis globais.
Todas as funções em C são iguais, exceto pela função main que é a única função chamada
“de fora” do programa, isto é, a execução de um programa começa sempre pelo primeiro
comando da função main. Afora isto a função main é igual a qualquer outra função.

A estrutura geral de um programa poderia ser similar a seguinte:


/***************************
* <nome-do-programa> *
* *
* <descrição> *
***************************/

// Includes
#include <stdio.h>
...

// Constantes
#define ...
...

// Tipos de dados
typedef ...
struct ...
...

// Variáveis globais
int ...
float ...
...

// Funções
int fun1( int par1 )
{
...
}

float fun2( float x, float y )


{
...
}

...

// Função Principal
int main()
{
return 0;
}

56 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

7.3. Chamada de Função


Para que uma função seja executada ela deve ser chamada. Meramente declarar uma função
não garante que ela seja executada (exceto apenas pela função main). Assim para que o
código associado a uma função seja executado é necessário chamar (ou usar) a função.

Uma função é chamada simplesmente pelo nome da função, seguido dos valores dos
parâmetros que esta função possa vir a ter. Se a função não tiver nenhum parâmetro basta
colocar abre e fecha-parênteses no fim do nome ‘()’.

Se a função retornar o valor basta usar o nome da função como se fosse uma outra
constante qualquer da linguagem. Uma função pode ser usada em qualquer lugar onde uma
constante de mesmo tipo de dados que a função poderia ser usada. Se a função não retornar
nenhum valor (tipo void) então ela deve ser usada apenas como um comando simples, sem
estar incluída dentro de uma expressão. Funções também nunca podem estar do lado
esquerdo do comando de atribuição (o comando =).

Por exemplo o programa a seguir define (novamente) e usa a função fatorial, imprimindo
uma tabela com o fatorial dos n primeiros naturais:
/***************************
* tabela-fatoriais.c *
* Imprime a tabela dos *
* n primeiros naturais *
* (n lido) *
* Autor: Joao Gluz *
***************************/

#include <stdio.h>

int fatorial( int n )


{
int i;
int fat;

fat = 1;
for (i=1; i<=n; i++)
fat = fat * i;

return fat;
}

int main()
{
int n, i;

printf(“Entre com o numero de naturais:\n”);


scanf(“%d”, &n);

if (n <= 0)
printf(“Somente trabalho com naturais!\n”);
else {
printf(“n fat(n)\n”);

57 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

printf(“--------------\n”);
i = 0;
while (i<=n)
{
// Chama a funcao fatorial de i e depois
// imprime i e o fatorial:
printf(“%d %d”, i, fatorial(i) );
i = i+1;
}
}

return 0;
}

7.4. Cabeçalhos de Função


Um cabeçalho de função tem o seguinte formato:
<tipo-da-função> <nome-da-função> ( <lista-de-parâmetros> ));

Note que ele é praticamente igual a declaração da função, exceto que neste caso o corpo da
função contendo o trecho de código a ser executado não deve ser declarado. Após a lista
de parâmetros da função, o cabeçalho deve ser seguido de um ponto-e-vírgula ‘;’.

O cabeçalho da função fatorial ficaria apenas:


int fatorial( int n );

7.5. Módulos
Não há restrição de quantas funções podem ser declaradas num programa. Além disso
funções podem ser declaradas em arquivos fonte distintos. Um módulo em C é um arquivo
fonte comum que contém uma ou mais funções que poderão ser usadas tanto por funções de
dentro do módulo quanto por funções que estão em outro módulo.

Para que uma função possa ser usada em outro módulo duas condições devem ser
satisfeitas:
• que o módulo que irá chamar (usar) uma função declarada em outro módulo conheça o
tipo de dados da função e
• que quando for gerado o programa executável final o compilador-ligador saiba quais
são e onde estão todos os módulos do programa.

A segunda condição é satisfeita informando na linha de comando do compilador uma lista


com os nomes de módulos (arquivos fonte) que compôe o programa.

58 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

Já a primeira condição é satisfeita definindo-se um arquivo-fonte auxiliar, denominado de


arquivo de cabeçalhos de funções (header file em inglês). Este header file é um arquivo
fonte comum que contém apenas os tipos de dados das funções disponíveis no módulo.

Um módulo será, então, composto de dois arquivos-fonte distintos:


• um arquivo-fonte com os cabeçalhos das funções, que tem a terminação “.h”
• um arquivo-fonte com as declarações das funções, que tem a terminação “.c”

O programa que imprime a tabela dos fatoriais poderia ser quebrado em 2 módulos:
• tabela-fatoriais
• fatorial

Sendo que o primeiro (tabela-fatoriais) conteria apenas a rotina main e o segundo


(fatorial) conteria apenas a função fatorial. Como o módulo tabela-fatoriais não tem
nenhuma função chamada por outro módulo ele não precisa de um header file. Porém como
o módulo fatorial tem a função fatorial chamada por outros módulos ele necessita de um
arquivo-fonte em C e também de um arquivo de header, ou seja, um arquivo fatorial.c e um
arquivo fatorial.h.

O arquivo fonte tabela-fatoriais.c ficaria:


/***************************
* tabela-fatoriais.c *
* Imprime a tabela dos *
* n primeiros naturais *
* (n lido) *
* Autor: Joao Gluz *
***************************/

#include <stdio.h>
#include “fatorial.”

int main()
{
int n, i;

printf(“Entre com o numero de naturais:\n”);


scanf(“%d”, &n);

if (n <= 0)
printf(“Somente trabalho com naturais!\n”);
else {
printf(“n fat(n)\n”);
printf(“--------------\n”);
i = 0;
while (i<=n)
{
// Chama a funcao fatorial de i e depois
// imprime i e o fatorial:
printf(“%d %d”, i, fatorial(i) );
i = i+1;
}

59 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

return 0;
}

Já o arquivo de cabeçalho fatorial.h é incluído em tabela-fatoriais.c no segundo #include,


contém apenas:
/***************************
* fatorial.c *
* Cabecalho das funções *
* disponibilizadas por *
* fatorial.c *
* Autor: Joao Gluz *
***************************/

int fatorial( int n );

Por fim o arquivo fatorial.c que contém as declarações de funções do módulo fatorial fica:
/***************************
* fatorial.c *
* Funcao de calculo do *
* fatorial de um numero *
* Autor: Joao Gluz *
***************************/

#include <stdio.h>

int fatorial( int n )


{
int i;
int fat;

fat = 1;
for (i=1; i<=n; i++)
fat = fat * i;

return fat;
}

Exercícios:

(7.a) Escreva um programa que calcule as áreas das seguinte figuras planas: círculo,
retângulo, elipse, triângulo e pentágono. O programa deve ser estruturado em dois módulos:
um módulo contendo a rotinas principal (rotina main) e outro módulo contendo uma
biblioteca de funções de cálculo de áreas. Cada função deste segundo módulo deve calcular
a área de uma figura distinta. Defina o arquivo de header apropriado. O programa principal
deve perguntar ao usuário qual figura ele quer calcular a área e depois dele selecionar o tipo
de figura ler os parâmetros correspondentes a figura (p.ex. altura e largura de um
retângulo), deve apresentar a área resultante na tela.

60 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

7.6. Parâmetros, Variáveis Locais e Variáveis Globais


Toda variável declarada dentro de um módulo C, mas fora de alguma função, é uma
variável global do módulo. As variáveis globais dentre de cada módulo de um programa
são criadas quando o programa entra em execução e continuam a existir enquanto o
programa estiver rodando.

Todas as funções de um módulo podem ter acesso a qualquer variável global definida
dentro do módulo, basta que a variável tenha sido declarada antes da função.

As variáveis definidas dentro do corpo da função, são chamadas de variáveis locais da


função. Estas variáveis locais somente passam a existir quando a função é chamada e
deixam de existir quando a função retorna. Os parâmetros de uma função também são
considerados variáveis locais da função, apenas que eles automaticamente recebem os
valores que a função chamadora colocou na lista de parâmetros. Assim, no exemplo da
função fatorial, na prática existem três variáveis locais: n, i e fat. Só que a “variável” n é
declarada como parâmetro da função e irá receber qualquer valor que tenha sido colocado
na chamada da função fatorial. Assim se chamarmos a função fatorial como:
...
f1 = fatorial( 12 );
...
então a o parâmetro n irá receber o valor 12 e será calculado o fatorial de 12, que será
retornado e posteriormente armazenado na variável f1.

7.7. Recursividade
Uma função recursiva é uma função se chama a si mesma direta ou indiretamente no
transcurso de sua execução. A recursividade direta ocorre quando a função se chama a si
mesma dentro do próprio corpo da função. A recursividade indireta ocorre quando uma
função chamada por ela, chama a própria função que a chamou (podem haver vários níveis
de chamada até que uma chama de novo a função original).

Para que um processo recursivo funcione deve haver, na função recursiva, uma condição
de parada que irá cortar o ciclo de chamadas recursivas, ou seja, irá retornar um valor
sem chamar de novo a mesma função.

Por exemplo, o fatorial de um número n normalmente é especificado como:

n! = 1*2*3*...*(n-1)*n

que é equivalente a formulação

n! = n*(n-1)* ... *3*2*1

Este tipo de função pode ser definido, em termos formais, da seguinte forma:

61 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 7 - Funções e Módulos

0! = 1
n! = n*(n-1)!

que diz que o fatorial de 0 é 1 e que o fatorial de um número n pode ser calculado
multiplicando-se n pelo fatorial de (n-1).

Esta função também pode definida de uma forma um pouco mais detalhada se adotarmas a
seguinte notação para a definição de funções recursivas:

fat(n) = 1, se n=0
fat(n) = n*fat(n-1), se n>0

Esta notação está muito próxima do código C que poderia ser usado para definir
recursivamente a função fatorial. Tal código seria similar ao seguinte:
int fat( int n )
{
if (n==0)
return 1;
else
return n*fat(n-1);
}

Compare este código com a versão da função fatorial que foi definida anteriormente de
forma não-recursiva (também se diz de forma iterativa).

Exercícios:

(7.b) Faça uma função recursiva que calcula a soma da série 1/1 + 1/2 + 1/3 + ... + 1/N
onde N é um valor passado como parâmetro.

(7.c) Faça uma função que calcula a soma dos termos da série:
1 2 3 4 n
+ 2 + 3 + 4 + ... + n
2 2 2 2 2
onde n é dado como parâmetro.

62 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

Capítulo 8
Arrays, Strings e Matrizes

Em todas as linguagens de programação é necessário ter algum tipo de estrutura de dados


que nos permite lidar com listas de objetos e também com tabelas ou matrizes de objetos.
Em C estas estruturas se chamam de arrays (em português seriam “arranjos”, mas o uso da
palavra array é tão disseminado que preferimos manter o original em inglês).

O array mais simples, também denominado de array unidimensional ou também de vetor,


é uma lista de objetos de um mesmo tipo de dados. Os objetos se chamam elementos do
array e são guardados em posições numeradas (indexadas) consecutivamente dentro do
array, desde a posição de índice 0 até a posição cujo índice é igual ao tamanho do array
menos uma posição.

Um vetor com n posições:

...
0 1 2 3 n-2 n-1
Figura 2 - Um Vetor com n Posições
Arrays também podem mais de uma dimensão. Quando o array tem duas dimensões ele é
usualmente denominado de matriz. Cada dimensão da matriz também tem um índice. Um
índice irá variar de 0 até o número de colunas menos 1 da matriz e a outra dimensão irá
variar de 0 até o número de linhas da matriz.

Uma matriz de m linhas por n colunas:

0
...
... 1

. . . . . .
. . . . . .
. . . . . .

... m-1
0 1 2 3 n-2 n-1

Figura 3 - Uma Matriz com m x n Posições


Os arrays podem ter até mais dimensões: 3, 4, 5, ..., na verdade, tantas quantas nós
queiramos. As limitações neste caso são de dois tipos:

63 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

• Possível falta de memória do computador para guardar estruturas tão complexas


(lembre-se um vetor com 10 inteiros tipicamente ocupa 10 palavras de memória, uma
matriz 10x10, ocupa 100 palavras, um “cubo” 10x10x10 ocupa 1000 palavras e assim
por diante)

• Dificuldade na compreensão do que significam objetos com mais de três dimensões,


nós somos seres adaptados a viver e pensar num mundo tridimensional, e múito difícil
de pensar em objetos com mais de três dimensões.

8.1. Declaração de Vetores


Um vetor é declarado apenas se dizendo qual o tipo de dados de cada elemento do vetor e
de quantos elementos este vetor irá conter. O formato básico de declaração de uma varíavel
array é o seguinte:
<tipo-da-elemento> <nome-do-vetor> [ <tamanho-do-vetor> ]

onde <tipo-do-elemento> é qualquer tipo de dados de C, <nome-do-vetor> é o nome da


variável que contém o vetor e <tamanho-do-vetor> é uma constante inteira positiva que
diz quantas posições tem o vetor.

Um vetor de caracteres é denominado usualmente de string.

Exemplos:
// Um vetor de inteiros de 20 posicoes
int vetor_de_inteiros[ 20 ];
// Um vetor de caracteres (um string) com 80 caracteres
int string_de_texto[ 80 ];
// Vetores de numeros ponto flutuante com 3 posicoes para
// guardar informacoes sobre pontos num espaco tridimensional
float ponto1[3];
float ponto2[3];

8.2. Uso dos Vetores


Normalmente não se trabalha diretamente com vetorores. Inclusive C não copia os valores
de um vetor para outro mesmo que eles tenham o mesmo tipo e imensões. Somente é
possível trabalhar diretamente com os elementos dos vetores.

Para se ter acesso a um elemento de um vetor basta colocar logo ao lado do nome da
variável vetor, entre abre e fecha-colchetes (‘[ ]’) o índice da posição desejada. Dentro do
programa, ou seja, após as declarações é possível usar variáveis como índice dos vetores
(na verdade qualquer tipo de expressão numérica inteira positiva).

Alguns exemplos de uso:

64 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

// Supondo os vetores definidos anteriormente e mais algumas


// variaveis auxiliares
int i;

// Prenche todo o vetor de inteiros com o valor 1000


for (i=0; i<20;i++)
vetor_de_inteiros[i] = 1000;
// Agora multiplica por 2 as posicoes deste vetor
for (i=0; i<20;i++)
vetor_de_inteiros[i] = vetor_de_inteiros[i] * 1000;

// Coloca espacos em branco em todos as posicoes do


// string, exceto na ultima posicao que e’ preenchida
// com o caracter nul (caracter de codigo 0)
for (i=0; i<79;i++)
string_de_texto[i] = ‘ ’;
string_de_texto[79] = ‘\0’;

// Prenche as coordenadas do ponto 1


ponto1[0] = 1.0;
ponto1[1] = -2.3;
ponto1[2] = 1.7;
// Agora copia o ponto 1 para o ponto 2
ponto2[0] = ponto1[0];
ponto2[1] = ponto1[1];
ponto2[2] = ponto1[2];

8.3. Cadeias de Caracteres (strings)


Não existe o tipo cadeia de caracter, texto ou string em C, na verdade um string é um
vetor de caracteres. Esse vetor deve terminar com o caractere ‘\0’ (também denominado de
caracter nul). Por essa razão um string deve conter uma posição a mais do que o número
de caracteres que se deseja. Portanto se desejamos guardar um string de n caracteres
devemos definir um vetor com tamanho n + 1. Constantes strings são uma lista de
caracteres que aparecem entre aspas, não sendo necessário colocar o '\0', que é colocado
pelo compilador.

Importante:

Uma constante string é tratada pelo compilador como um apontador para o início do string.
Assim, uma declaração como:
char str[10] = “um texto”;
está errada e não irá inicializar a variável str (que é um vetor com 10 caracteres). Para
inicializar esta variável apropriadamente pode-se usar a seguinte declaração:
char str[10] = {‘u’, ‘m’, ‘ ’, ‘t’, ‘e’, ‘x’, ‘t’, ‘o’, ‘\0’};
ou então usar a função strcpy no início do programa:
strcpy(str, “um texto”);

Saída de dados com strings:

65 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

Para saída de dados com strings pode-se usar o formato “%s” da função printf e passar a
variável string diretamente como argumento correspondente. Também se pode usar a
função puts para este fim:
puts(<var_string>);

Esta função escreve o seu argumento no dispositivo padrão de saída (vídeo), colocando um
'\n' (um salto de linha) no final. Ela reconhece os códigos de barra invertida usados na
printf.

Entrada de dados em strings:

A função scanf só lê strings de uma palavra com o formato “%s”. No primeiro espaço em
branco a função encerra. Para a leitura de uma string com tamanho indeterminado deve-se
usar a função gets:
gets(<var_string>);

Esta função é utilizada para leitura de uma string através do dispositivo padrão, até que a
tecla ENTER seja pressionada. A função gets() não testa limites do vetor em que é
chamada.

Exemplo:
main()
{
char str1[80];

printf("Entre com um string:\n”);


gets(str1);
printf("O string lido foi:\n”);
puts(str1);

printf("Entre com uma palavra:\n”);


scanf("%s",str1);
printf("A palavra lida foi: %s",str1);
}

Manipulação de strings

A manipulação de strings em C é feita através de rotinas especializadas para este


tratamento. Existem rotinas para copiar, concatenar e comparar strings, além de contar o
número de caracteres (o tamanho) de um string. A seguir serão apresentadas as principais
rotinas de manipulação de strings. Entretanto, é importante ressaltar que esta não é uma
lista exaustiva das rotinas de manipulação de strings. Além destas funções, há outras que
podem ser consultadas no manual de referência da linguagem que apresenta a biblioteca de
rotinas padrão de C.

A função strcpy:
strcpy(<string1>,<string2>);

66 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

Serve para copiar o conteúdo de um string noutro. Ela recebe o endereço de dois strings e
copia o conteúdo do segundo string (<string2>) para dentro do primeiro string I
(<string1>). O conteúdo original do primeiro string é apagado e o conteúdo do segundo
string é copiado por cima. O segundo string não sofre alteração. O primeiro string deve
possuir espaço livre suficiente para a cópia.

A função strcat:
strcat(<string1>,<string2>);

Serve para concatenar o conteúdo de um string no fim de outro string. Ela recebe o
endereço de dois strings e copia o conteúdo do segundo string (<string2>) no fim do
primeiro string I (<string1>). O segundo string não sofre alteração. O primeiro string deve
possuir espaço livre suficiente para a concatenação.

A função strlen:
strlen(<string>);

Serve para contar quantos caracteres existem num string. Ela recebe o endereço de um
string e retorna o tamanho deste. Esta função não conta o caracter ‘\0’ do fim do string.

A função strcmp:
strcmp(<string1>,<string2>);

Serve para comparar dois strings. Ela recebe o endereço de dois strings e compara o
conteúdo de um string contra o outro. Esta função irá retornar um valor numérico negativo,
zero ou positivo dependendo do resultado da comparação, de acordo com as seguintes
regras:
• Retorna um número negativo se o <string1> for menor que o <string2>
• Retorna zero se o <string1> for igual ao <string2>
• Retorna um número positivo se o <string1> for maior que o <string2>

Observações:
• Menor que (ou maior que) significa que se colocarmos os strings em ordem alfabética o
<string1> aparecerá primeiro (ou depois).
• Letras maiúsculas são consideradas diferentes de minúsculas.
• O valor de retorno corresponde a diferença em ASCII entre os primeiros dois caracteres
diferentes.

Exemplos:
main()
{
char str1[100], str2[100], str3[100];

// inicializa um string com uma constante


strcpy( str1, “uma constante string” );

67 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

// copia o conteudo de um string noutro


strcpy( str2, str1);

// concatena um texto ao fim de str1


strcat( str2, “ que foi concatenada com outra”);

// inicializa o string str3


strcpy( str3, “uma constante string” );

// compara os tamanhos de strings


if (strlen( str1 ) == strlen( str3 ))
printf(“str1 tem o mesmo tamanho de str3\n”);
else
printf(“str1 nao tem o mesmo tamanho que str3\n”);

// compara os strings str1 e str2


if (strcmp( str1, str2) == 0)
printf(“str1 e str2 sao iguais\n”);
else if (strcmp( str1, str2) < 0)
printf(“str1 eh menor que str2\n”)
else
printf(“str1 eh maior que str2\n”)
}

8.4. Declaração de Matrizes


Uma matriz é declarada de uma forma muito parecida que um vetor, bastando definir qual o
tipo de dados de cada elemento do vetor e de quantos elementos a matriz irá conter em cada
dimensão. O formato básico de declaração de uma varíavel matriz é o seguinte:
<tipo-da-elemento> <nome-da-matriz> [ <num-linhas> ] [ <num-colunas> ]

onde <tipo-do-elemento> é qualquer tipo de dados de C, <nome-da-matriz> é o nome da


variável que irá conter a matriz, <num-linhas> diz quantas linhas tem esta matriz e <num-
colunas> diz quantas colunas ela tem. Da mesma forma que <tamanho-do-vetor> nas
declarações de vetore tanto <num-linhas> quanto <num-colunas> devem ser constantes
inteiras positivas.

Exemplos:
// Uma matriz de inteiros de 15 linhas por 25 colunas
int mat_int[ 15 ][ 25 ];
// Uma matriz de 100x80 caracteres
int tab_str[ 100 ][ 80 ];
// Matriz de numeros ponto flutuante de 100x50
float mat_float[ 100 ] [ 50 ];

68 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

8.5. Uso das Matrizes


Da mesma forma que com os vetores, normalmente não se trabalha diretamente com as
matrizes, mas com seus elementos. Para se ter acesso a um elemento de uma matriz basta
colocar logo ao lado do nome da variável matriz, entre abre e fecha-colchetes (‘[ ]’) os
índices linha e coluna da posição desejada. Dentro do programa, ou seja, após as
declarações é possível usar variáveis como índice das matrizes (na verdade qualquer tipo de
expressão numérica inteira positiva).

Alguns exemplos de uso:


// Supondo os vetores definidos anteriormente e mais algumas
// variaveis auxiliares
int i,j;

// Prenche toda matriz de inteiros o valor 5


for (i=0; i<15;i++)
for (j=0; j<25; j++)
mat_int[i][j] = 5;

// Agora multiplica por 15 as posicoes desta matriz


for (i=0; i<15;i++)
for (j=0; j<25; j++)
mat_int[i][j] = mat_int[i][j] * 15;

Exercícios:

(8.a) Faça um programa que lê e armazena frases (linhas de texto) digitadas pelo usuário.
As frases devem ser armazenadas numa matriz de strings (uma matriz bidimensional de
caracteres). Quando o usuário digitar uma frase contendo apenas o caracter ‘$’, o programa
para e imprime na tela todas as frases lidas.

(8.b) Faça um programa que leia um string e imprima este string ao contrário.

(8.c) Faça um programa que leia uma matriz bidimensional e verifique se ela é uma matriz
diagonal. Uma matriz diagonal é uma matriz onde os elementos que não estão na diagonal
principal da matriz são todos iguais a zero (0). A diagonal principal da matriz é formada
pelos elementos A[i][j], onde i=j.

(8.d) Faça um programa que leia duas matrizes bidimensionais, some as duas matrizes e
apresente o resultado na tela. Duas matrizes somente podem ser somadas se tem o mesmo
número de linhas e colunas. Supondo duas matrizes A e B, com m linhas por n colunas, a
matriz C será a resultante da soma se todos os elementos de C atenderem a seguinte
condição:

C[i][j] = A[i][j] + B[i][j]

para 0 ≤ i <n e 0 ≤ j < n.

69 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 8 - Arrays, Strings e Matrizes

(8.e) Faça um programa que multiplique duas matrizes fornecidas pelo usuário e apresente
o resultado na tela. O produto da matriz A de m linhas por p colunas pela matriz B de p
linhas por n colunas resulta na matriz C de m linhas por n colunas, se cada elemento C[i][j]
de C é obtido multiplicando-se os elementos da linha i de A pelos correspondentes
elementos da coluna j de B e, posteriormente, somando-se os produtos obtidos.

70 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 9 - Apontadores

Capítulo 9
Apontadores (ou Ponteiros)

Uma variável nada mais é do que o nome que damos a uma posição na memória de um
computador que deverá armazenar um determinado valor. O tipo de dados da variável, quer
seja caracter (char), número inteiro (int), ponto flutuante (float), ou outro, define o formato
e o tamanho que o dado irá ocupar na memória.

Até agora trabalhamos com variáveis estáticas que designam uma posição fixa na memória.
O que “varia” nestas variáveis é o seu conteúdo, não a posição ou lugar que ela ocupa na
memória. Uma analogia que ajuda a entender este conceito, é imaginarmos que o nome de
uma variável é o nome de uma espécie de “caixa” |(que está em algum lugar) que serve para
guardar dados.

Estas variáveis fixas na memória não são, entretanto, tudo o que nós podemos fazer em
termos de manipulação de dados. Existe, na verdade, uma técnica muito poderosa de
manipulação que nos permitirá trabalhar diretamente com as posições ou lugares onde se
armazenam dados na memória.

Para tanto é necessário trabalhar com os apontadores para variáveis (ou para posições de
memória, o que é a mesma coisa). Estes apontadores (também denominados de ponteiros)
são variáveis como as outras, isto é, tem um nome e guardam um valor dentro deles. Porém
seus valores tem um significado completamente diferente dos valores comumente
guardados em variáveis inteiras, caracter, etc.

Uma variável apontadora guarda dentro de si um apontador de uma outra variável que está
na memória:

ap1: var1:
Figura 4 - Apontador para Variável
No exemplo acima a variável apontadora ap1 aponta para uma variável “comum” var1.

Além de “apontador”, também se denomina o valor guardado dentro de ap1 de ponteiro


para a variável var1, ou de referência à variável var1 ou ainda de endereço da variável
var1. Em C todas estas denominações são equivalentes e podem ser intercambiadas sem
maiores problemas, porque em C, um apontador, ponteiro ou referência a uma dada
variável nada mais é do que o endereço (ou posição) desta variável na memória principal do
computador.

71 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 9 - Apontadores

9.1. Declaração de Variáveis Apontadoras


Em C os apontadores, não apontam apenas para uma variável, mas pode-se mudar o lugar
(ou posição de memória) onde eles irá apontar. Assim não faz sentido em falar no
apontador apenas para uma variável. O que se tem que definir é um apontador para um tipo
de dados específico, que pode, então, ser usado para apontar para variáveis dàquele mesmo
tipo de dados. Assim se definirmos um apontador para inteiro poderemos usá-lo para
apontar para variáveis do tipo inteiro e assim por diante.

Um apontador, ou seja, uma variável apontadora, é definido através de declarações com o


seguinte formato:
<tipo-de-dados-apontado> *<nome-da-variavel-apontadora>;

onde <tipo-de-dados-apontado> é qualquer tipo de dados de C, e <nome-da-variável-


apontadora> é o nome da variável que irá conter o apontador. O operador ‘*’ quando
aplicado ao nome da variável é que diz ao compilador que ela será um apontador para o
tipo de dados <tipo-de-dados-apontado> e não apenas uma varável comum deste tipo.

Exemplos:
// Apontadores para caracteres (strings)
char *ps1;
char *ps2;
char *ps3;
// Apontadores para inteiros
int *pvar1;
int *pvar2;
// Apontadores para numeros ponto-flutuante
float *px;
float *py;

9.2. Inicialização de Apontadores


Normalmente, um apontador é inicializado obtendo-se o endereço (referência ou posição de
memória) da variável comum que se quer que ele aponte. Isto é feito através do operador
‘&’ que, quando aplicado sobre uma variável retorna um apontador para esta variável.
Tecnicamente este operador apenas retorna o endereço (ou posição) na memória principal
onde esta variável está localizada.

Exemplos:

Assim para inicializar os vetores que vimos anteriormente poderiamos ter o seguinte
código:
// Supondo os apontadores anteriores, definimos algumas
// variáveis comuns:
char c1, c2;

72 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 9 - Apontadores

char str1[100], str2[200];


int i,j,k;
int vet1[10], vet2[30];
float x,y,z;

// Codigo que inicializa os vetores que definimos anteriormente:


ps1 = &c1;
ps2 = &str1[0];
ps3 = str2;
pvar1 = &i;
pvar2 = vet1;
px = &x;
py = &y;

Alguns comentários sobre o código acima:

Para inicializar um apontador para uma variável basta usar o operador ‘&’ sobre o nome da
variável, como nas linhas:
ps1 = &c1;
...
pvar1 = &i;
...
px = &x;
py = &y;

No caso de apontadores para vetores,entretanto, é necessário dizer qual posição do vetor


iremos apontar. No exemplo o apontador ps2 irá apontar para posição 0 do vetor (string)
str1.
...
ps2 = &str1[0];
...

Porém, no caso de apontadores para a posição 0 de um vetor o compilador C aceita a


seguinte simplificação:
...
ps3 = str2;
...
pvar2 = vet1;
,,,
onde não é necessário dizer o índice do array nem colocar o operador ‘&’ na frente dele.

Mais imporante do que isto, é necessário entender que em C apontadores para vetores e
variáveis array são formalmente equivalentes, exceto pelo fato que ao declararmos uma
variável array será reservado na memória uma área para guardar o array, enquanto que a
declaração de um apontador não reserva memória. Apenas abre espaço para guardar o
endereço do início de um array, por exemplo, e não o array todo.

73 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 9 - Apontadores

9.3. Uso dos Apontadores


Um apontador é usado justamente quando fazemos referência a variável ou posição de
memória que ele aponta, tanto para ler os dados que estão lá quanto para guardar novos
valores.

Para isto é usado o operador ‘*’ que ao ser aplicado a uma variável apontadora retorna o
próprio objeto sendo apontador. Assim se quizermos ler o valor apontado por um apontador
basta colocar o ‘*’ na frente do nome do apontador e usar esta construção como se fosse
uma variável comum do tipo apontado pelo apontador.

Da mesma forma se queremos atribuir um novo valor basta usar o nome da variável
apontadora precedido do ‘*’ à esquerda do operador de atribuição de C.

Exemplos:
// Supondo os apontadores e variaveis definidos anteriormente
c1 = ‘a’;
c2 = *ps1;
j = 1;
*pvet1 = j;
k = *pvet1 * *pvet1 + 12
*px = 2.5;
*py = *px + 5.7;

9.4. Manipulação de Apontadores


Além de ser usado como referência ou para atribuir um valor, também podemos manipular
apontadores como se fossem variáveis comuns, isto é, podemos atribuir o valor de um
apontador para outro e fazer algumas operações sobre diretamente sobre os operadores.

A operação mais simples é a cópia ou atribuição do valor de um operador para outro. A


única restrição nesta operação é que ambos operadores tenham o mesmo tipo. Isto é não
podemos atribuir um apontador de caracteres para um de inteiros e vice-versa. E nem as
outras combinações.
pvet2 = pvet1;
py = px;
pstr3 = pstr1;

Outra operação que pode ser feita com os apontadores é o incremento ou decremento deles.
A lógica aqui é bastante simples: se um apontador aponta para um objeto de um dado tipo
que está localizado na memória, ao incrementar este apontador teremos um novo
apontador para a próxima posição na memória principal que pod guardarum objeto de
mesmo tipo.
// Preenche o string str2 com “abc”, usando o

74 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 9 - Apontadores

// apontador ps2
ps2 = str2;
*ps2 = ‘a’;
ps2++;
*ps2 = ‘b’;
ps2++;
*ps2 = ‘c’;
ps2++;
*ps2 = ‘\0’;

De forma similar ao decrementar um apontador, teremos um apontador para a posição


prévia ou anterior na memória para um objeto de mesmo tipo.
// Agora trocamos os caracteres do string str2 para “XYZ”, usando o
// apontador ps2
ps2--;
*ps2 = ‘Z’;
ps2--;
*ps2 = ‘Y’;
ps2--;
*ps2 = ‘X’;

Por fim, também podemos adicionar um indice a um apontador da mesma forma que a uma
variável vetor. Como já foi comentado anteriormente apontadores e vetores são muito
similares, exceto que os apontadores não reservam área na memória. Assim ao
adicionarmos um índice a um vetor apenas consideramos que o apontador aponta para o
início de um vetor de objetos de um dado tipo.

A posição 0, é a primeira posição deste vetor, a posição 1 a próxima (que também poderia
ser obtida incrementando o apontador) e assim sucessivamente.
// Agora trocamos os caracteres do string str2 para “def”, usando
// indices sobre ps2
ps2 = str2;
ps2[0] = ‘d’;
ps2[1] = ‘e’;
ps2[2] = ‘f’;
ps2[3] = ‘\0’;
// Agora zeramos todo o string str1, usando o apontador ps1
ps1 = pstr1;
for (i=0; i<100; i++)
ps1[i] = ‘\0’;

9.5. Apontadores como Parâmetros de Funções


Quais as razões para usar apontadores? Existem duas razões principais para se usar
apontadores em C:

• Uma primeira razão esta relacionada a sua utilização como argumentos de funções.

75 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 9 - Apontadores

• Outra razão é que os apontadores nos permitem reservar memória (também se diz
alocar memória) dinâmicamente, isto é, não apenas quando escrevemos o programa
através das declarações de variáveis, mas depois, quando o programa entra realmente
em execução.

O uso de alocação dinâmica será estudado mais adiante, porém agora iremos analisar o
primeiro caso, da utilização de apontadores como parâmetros de função.

A primeira coisa que tem que ser compreendida é que existem duas formas ou maneiras de
se passar parâmetros para uma função:
• passagem de parâmetros por valor e
• passagem por referência.

A passagem por valor significa que passamos de uma função para outra o valor de uma
variável, isto é, a função chamada recebe uma cópia do valor da variável. Assim qualquer
alteração deste valor, pela função chamada, será uma alteração de uma cópia do valor da
variável. O valor original na função chamadora não é alterado pois o valor original
e copia ficam em blocos de memória diferentes.

Já na passagem por referência significa que passamos de uma função para outra o
endereço de uma variável, isto é, a função chamada recebe sua localização na
memória através de um ponteiro. Assim qualquer alteração no conteúdo apontado pelo do
ponteiro será uma alteração no conteúdo da variável original. O valor original é
alterado.

No capítulo 7, quando apresentamos a declaração e uso (chamada) de funções em C foi


comentado que os parâmetros de uma função se comportam como variáveis locais da
função que recebem os valores reais dos parâmetros quando a função é chamada e que
“desaparecem” logo após a função retornar. Resumindo o tipo de passagem de parâmetros
padrão de C é por valor.

Apesar disso, através da utilização de apontadores também é possível se fazer a passagem


por referência em programas C, visto que o que se requer é, justamente, passar como
parâmetro o endereço ou o apontador para uma variável.

Então, para passar um parâmetro por referência basta declará-lo, na lista de parâmetros da
função como um apontador. Além disso, quando a função é chamada é necessário usar o
operador ‘&’ para se obter o apontador da varíavel que se quer usar como parâmetro
passado por referência.

O exemplo a seguir, que define uma função que consegue trocar o valor de duas variáveis
externas à função deve mostrar como estes tipos de parâmetros podem ser definidos e
usados.

Exemplo:
// Declaracao de uma funcao que troca o valor de dois
// numeros ponto flutuante

76 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 9 - Apontadores

void troca_float(float *px, float *py)


{
float temp;

temp = *px;
*px = *py;
*py = temp;
}

// Exemplo de uso da funcao troca_float


void main()
{
float x;
float y;

x = 10.0;
y = 20.0;
printf(“x=%f y=%f\n”, x, y);
troca_float(&x, &y); // chamada de troca_float
// note o uso do operador ‘&’
printf(“x=%f y=%f\n”, x, y);
}

Exercícios:

(9.a) Escreva um programa que peça ao usuário um número que deve ser digitado do
teclado. Guarde este número em uma variável. Depois faça um ponteiro apontar para a
variável que guardou este número e imprima-o na tela, acessando este pela variável
ponteiro.

(9.b) Crie uma variável n do tipo inteiro e armazene nela um valor inicial. Crie agora uma
variável apontadora para inteiros chamada ptr para apontar para n. Agora, note a diferença
do ponteiro para o tipo imprimindo:
printf("%d\n",n);
printf("%p\n",ptr);

Onde "%p" pede para imprimir o endereço de memória armazenado em ptr.

(9.c) Implemente a função string_concat(char *s1, char *s2, int max) que copia o string
apontado por s2 para o string apontado por s1 até um máximo de caracteres definido pelo
parâmetro inteiro max. Se s2 tem menos caracteres que max, então a função copia todos os
caracteres de s2 ao fim de s1.

(9.d) Implemente a função string_reverse(char *str) que reverte o string apontado por str.

77 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 10 - Estruturas

Capítulo 10
Estruturas (structs)

Formalmente uma estrutura guarda, sobre o mesmo nome, uma coleção de valores de tipos
de dados distintos. Um array é uma repetição de um mesmo tipo de dados, ou seja, quando
definimos um array criamos uma lista de objetos do mesmo tipo. Além disso estes objetos
da lista não tem um “nome” eles tem apenas um índica que serve para localizá-los.

Porém ao definirmos uma estrutura em C, nós criamos uma coleção de objetos que tem
nomes e tipos de dados distinto e que podem ser localizados através destes nomes.

Ná prática uma estrutura poderia ser entendida como um conjunto de variáveis que tem um
nome único para serem referenciadas. Estas “variáveis” dentro das estruturas são
normalmente denominadas de campos da estrutura.

10.1. Declaração de Estruturas


Podemos criar definições de estruturas, nas quais podemos utilizá-las como uma espécie de
"molde" (ou, mais propriametne tipo) para futura utilização. A pressuposição básica no uso
de estruturas é que existe uma ligação lógica entre os elementos desta estrutura. Podemos
exemplificar isso com uma estrutura que contenha como campos: o nome, telefone, codigo,
sexo e estado civil de um funcionário de uma empresa:
struct funcionario
{
char nome[100];
char telefone[20];
int codigo;
int sexo;
int estado_civil;
};

Bem, declaramos o tipo de dados struct funcionario como um molde para utilização no
futuro. Repare que a linha foi terminada com ponto e vírgula, como em um comando
comum. Definido o molde, devemos agora declarar a variável que utilizará desse molde.
struct funcionario fun1, fun2;

Agora temos as variáveies fun1 e fun2 declaradas de acordo com o molde especificado por
struct funcionario. Uma outra opção é a declaração direta, por exemplo, já na definição do
molde, declaramos as variáveis de forma embutida. Assim:
struct funcionario
{

78 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 10 - Estruturas

char nome[100];
char telefone[20];
int codigo;
int sexo;
int estado_civil;
} fun1, fun2;

Independente de como foram declaradas teremos, na memoria, estruturas similares as da


seguinte figura:

fun1: nome: char [100]

telefone: char [20]

codigo: int

sexo: int

estado_civil: int

fun2: nome: char [100]

telefone: char [20]

codigo: int

sexo: int

estado_civil: int

Figura 5 - Formato de Variáveis de Tipo struct na Memória


Além disso também é possível declarar uma variável diretamente com a especificação do
formato da estrutura mas sem dar nome a estrutura. Seria o caso de fazer uma declaração
igual ao último exemplo, mas sem o nome funcionario aparecendo após a palavra struct:
struct
{
char nome[100];
char telefone[20];
int codigo;
int sexo;
int estado_civil;
} fun1, fun2;

Assim, temos as variáveis fun1 e fun2, exatamente igual ao exemplo anterior. Isso é útil
para quando não precisamos declarar mais variáveis com o mesmo formato ou molde.

79 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 10 - Estruturas

Na memória, as variáveis dentro da struct são seqüenciais, o nome da struct só é o


endereço da primeira posição de memória de tal struct, os demais elementos são dados
através de deslocamentos apartir deste endereço inicial da estrutura.

10.2. Utilização das Estruturas


Para termos acesso e podermos ler ou alterar os valores armazenados nos campos de uma
estruturas devemos utilizar o operador ‘.’, chamado de operador ponto . Dessa forma, no
exemplo acima, caso queiramos escrever um valor no campo codigo, basta usar:
fun1.codigo = 10;

O formato é sempre este:


<nome-da-variável>.<nome-do-campo>

que pode ser considerada como uma variável comum cujo tipo de dados é o mesmo que foi
definido para <nome-do-campo> na estrutura.

Exemplos:
struct funcionario
{
char nome[100];
char telefone[20];
int codigo;
int sexo;
int estado_civil;
};
struct funcionario fun1, fun2;

// Os seguintes trechos de codigo inicializam


// as variaveis fun1 e fun2

strcpy(fun1.nome, “Jose da Silva”).


strcpy(fun1.telefone,”7777777”);
fun1.codigo = 101;
fun1.sexo = 1; // masculino
fun1.estado_civil = 2; // casado

strcpy(fun2.nome, “Maria dos Santos”).


strcpy(fun2.telefone,””);
fun2.codigo = 102;
fun2.sexo = 2; // feminino
fun2.estado_civil = 1; // solteira

// Agora os valores de fun2 sao impressos na tela


printf(“Dados do funcionario\n”);
printf(“Nome: %s\n”, fun2.nome ).
printf(“Telefone: %s\n”, fun2.telefone);
printf(“Codigo: %d\n”, fun2.codigo);
if (fun2.sexo==1)
printf(“Sexo: masculino\n”);

80 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 10 - Estruturas

else
printf(“Sexo: feminino\n”);
printf(“Estado civil: ”);
switch(fun2.estado_civil)
{
case 1:
printf(“solteiro\n”);
break;
case 2:
printf(“casado\n”);
break;
case 3:
printf(“separado\n”);
break;
}

10.3. Apontadores para Estruturas


Da mesma forma como as demais variáveis também é possível definir apontadores para
estruturas. Um apontador para uma estrutura é definido como:
struct <estrutura> *<apontador-para-estrutura>;

de forma que a variável de nome <apontador-para-estrutura> será agora um apontador para


estrutura. O operador ‘*’ também deve ser usado para referenciar a estrutura apontada pelo
apontador. Nos exemplos acima ficaria:
struct funcionario fun1, fun2;
struct funcionario *pfun;

// Algumas inicializacoes
fun1.codigo = 102;
fun1.sexo = 2; // feminino
fun1.estado_civil = 1; // solteira

// Agora usando os apontadores


// Primeiro faz pfun apontar para fun2
pfun = &fun2;

// Agora usa este apontador para preencher


// alguns valores de fun1
(*pfun).codigo = 101;
(*pfun).sexo = 1;
(*pfun).estado_civil = 2;

// Uma forma mais simples de usar o apontador


// e’ atraves do operador ‘->’ que todos os
// compiladores C aceitam como equivalentes
// aos comandos acima:
pfun->codigo = 101;
pfun->sexo = 1;
pfun->estado_civil = 2;

81 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 10 - Estruturas

Note alguns detalhes importantes:

• É necessário colocar o operador ‘*’ entre parênteses para garantir a precedência correta.
A precedência padrão faria a expressão:
*pfun.codigo = 101;
que está sem parênteses, significar:
*(pfun.codigo) = 101;
o que não estaria correto no contexto.

• Um outro recurso importante é a utilização do operador ‘->’ que deixa o código muito
mais simples e legível.

Exercícios:

(10.a) Faça um programa para gerenciar um banco de referências bibliográficas. Uma


referência bibliográfica deve conter, pelo menos, o nome do livro, o nome do autor, o nome
da editora, o local e ano de publicação e o assunto tratado pelo livro. O programa deve
armazenar estas informações numa estrutura (struct) apropriada e guardar as referências
num array destas estruturas. O programa deve permitir, pelo menos, as seguintes
operações:
• Armazenar uma nova referência bibliográfica;
• Eliminar (“deletar”) uma referência bibliográfica;
• Imprimir uma referência bibliográfica;
• Imprimir as referências bibliográficas armazenadas;
As referências bibliográficas são localizadas pelo índice (pela posição) delas no array.

(10.b) Modifique o programa do exercício (10.a) de forma que ele possa procurar por uma
referência, pelo nome do livro ou pelo nome do autor, e após localizá-la imprimir esta
referência na tela. Esta procura deve ser implementada através de apontadores para
estruturas e não pelo uso de índices no array de estruturas.

82 Copyright  2002,03 João Carlos Gluz


UERGS - Algoritmos e Programação Capítulo 10 - Estruturas

Bibliografia

KERNIGHAN, Brian W.; RITCHIE, Dennis M. C: a Linguagem de Programação


Padrão Ansi. Rio de Janeiro: Campus, 1999.

ZIVIANI, Nivio. Projeto de Algoritmos: com Implementações em Pascal e C. São Paulo:


Pioneira, 2002.

FARRER, H. et al. Programação Estruturada de Computadores – Algoritmos


Estruturados. Rio de Janeiro: Livros Técnicos e Científicos, 1989.

SALVETTI, Dirceu D.; BARBOSA, Lisbete M. Algoritmos. São Paulo: Makron, 1998.

SCHILDT, Herbert. C Completo e Total. São Paulo: Makron, 1997.

TENENBAUM, Aarron M.; LANGSAM, Y.; AUGENSTEIN, M. Estruturas de Dados


Usando C. São Paulo: Makron Books, 1995.

MANZANO, José Augusto N. G.; OLIVEIRA, Jayr F. Estudo Dirigido de Algoritmos.


São Paulo: Érica, 1997. (Coleção PD).

CORMEN, Thomas H.; LEISERSON, Charles E.; RIVEST, Ronald L. Algoritmos: Teoria
e Prática. Rio de Janeiro: Campus, 2002.

83 Copyright  2002,03 João Carlos Gluz

You might also like