Escolar Documentos
Profissional Documentos
Cultura Documentos
Unidade de Guaíba
Curso de Engenharia em Sistemas Digitais
Apostila da Disciplina de
Algoritmos e Programação
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
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
Capítulo 1
Introduçã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.
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.
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 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.
É 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.
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.
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...
década de 40, também foi completamente perdido com a derrota da Alemanha, somente
tendo sido recuperado 20 ou 30 anos depois.
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
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.
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
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”.
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.
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.
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.
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.
• 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.
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.
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).
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).
Sintaxe e Semântica
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.
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.
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
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
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.
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:
(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.
(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.
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.
Por fim, toda a linguagem de programação deve possuir comandos que permitem controlar
o fluxo de execução do programa.
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.
Capítulo 3
Elementos Básicos de C
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.
Exercício:
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.
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:
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.
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
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:
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
*/
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):
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.
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)
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
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:
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.
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>
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))
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
verdadeiro dentro de um if ou while qualquer valor diferente de zero. Somente o valor zero
(0) é considerado falso.
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>
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;
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>.
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.
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:
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.
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.
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;
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.
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>);
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).
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 &.
Os principais comandos de formatação usados nas rotinas scanf e printf são apresentados
na tabela a seguir:
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);
}
main()
{
int numero;
printf( “Digite um Numero: “);
scanf( “%d”,&numero);
printf( “O %d elevado ao quadrado resulta em %d. \n”,
numero, numero*numero);
}
Não esqueça, que como na sequência normal de comandos, cada comando deve ter um
ponto-e-vírgula (‘;’) ao seu final;
Pré-Processador
Compilador
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:
Logo a seguir, aparece um comando não visto até agora, o comando de pré-processador:
#include <stdio.h>
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”);
}
Capítulo 5
Comandos de Seleção
Exemplo:
int n;
int m;
if (n>m)
prinf(“n e’ maior que m\n”);
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>;
Exemplo:
int n;
int m;
int result;
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>;
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;
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
}
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.
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;
int main()
{
float raio;
return 0;
}
Em primeiro lugar, pode-se usar mais de uma biblioteca de funções num programa:
#include <stdio.h>
#include <math.h>
Além disso este programa, define algumas constantes para uso global:
#define CONSTANTE_PI 3.1415
#define AREA_ZERO 0.0
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;
...
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.
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).
Capítulo 6
Comandos de Repetição
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;
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).
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).
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 *
#include <stdio.h>
int main()
{
int n, s, i;
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;
if (n <= 0)
printf(“Somente trabalho com naturais!\n”);
else {
s = i = 0;
while (i<=n)
{
s += i;
i++;
}
printf(“A soma dos primeiros %d naturais e’: %d”,
n, s );
}
return 0;
}
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>;
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;
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;
}
i++
O comando a ser repetido, por sua vez, se torma apenas a soma dos naturais:
s += i;
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>);
Exemplo:
#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.c) Calcule a soma da série 1/1 + 1/2 + 1/3 + ... + 1/N onde N é um valor lido do teclado.
(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.
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.
{
<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.
fat = 1;
for (i=1; i<=n; i++)
fat = fat * i;
return fat;
}
// Includes
#include <stdio.h>
...
// Constantes
#define ...
...
// Tipos de dados
typedef ...
struct ...
...
// Variáveis globais
int ...
float ...
...
// Funções
int fun1( int par1 )
{
...
}
...
// Função Principal
int main()
{
return 0;
}
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>
fat = 1;
for (i=1; i<=n; i++)
fat = fat * i;
return fat;
}
int main()
{
int n, i;
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;
}
}
return 0;
}
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 ‘;’.
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.
O programa que imprime a tabela dos fatoriais poderia ser quebrado em 2 módulos:
• tabela-fatoriais
• fatorial
#include <stdio.h>
#include “fatorial.”
int main()
{
int n, i;
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;
}
return 0;
}
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>
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.
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.
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.
n! = 1*2*3*...*(n-1)*n
Este tipo de função pode ser definido, em termos formais, da seguinte forma:
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.
Capítulo 8
Arrays, Strings e Matrizes
...
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.
0
...
... 1
. . . . . .
. . . . . .
. . . . . .
... m-1
0 1 2 3 n-2 n-1
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];
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).
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”);
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.
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];
Manipulação de strings
A função strcpy:
strcpy(<string1>,<string2>);
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];
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 ];
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:
(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.
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.
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;
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;
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;
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.
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;
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
// apontador ps2
ps2 = str2;
*ps2 = ‘a’;
ps2++;
*ps2 = ‘b’;
ps2++;
*ps2 = ‘c’;
ps2++;
*ps2 = ‘\0’;
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’;
• Uma primeira razão esta relacionada a sua utilização como argumentos de funções.
• 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.
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
temp = *px;
*px = *py;
*py = temp;
}
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);
(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.
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.
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
{
char nome[100];
char telefone[20];
int codigo;
int sexo;
int estado_civil;
} fun1, fun2;
codigo: int
sexo: int
estado_civil: int
codigo: int
sexo: int
estado_civil: int
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.
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;
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;
}
// Algumas inicializacoes
fun1.codigo = 102;
fun1.sexo = 2; // feminino
fun1.estado_civil = 1; // solteira
• É 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.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.
Bibliografia
SALVETTI, Dirceu D.; BARBOSA, Lisbete M. Algoritmos. São Paulo: Makron, 1998.
CORMEN, Thomas H.; LEISERSON, Charles E.; RIVEST, Ronald L. Algoritmos: Teoria
e Prática. Rio de Janeiro: Campus, 2002.