Escolar Documentos
Profissional Documentos
Cultura Documentos
Mariateixeira-Pré-Texto - Livro Programação C.Flávio Varejão PDF
Mariateixeira-Pré-Texto - Livro Programação C.Flávio Varejão PDF
1 Introducao 6
1.1 Historico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Arquitetura de Computadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.1 Memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.2 Processador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 Algoritmos e Programas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4 Tecnica de Desenvolvimento de Programas . . . . . . . . . . . . . . . . . . . . . 11
1.5 Partes de um Programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.6 Traducao de Programas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6.1 Compilacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6.2 Interpretacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.7 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.8 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2 Conceitos Basicos 18
2.1 Variaveis e Celulas de Memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.2 Identificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3 Comando de Atribuicao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4 Tipos de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.4.1 Declaracao de Variaveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.4.2 Tipo Inteiro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.4.3 Tipo Ponto Flutuante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.4.4 Tipo Booleano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.4.5 Tipo Caractere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.4.6 Conversao de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.5 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.6 Expressoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.6.1 Expressoes Aritmeticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.6.2 Expressoes Relacionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.6.3 Expressoes Logicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.7 Comando de Entrada de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1
2 SUMARIO
3 Modularizacao 70
3.1 Introducao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
3.2 Subprogramas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.3 Partes de um Subprograma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.3.1 Cabecalho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.3.2 Dicionario de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.3.3 Corpo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3.3.4 Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
3.4 Chamada de subprogramas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
3.5 Passagem de parametros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
3.6 Retorno de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.6.1 Encerramento antecipado de execucao . . . . . . . . . . . . . . . . . . . . 84
3.7 Funcoes sem lista de parametros . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
3.8 Funcoes sem retorno de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.9 Recursividade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.9.1 Implementacao nao recursiva equivalente . . . . . . . . . . . . . . . . . . 90
3.10 Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.11 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
3.12 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
3.13 Trabalhos Sugeridos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
5 Vetores 135
5.1 Vetores e sua importancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.2 Representacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5.3 Definicao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5.3.1 Definicao do Tamanho do Vetor . . . . . . . . . . . . . . . . . . . . . . . . 139
5.4 Operacoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
5.4.1 Acesso indevido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.5 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.6 TAD Implementacional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
5.6.1 Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
5.6.2 Operacoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
5.7 Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
5.8 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
5.9 Lista de Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
5.10 Trabalhos Sugeridos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6 Matrizes 164
6.1 Introducao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
6.2 Definicao e Acesso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
6.2.1 Definicao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
6.2.2 Acesso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
6.3 O TAD implementacional tMatriz . . . . . . . . . . . . . . . . . . . . . . . . . . 168
6.3.1 Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
6.3.2 Operacoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
6.4 Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
6.5 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
6.6 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
6.7 Trabalhos Sugeridos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
6.8 Topicos Avancados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
4 SUMARIO
7 Apontadores 187
7.1 Variaveis Apontadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
7.2 A Sintaxe dos Apontadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
7.2.1 Operador Endereco de Memoria . . . . . . . . . . . . . . . . . . . . . . . 190
7.2.2 O Operador Seta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
7.3 Acesso a Variavel por Meio de Apontadores . . . . . . . . . . . . . . . . . . . . . 191
7.4 Uso de Apontadores nas Passagens de Parametros . . . . . . . . . . . . . . . . . 192
7.5 Alocacao Dinamica de Memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
7.6 Problemas Gerados por Apontadores . . . . . . . . . . . . . . . . . . . . . . . . . 198
7.6.1 Apontadores Nao Inicializados . . . . . . . . . . . . . . . . . . . . . . . . 198
7.6.2 Objetos Pendentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
7.6.3 Referencia Pendente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
7.6.4 Programacao Macarronica . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
7.7 TAD Implementacional Lista Encadeada tLista . . . . . . . . . . . . . . . . . . . 200
7.7.1 Definicao do Tipo tNo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
7.7.2 Atributos de tLista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
7.7.3 Operacoes de tLista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
7.7.4 Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
7.8 Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
7.9 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
7.10 Lista de Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
7.11 Trabalhos Sugeridos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
8 Arquivos 235
8.1 Variaveis Transientes X Variaveis Persistentes . . . . . . . . . . . . . . . . . . . . 235
8.2 Tipos de Arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
8.2.1 Tipos de Arquivos - Arquivos Texto . . . . . . . . . . . . . . . . . . . . . 236
8.2.2 Tipos de Arquivos - Arquivos Binarios . . . . . . . . . . . . . . . . . . . . 237
8.3 Definicao de arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
8.4 Operacao sobre arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
8.4.1 Abertura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
8.4.2 Fechamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
8.5 Operacoes sobre arquivos texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
8.5.1 Leitura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
8.5.2 Escrita . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
8.6 Operacoes sobre arquivos binarios . . . . . . . . . . . . . . . . . . . . . . . . . . 244
8.6.1 Leitura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
8.6.2 Escrita . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
8.7 Outras funcoes uteis para arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . 246
8.7.1 feof() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
8.7.2 fseek() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
8.8 Exercicios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
SUMARIO 5
Introducao
Co-autor:
Andre Boechat
Objetivos:
Definir o que sao algoritmos e programas, alem de apresentar algumas tecnicas para de-
senvolve-los;
1.1 Historico
Dado o alto grau tecnologico dos computadores atuais, pode ser difcil imaginar que os primeiros
computadores eram totalmente mecanicos. Diversos tipos foram projetados e construdos ao
longo da evolucao, chegando aos modernos computadores digitais; porem, alguns se destacam
pela inovacao e complexidade que marcaram suas epocas.
6
1.1. HISTORICO 7
A primeira maquina programavel que se tem notcia foi construda pelo professor de ma-
tematica da Universidade de Cambridge, Charles Babbage (1792-1871). A maquina analtica,
como ficou conhecida, era totalmente mecanica, composta basicamente por engrenagens que
formavam quatro componentes: a memoria, a unidade de calculo ou computacao, a unidade
de entrada e a de sada. A unidade de computacao recebia operandos da memoria para reali-
zar sobre eles as operacoes de soma, subtracao, multiplicacao ou divisao, e depois armazenar o
resultado na memoria.
Como a maquina analtica executava as instrucoes lidas pela unidade de entrada, era possvel
executar diferentes sequencias de calculos, bastando para isso que um programa diferente fosse
utilizado. Assim, a primeira pessoa no mundo a programar um computador foi a jovem Ada
Augusta Lovelace, contratada pelo proprio Babbage a fim de produzir o software necessario para
o funcionamento da maquina.
A procura por maquinas calculadoras cresceu com a Segunda Guerra Mundial, estimulando
o surgimento dos primeiros computadores eletronicos. O professor de fsica da Universidade da
Pensilvania, John Mauchley, junto com seu aluno de mestrado, J. Presper Eckert, construiu
um computador chamado ENIAC (Electronic Numerical Integrator And Computer )[2], o qual
detinava-se ao computo de trajetorias taticas que exigissem conhecimento substancial em ma-
tematica. O ENIAC tinha 18.000 valvulas e 1.500 reles, pesava 30 toneladas e consumia 140
quilowatts de energia eletrica. Para programar o ENIAC, era necessario ajustar a posicao de
6.000 chaves de varias posicoes e conectar um numero imenso de soquetes por meio de uma ver-
dadeira floresta de cabos. Este gigantesco computador so ficou pronto em 1946, apos o termino
da guerra.
Um dos pesquisadores envolvidos no projeto do ENIAC, John von Neumann, construiu para
o Instituto de Estudos Avancado de Princeton (Princeton Institute of Advanced Studies IAS )
a maquina IAS[3], a qual ainda e a base de praticamente todas as maquina atuais. Ele imaginou
que os programas poderiam ser representados em formato digital na memoria, junto com os
dados.
A invencao do transstor e o desenvolvimento de circuitos integrados revolucionaram os pro-
jetos de computadores do final da decada de 1950, tornando obsoletos os computadores valvula-
dos. Nas decadas de 1960 e 1970, as famlias de computadores da IBM1 (International Business
Machines), System/360, e da DEC2 (Digital Equipament Corporation), PDP-11, dominavam o
mercado.
Nesse perodo surgiu uma das linguagens de programacao mais usadas para o desenvolvi-
mento de softwares e tambem adotada neste livro, a linguagem C[1]. Ela e, ainda hoje, muito uti-
lizada para a criacao de programas diversos, como processadores de texto, planilhas eletronicas,
programas para a solucao de problemas de engenharia, e muitos outros.
Diversos fatores, como a criacao de linguagens de programacao mais semelhantes a linguagem
humana3 , o que facilitava a vida dos programadores, e a integracao de circuitos em escala muito
alta, o que aumentou o desempenho e diminuu o tamanho das maquinas, deram incio a era
1
http://www.ibm.com
2
http://www.hp.com
3
Essas linguagens sao chamadas de linguagens de alto nvel
8 CAPITULO 1. INTRODUCAO
dos computadores pessoais. E foi nesse contexto, na decada de 80, que a IBM construiu o
computador mais vendido de toda a historia, o Personal Computer o famoso PC.
O processador utilizado no PC foi construdo por uma promissora empresa da epoca, a Intel4 .
E a versao inicial desse computador vinha com o sistema operacional MS-DOS fornecido por
outra empresa, a recem-criada Microsoft Corporation.
1.2.1 Memoria
A memoria e a parte do computador onde as operacoes a serem executadas pelo computador
(instrucoes) e as informacoes a serem processadas pelo mesmo (dados) sao armazenados.
Na memoria, o processador le e escreve informacoes ao executar as instrucoes de um pro-
grama. Existem dois tipos de memoria: a memoria principal e a secundaria. A principal,
conhecida tambem como RAM (Random Access Memory), possui as seguintes caractersticas:
Proporciona ao computador acesso rapido aos dados e instrucoes armazenados por ela;
1.2.2 Processador
O processador e o cerebro do computador. Basicamente, o processador e responsavel por
buscar instrucoes na memoria, decodifica-las para determinar seus operandos (dados que serao
4
http://www.intel.com
1.3. ALGORITMOS E PROGRAMAS 9
usados ao processar a instrucao) e quais operacoes devem ser realizadas com os mesmos, e
executar tais operacoes. Essas tarefas compoem o processo de execucao de um programa.
8. Enxugar o rosto.
Apos conhecer um exemplo comum de algoritmo, torna-se mais facil compreender a sua definicao:
Apesar de conseguirem executar operacoes muito complexas, os computadores nao sao do-
tados da mesma capacidade de compreensao que os humanos. Uma sequencia de tarefas consi-
derada simples e obvia para uma pessoa qualquer pode ser incompreensvel e pouco detalhada
para um computador.
Voltando ao exemplo da escovacao, um computador poderia considera-lo incompleto, pois
ha diversas questoes sobre como algum passo pode ser executado, por exemplo:
3. O que fazer se nao houver pasta? Escovar mesmo sem a pasta ou interromper o processo
de escovacao?
Questoes como essas sao facilmente contornadas por um humano, decididas instantaneamente
de acordo com o ambiente que o cerca. Entao, para que uma maquina tambem possa decidir
o que fazer em situacoes semelhantes, e necessario que se estabelecam as condicoes iniciais
(condicoes de entrada) e as finais (condicoes de sada) do problema. Dessa forma, ela sabera
quais ferramentas poderao ser usadas para atingir a condicao de sada desejada.
Assim, o problema da escovacao seria mais bem definido da seguinte maneira:
Condicoes de Entrada: Dentes sujos com restos de alimentos, uma escova dental em condicoes
de uso, 90 gramas de creme dental e 300 mililitros de agua tratada.
Condicoes de Sada: Dentes limpos (sem restos de alimentos visveis), uma escova dental em
condicoes de uso e 85 gramas de creme dental. Toda a quantidade de agua deve ser
utilizada.
Portanto, para um computador, os algoritmos definem o conjunto de atividades que ele deve
desempenhar para solucionar um problema.
Contudo, tao importante quanto saber o que escrever para a maquina e saber como
escrever. Para que um computador possa executar um algoritmo, e necessario que esse algoritmo
seja traduzido para uma linguagem de programacao, geralmente incompreensvel para a maioria
das pessoas. Ao se traduzir um algoritmo para uma linguagem de programacao, obtem-se um
programa.
8. Enxugar o rosto.
1. Enquanto nao encontrar a escova e o tubo de pasta, continuar procurando por cada gaveta
do armario;
5
Tambem conhecida como dividir para conquistar.
12 CAPITULO 1. INTRODUCAO
2. Caso tenham acabado as gavetas e nao tenha encontrado a escova e o tubo, interromper a
tarefa.
O exemplo anterior mostra apenas parte do processo de refinamentos, que deve prosseguir
ate que cada atividade esteja suficientemente detalhada, possibilitando que o computador as
reconheca e execute.
Nome da receita;
Modo de preparo: descreve a forma de trabalhar com os ingredientes para que se obtenha
o resultado esperado;
Documentacao: explica certos aspectos nao muito claros do programa, tanto no corpo do
programa quanto no cabecalho ou no dicionario de dados.
Pseudocodigo 1.1 Calculo das razes reais de uma equacao de segundo grau.
Descricao: programa que calcula as razes reais de uma equacao de segundo grau.
Dados de Entrada: os tres coeficientes da equacao.
Sada do Programa: razes reais da equacao.
Leitura dos coeficientes da equac~ao;
Calculo do discriminante (delta);
Calculo das razes pelo processo componente "Calculo das Razes";
Utilizando a tecnica dos refinamentos sucessivos, na ultima linha do Pseudocodigo 1.1 ocorre
a execucao do processo componente Calculo das Razes, que, a partir dos valores dos coeficien-
tes lidos e do discriminante calculado, encontra os valores das razes reais da equacao. No geral,
o processo componente Calculo das Razes deve conter os passos descritos no Pseudocodigo
1.2.
No Exemplo 1.1, o programa para o calculo das razes esta transcrito para a linguagem de
programacao C. Deve-se destacar que o texto escrito entre os smbolos /* e */ e sempre
ignorado pelo computador, nao alterando em nada o funcionamento do programa. Porem, esse
texto serve para comentar o codigo e deixa-lo mais claro. Pode-se faze-los tambem utilizando o
smbolo //, que transforma em comentario tudo aquilo que estiver a sua direita, ate o final
da linha.
1 /*
2 Programa para calculo das razes de segundo grau .
3 Dados de entrada : os coeficientes a , b e c de uma equac~ ao
4 da forma ax ^2 + bx + c = 0
5 Dados de sada : imprime na tela as razes reais da equac~a o , caso existam .
6 Restric~
a o : N~
a o se considera o zero como um possvel valor de a .
7 */
8
9 # include < math .h >
10 # include < stdio .h >
11
12 main () {
13 float a ; // Coeficiente angular .
14 float b ; // Coeficiente linear .
15 float c ; // Termo independente .
16 float delta ; // Discriminante .
17 float raiz1 ; // A primeira raiz .
18 float raiz2 ; // A segunda raiz .
19
20 // Leitura dos coeficientes .
21 scanf ( " % f % f % f " ,&a , &b , & c ) ;
22
23 // Calculo do discriminante ( delta ) .
24 delta = b * b - 4 * a * c ;
25
26 // Calculo das razes .
27 if ( delta < 0) {
28 printf ( " A equac~
a o n~
a o possui razes reais " ) ;
29 } else {
30 raiz1 = ( - b + sqrt ( delta ) ) / (2* a ) ;
31 raiz2 = ( - b - sqrt ( delta ) ) / (2* a ) ;
32 printf ( " As razes da equac~ao s~
a o : % f e % f " , raiz1 , raiz2 ) ;
33 }
34 }
Exemplo 1.1: Programa para o calculo das razes reais de uma equacao de segundo grau.
Na linguagem C, o cabecalho dos programas sempre tem o nome main() e o incio do corpo
do programa e marcado pela chave {, assim como o final e marcado pela ultima chave }.
As linhas 9 e 10 permitem o uso dos comandos sqrt e printf (o significado desses comandos e
explicado no proximo captulo).
1.6. TRADUCAO DE PROGRAMAS 15
1.6.1 Compilacao
O processo de compilacao efetua a traducao integral do programa fonte para o codigo de maquina.
Uma vez traduzido, o programa em linguagem de maquina pode ser executado diretamente. A
Figura 1.2 ilustra esse processo.
A grande vantagem desse metodo de traducao e a rapidez na execucao dos programas, pois
nao e necessario fazer qualquer traducao durante a execucao. Porem, o codigo objeto gerado pela
compilacao e, geralmente, especfico para um determinado tipo de arquitetura de computador.
Assim, e necessario uma nova compilacao do programa para cada tipo diferente de computador.
A linguagem C, adotada neste livro, e um exemplo de linguagem de programacao compilada.
1.6.2 Interpretacao
No processo de interpretacao, cada instrucao do codigo fonte e traduzida no instante imedia-
tamente anterior a execucao dela. Desse modo, em contraste com a compilacao, a traducao e
execucao de programas interpretados podem ser vistas como um processo unico (veja as Figuras
1.2 e 1.3).
16 CAPITULO 1. INTRODUCAO
Apesar de ser um processo mais lento quando comparado ao metodo anterior, a interpretacao
apresenta maior flexibilidade na construcao de programas e facilidade de depuracao de codigo.
A presenca do interpretador durante a execucao permite, por exemplo, a execucao de trechos
de programas criados, alterados ou obtidos durante a propria execucao.
1.7 Resumo
O processador e o componente do computador responsavel por executar os programas
armazenados na memoria.
Algoritmo e uma sequencia de operacoes que deve ser executada em uma ordem definida
e nao-ambgua, com o proposito de solucionar um determinado problema. Ao traduzir-se
um algoritmo para uma linguagem de programacao, obtem-se um programa.
Um programa bem estruturado deve conter as seguintes partes bem definidas: cabecalho,
dicionario de dados, corpo e documentacao.
1.8. EXERCICIOS PROPOSTOS 17
3. Considere uma memoria que possua celulas de 8 bits cada uma. Qual o numero de possveis
informacoes diferentes cada celula pode armazenar?
Conceitos Basicos
Co-autores:
Andre Boechat
Bruno Pandolfi
Objetivos:
Apresentar o conceito de variavel;
18
2.1. VARIAVEIS E CELULAS DE MEMORIA 19
Como dito na Secao 1.2.1, a memoria pode ser entendida como uma sequencia de celulas,
cada celula podendo armazenar uma porcao dos dados de um programa. Gracas a essa ordenacao
sequencial, cada celula possui um numero identificador chamado endereco, que esta relacionado
com a posicao que a celula ocupa na sequencia. Por meio dos enderecos e possvel acessar
qualquer celula para ler ou alterar seu conteudo. A Figura 2.1 esboca essa ideia, porem, em
contraste com a Figura 1.1, omite a representacao dos bits.
Figura 2.1: Modelo para a estrutura da memoria do computador, mostrando uma fracao de seis
celulas, seus enderecos e conteudos.
As seis celulas de memoria da Figura 2.1 podem guardar valores numericos ou letras em seus
interiores. Cada celula e apontada por um endereco unico e sequencial.
A estrutura da memoria favorece a execucao de suas duas operacoes basicas: leitura e escrita.
A leitura e o processo de consulta do conteudo da celula. Essa operacao nao altera o valor
guardado na celula.
A escrita e o processo de armazenamento de um novo dado na celula indicada por um
endereco conhecido. Apos a execucao da escrita, o valor que estava anteriormente armazenado
na celula e perdido.
Um algoritmo para o calculo do valor da conta mensal de energia eletrica e um bom exemplo
para compreender as operacoes basicas da memoria. Os dados de entrada desse algoritmo sao: a
leitura atual do medidor, a leitura registrada no mes anterior e o preco do quilowatt-hora (kWh)
unidade comercial de energia eletrica.
Na pratica, o que se faz para calcular o valor da conta e encontrar a diferenca entre a leitura
atual do medidor e a leitura do mes anterior e multiplicar esse valor pelo preco do quilowatt-hora.
O algoritmo computacional, por sua vez, e descrito no Pseudocodigo 2.1.
No Pseudocodigo 2.1, o computador le os conteudos das celulas 43 e 46 (que guardam os
valores das leituras atual e anterior, respectivamente), realiza a subtracao e o resultado e guar-
dado na celula 41. Posteriormente, essa diferenca e lida e seu valor multiplicado pelo conteudo
da celula 44, que guarda o valor do preco unitario do kWh. Finalmente, o valor da conta e
guardado na celula 42.
Acompanhando a descricao acima e possvel chegar a situacao encontrada na Figura 2.2.
Nos primordios da programacao, percebeu-se que acessar a memoria por meio de enderecos
era trabalhoso demais e causa constante de erros. Isso porque o programador deveria escolher os
enderecos das celulas com as quais iria trabalhar, tanto das celulas que teriam valores a serem
lidos quanto das que seriam usadas para a escrita de resultados.
Essa situacao confusa e observavel no exemplo da conta de energia eletrica. Sem o comentario
20 CAPITULO 2. CONCEITOS BASICOS
Pseudocodigo 2.1 Calculo da conta de energia eletrica utilizando os enderecos das celulas de
memoria.
Descricao: programa que calcula o valor da conta de energia eletrica.
Dados de Entrada: celulas 43 e 46 contendo, respectivamente, a leitura atual e a anterior; e valor
unitario do kWh na celula 44.
Sada do Programa: valor da conta, armazenada na celula 42.
Subtrair o conteudo da celula 43 do conteudo da celula 46 e guardar na
celula 41;
Multiplicar o conteudo da celula 41 pelo conteudo da celula 44 e guardar na
celula 42;
Figura 2.2: Conteudo da memoria apos execucao do algoritmo que calcula o valor de uma conta de
energia eletrica.
no incio do Pseudocodigo 2.1, seria impossvel ter a mnima ideia do que o programa faz em
cada passo. Isso torna difcil nao so a escrita, mas tambem a correcao dos programas.
Para resolver essa questao, o conceito de variavel foi criado. Uma variavel nada mais e do
que uma abstracao para o endereco de memoria. Com o emprego de variaveis, as celulas de
memoria sao referenciadas nos programas por meio de rotulos, definidos com ajuda do bom-
senso do programador. O compilador fica encarregado do trabalho de transformar rotulos em
enderecos para que as operacoes de acesso a memoria sejam realizadas.
O Pseudocodigo 2.2 mostra como fica o programa que calcula o valor da conta de energia
eletrica, agora escrito utilizando variaveis.
A partir do Pseudocodigo 2.2, um compilador pode converter os rotulos para posicoes quais-
quer na memoria, evitando que o programador tenha que se preocupar em manipula-las. Uma
2.2. IDENTIFICADORES 21
Figura 2.3: Uma possvel traducao para enderecos de memoria, a partir dos rotulos do codigo que utiliza
variaveis.
2.2 Identificadores
Em geral, as linguagens de alto nvel possuem dois tipos de elementos: os elementos definidos
pela propria linguagem smbolos para operadores, nome de comandos etc e os elementos
definidos pelo programador identificadores, comentarios etc.
Um identificador e um smbolo que pode representar alguma entidade criada pelo progra-
mador, como uma variavel, por exemplo. Cada linguagem define uma regra para formacao de
identificadores. Em geral, sempre e possvel utilizar uma sequencia de caracteres alfanumericos
letras ou dgitos, sem acentos e sem cedilha sendo que o primeiro caractere deve ser obri-
gatoriamente alfabetico. Os Exemplos 2.1 e 2.2 apresentam, respectivamente, nomes corretos e
nomes invalidos de variaveis na linguagem C.
Algumas linguagens, como C, fazem diferenciacao entre letras maiusculas e minusculas.
Desta forma, uma variavel de nome Saldo e considerada diferente de outra de nome saldo,
ou, ainda, de nome SALDO. E importante ressalvar que nao e uma boa pratica de programacao
criar identificadores que apenas se diferenciem pelo formato das letras, como no exemplo do
saldo, pois isso tem impacto negativo na legibilidade dos programas.
22 CAPITULO 2. CONCEITOS BASICOS
1 abc
2 x1
3 y2
4 letra
5 SOMA_TOTAL
6 B_32
1 fim ? // ? n~
a o e um caractere alfanumerico
2 % percentual % // % n~
a o e um caractere alfanumerico
3 123 quatro // Iniciado por numero
4 ! hola ! // ! n~
a o e um caracter alfanumerico
5 @ARROBA // @ n~ a o e um caractere alfanumerico
Normalmente, em grandes projetos de software, sao adotados padroes para a escrita dos
identificadores a fim de que os programadores possam trocar seus codigos, entende-los e altera-
los sem grande dificuldade. Neste texto, e adotado como regra na escrita:
Nomes compostos: primeira parte e iniciada por letra minuscula e as demais partes inici-
adas por letra maiuscula. Os demais caracteres sao minusculos.
Como dito na Secao 1.5, um bom programa deve necessariamente ser legvel e de facil com-
preensao. A escolha dos nomes dos identificadores influenciam diretamente esses dois aspectos.
Logo, e muito importante que os nomes sejam significativos, deixando bem clara a sua referencia.
A fim de contrastar com a declaracao de variaveis do Exemplo 2.1, o Exemplo 2.3 ilustra
a convencao proposta, apresentando nomes significativos para variaveis. Neste, o rotulo das
variaveis ja e suficiente para informar a funcionalidade das mesmas, tornando rapida a compre-
ensao do programa que as utiliza e dispensando a necessidade de comentarios.
1 delta
2 raiz1
3 idade
4 letra
5 perc entualDeLucro
6 primeiraLetra
7 indiceBovespa
1 main () {
2 leituraAtual = 125;
3 leituraAnterior = 25;
4 valorUnitario = 2;
5 diferenca = leituraAtual - leituraAnterior ;
6 valorConta = diferenca * valorUnitario ;
7 }
<variavel> = <express~
ao>;
24 CAPITULO 2. CONCEITOS BASICOS
1 contador = 10;
2 contador = contador + 1;
No Exemplo 2.5 ocorrem duas atribuicoes. A primeira ocorre na linha 1, sendo mais um
exemplo simples de inicializacao de uma variavel, como ja discutido anteriormente. Na linha 2,
a variavel contador aparece em ambos os lados da expressao, o que pode parecer um absurdo
matematico. Porem, e necessario lembrar que, nesse contexto, o smbolo = atribui o valor da
expressao da direita a variavel que aparece a esquerda. Ou seja, avalia-se primeiro o resultado
da expressao a direita (10 + 1 = 11) e, posteriormente, atribui-se o valor a variavel a esquerda
(contador = 11). Isso e representado graficamente na Figura 2.5.
Mais uma observacao e necessaria: O programador deve ficar atento aos tipos de dados
manipulados em um comando de atribuicao, pois o tipo da expressao a direita deve ser compatvel
com o tipo da variavel a esquerda. A manipulacao dos tipos de dados e detalhada na Secao 2.4.
2.4. TIPOS DE DADOS 25
<tipo> <identificador>;
26 CAPITULO 2. CONCEITOS BASICOS
E possvel declarar mais de uma variavel em uma mesma linha, desde que todas sejam do mesmo
tipo, conforme o Exemplo 2.6.
1 main () {
2 int x , y , a , b , c , d , e , f ;
3
4 x = 10;
5 y = 3;
6
7 a = x + y; // a armazena 13
8 b = x - y; // b armazena 7
9 c = x * y; // c armazena 30
10 d = x / y; // d armazena 3
11 e = x % y; // e armazena 1
2.4. TIPOS DE DADOS 27
12 f = -a ; // f armazena -13
13 // c = xy ;
14 }
1 main () {
2 float p , q , x , z ;
3 int y ;
4
5 x = 10;
6 y = 4;
7
8 p = 50 / 30; // p guardara 1
9 q = 10.0 / 4; // q guardara 2.5
10
11 z = x / y; // z guardara 2.5
12 }
As operacoes entre variaveis do tipo float sao escritas com os mesmos smbolos das operacoes
do tipo int. O Exemplo 2.8 mostra particularidades do processo de compilacao que podem gerar
erros nos programas.
28 CAPITULO 2. CONCEITOS BASICOS
A linha 8 do Exemplo 2.8 possui um comentario explicando que a variavel p recebe 1 como
resultado da operacao de divisao. Tal erro nao ocorre para a divisao da linha seguinte. A
justificativa para essa situacao e que, conforme e mostrado na Secao 2.3, o computador primeiro
calcula o resultado da expressao do lado direito do comando de atribuicao para, em seguida,
escrever esse valor na correspondente variavel de destino.
Durante a compilacao, o compilador checa os tipos das variaveis envolvidas nos calculos
para realizar as operacoes correspondentes. Entretanto, quando ha apenas valores numericos
envolvidos, o compilador considera os numeros escritos sem ponto decimal como sendo do tipo
inteiro. E por isso que a divisao realizada na linha 8 do Exemplo 2.8 resulta em 1. O computador
entende a operacao 50/30 como uma divisao inteira, descartando a parte decimal do resultado.
Ja na linha 9, o compilador enxerga o valor 10.0 como sendo do tipo float devido a presenca
explcita do ponto decimal, e realiza a divisao, resultando no valor esperado.
No caso da ultima linha do Exemplo 2.8, nenhum resultado inesperado ocorre porque o
tipo mais abrangente tipo float da variavel x encontrado na expressao e considerado nas
operacoes, garantindo o resultado esperado. Caso a variavel x tambem fosse do tipo int, entao
o mesmo incoveniente da linha 8 ocorreria.
Com o passar do tempo, a tabela ASCII sofreu modificacoes. Ela foi atualizada e incorporou
mais 128 novos smbolos, os quais incluem, por exemplo, os caracteres acentuados e o cedilha,
do Portugues. Essa nova versao e conhecida como tabela ASCII estendida.
Para fazer com que uma variavel do tipo char aponte para um smbolo de interesse, o
programador precisa apenas atribuir a essa variavel o caractere desejado, colocando-o entre
aspas simples. O Exemplo 2.9 exibe essa situacao.
1 main () {
2 char letraA , letraFMaiuscula , simboloSoma ;
3
4 letraA = a ;
30 CAPITULO 2. CONCEITOS BASICOS
5 letraFMaiuscula = F ;
6 simboloSoma = + ;
7 }
As diferencas nos tamanhos sao justificaveis, uma vez que, quanto maior o conjunto a ser
representado, mais celulas de memoria sao necessarias para acomodar uma variavel. Desta
forma, sendo o conjunto dos caracteres o menor, e preciso apenas um byte (uma celula) para
representa-lo. Ja o conjunto do tipo float, que e o maior dos tres, precisa de 4 bytes (4 celulas)
para uma representacao coerente de seus valores.
E preciso ter em mente que, devido a essas restricoes de implementacao, nao se deve utili-
zar operacoes de atribuicao entre variaveis sem tomar algum cuidado. Se elas forem de tipos
diferentes, problemas podem acontecer.
Nao ha problemas em atribuir uma variavel do tipo int a outra do tipo float. Tal qual ocorre
na matematica, toda variavel do tipo int e possvel de ser representada por outra do tipo float,
pois o conjunto dos numeros racionais contem o conjunto dos inteiros. Um raciocnio similar
pode ser empregado para justificar as atribuicoes de variaveis char a variaveis de tipo int ou
float, que tambem ocorrem sem erros.
O problema e quando se tenta atribuir uma variavel de um conjunto mais amplo a uma de
um conjunto mais restrito. Veja o Exemplo 2.10.
Observando o Exemplo 2.10, um programador distrado pode pensar que houve uma atri-
buicao correta. Entretanto, o valor armazenado pela variavel teste e 125. Nao havendo condicoes
de armazenar corretamente (um codigo de 4 bytes nao pode ser escrito num espaco de apenas
2 bytes), o compilador simplesmente descarta a parte decimal do numero, armazenando apenas
2.5. CONSTANTES 31
sua parte inteira na variavel. Logo, e preciso permanecer atento as atribuicoes desse tipo para
nao se equivocar sobre o valor armazenado por uma variavel.
1 main () {
2 int teste ;
3
4 teste = 125.568;
5 }
2.5 Constantes
Em algumas situacoes surge a necessidade de se utilizar um determinado valor constante em
diversas partes do codigo. Para simplificar a alteracao dessas constantes e facilitar a leitura do
codigo, e possvel definir um nome signicativo para elas de maneira simples, por meio de uma
estrutura especfica.
Essa estrutura e iniciada pela diretiva #define, seguida pelo identificador da constante e
pelo seu respectivo valor, todos separados por um espaco em branco.
#define <identificador> <valor>
Na linguagem C, a declaracao de constantes deve ser feita no incio do codigo, antes da funcao
main. O Exemplo 2.11 apresenta declaracoes validas de constantes.
1 # define PI 3.141593
2 # define FALSO 0
3 # define VERDADEIRO 1
4 # define RAIZDEDOIS 1.414214
5
6 main () {
7 float raio , comprimento , area ;
8
9 raio = 10;
10 area = PI * raio * raio ;
11 comprimento = 2 * PI * raio ;
12 }
Na declaracao de constantes, a escolha dos nomes segue as mesmas regras para a escrita dos
nomes de variaveis. Geralmente, para diferenciar as constantes no codigo, opta-se por escrever
seus nomes com todas as letras maiusculas.
O Exemplo 2.11 mostra um exemplo simples onde diversas constantes sao declaradas de
acordo com a convencao proposta. Durante a compilacao do codigo, o compilador troca tocas
as ocorrencias da constante PI por seu respectivo valor, definido na linha 1.
32 CAPITULO 2. CONCEITOS BASICOS
2.6 Expressoes
As variaveis e constantes podem ser combinadas com os operadores associados a cada tipo de
dados, gerando expressoes.
2. Adicao e subtracao.
Para se obter uma sequencia diferente de calculos, varios nveis de parenteses podem ser
usados para quebrar as prioridades definidas. Nao e permitido o uso de colchetes e chaves, uma
vez que estes smbolos ja sao utilizados com outras finalidades. O Exemplo 2.12 exibe uma serie
de expressoes aritmeticas validas.
1 main () {
2 int x , y , z ;
3
4 x = 10 * 10 + 25; // x = 125
5 y = 5 * (7 + 3) ; // y = 50
6 z = 10 * (( x - 25) / y + ( x - 25) * 2) ; // z = 2020
7 }
Exemplo 2.12: Expressoes aritmeticas e o uso de parenteses para determinar a precedencia das
operacoes.
<nome da func~
ao>(<valor>)
Nome Descricao
abs Modulo ou valor absoluto de um valor inteiro
fabs Modulo ou valor absoluto de um valor racional
sin Seno de um angulo em radianos
cos Cosseno de um angulo em radianos
sqrt Raiz quadrada de um numero
pow Potenciacao
floor Maior inteiro nao maior que o numero de entrada
ceil Menor inteiro nao menor que o numero de entrada
log Logaritmo neperiano
exp Exponencial
Para o uso das funcoes matematicas apresentadas no Exemplo 2.13 e necessario a inclusao
da biblioteca math.h, como na linha 1. Os mesmos calculos para a procura das razes de uma
equacao de segundo grau sao executados nas linhas 13 a 15, onde a funcao sqrt calcula a raiz
quadrada do seu operando a variavel delta. Ja as linhas 15 a 17 apresentam calculos avulsos:
a linha 17 executa o calculo do seno de /2 e armazena o resultado na variavel x; a linha
18 armazena, na variavel y, o modulo do valor armazenado em x; e a linha 19 calcula o valor
armazenado em y elevado a potencia de 2 e armazena o resultado na variavel z.
34 CAPITULO 2. CONCEITOS BASICOS
As expressoes relacionais resultam sempre em 0 (zero) para uma comparacao falsa ou 1 para
uma comparacao verdadeira.
As variaveis do tipo char tambem podem ser comparadas entre si, respeitando a ordenacao
do padrao de codificacao utilizado.
E importante atentar que, embora sejam escritos de maneira parecida, ha uma grande dife-
renca entre o operador de comparacao == e o comando de atribuicao =. Essa semelhanca e
uma fonte comum de erros de programacao, geralmente difceis de detectar.
Os resultados obtidos das expressoes logicas tambem sao valores do tipo inteiro: 0 para falso
e 1 para verdadeiro.
Para melhor entender o que cada operador realiza, sao levados em conta dois valores P e Q
de entrada, que podem ser verdadeiros (V) ou falsos (F). A Tabela 2.4 resume todos os casos
possveis.
2.7. COMANDO DE ENTRADA DE DADOS 35
P Q P && Q P || Q !P !Q
V V V V F F
V F F V F V
F V F V V F
F F F F V V
O uso dos operadores relacionais e logicos fica bem mais claro adiante, neste captulo, quando
os comandos de selecao e repeticao sao estudados.
Essa estrutura e dividida em duas partes distintas. A primeira, colocada entre aspas duplas,
contem os formatos, os quais sao relacionados diretamente com os tipos das variaveis a serem
lidas. A segunda parte e uma lista dos nomes dessas variaveis, com uma relacao direta entre
a posicao nessa lista e o respectivo formato descrito na primeira parte do comando. Conforme
mostra a descricao da sintaxe, os nomes das variaveis devem ser precedidos pelo caractere &.
A Tabela 2.5 mostra alguns dos possveis formatos.
Codigo Significado
%c Le um unico caracter
%d Le um inteiro decimal
%f Le um numero em ponto flutuante
diferentes contas, bastando, para isso, executar novamente o programa. O codigo modificado e
exibido no Exemplo 2.14.
De acordo com o formato descrito no comando de leitura, os valores das variaveis leituraA-
tual, leituraAnterior e valorUnitario devem ser digitados pelo usuario, nesta ordem, separados
por espacos em branco.
Exemplo 2.14: Emprego do comando de entrada de dados para o algoritmo do Exemplo 2.4.
Observando o codigo do Exemplo 2.14, e importante ressaltar que nao ha mais a parte de
inicializacao das variaveis. Essa tarefa foi substituda pelo comando de entrada de dados e os
valores sao, agora, passados pelo usuario do programa. Esses valores podem ser alterados a cada
execucao do programa para que varias contas diferentes possam ser calculadas.
Codigo Significado
%c Caracter
%d Inteiros decimais com sinal
%e Notacao cientfica
%f Ponto flutuante decimal
%% Escreve o smbolo %
Tabela 2.6: Codigos para formatos de dados para o comando de sada printf .
Pode-se, ainda, mesclar uma mensagem de texto com valores de variaveis. O Exemplo 2.16
ilustra essa ideia. Nele, e possvel notar que, para cada especificador de formato na primeira
parte do comando, deve existir uma variavel de tipo correspondente posicionada adequadamente
na segunda parte.
Uma versao mais completa do codigo para o calculo da conta de energia eletrica e exibida no
Exemplo 2.17. Nele, o comando de sada e usado antes de cada comando de entrada de dados
(linhas 5, 8 e 11), escrevendo mensagens para indicar ao usuario do programa qual valor ele
devera informar. A ultima linha de codigo utiliza o comando de sada para exibir o valor do
calculo da conta. Trata-se de uma mensagem mista, em que o especificador %f sera substitudo
pelo valor da variavel valorConta. Supondo que, apos os calculos, essa variavel armazene o valor
187, a ultima mensagem exibida na tela seria:
6
7 printf ( " Digite o valor da leitura ATUAL : " ) ;
8 scanf ( " % d " , & leituraAtual ) ;
9
10 printf ( " Digite o valor da leitura ANTERIOR : " ) ;
11 scanf ( " % d " , & leituraAnterior ) ;
12
13 printf ( " Digite o preco do Quilowatt - hora : " ) ;
14 scanf ( " % f " , & valorUnitario ) ;
15
16 diferenca = leituraAtual - leituraAnterior ;
17 valorConta = diferenca * valorUnitario ;
18
19 printf ( " Valor da conta R$ : % f " , valorConta ) ;
20 }
Exemplo 2.17: Algoritmo mais interativo para o exemplo de calculo da conta de energia eletrica.
Quando se usa o comando printf , o caractere especial \n e bastante util. Ele serve para
quebrar a linha exatamente na posicao onde for inserido no texto. A Tabela 2.7 explica melhor
o funcionameto desse caractere.
Codigo Resultado
printf("OI.\nComo vai?"); OI.
Como vai?
int x; O valor de x eh:
x = 10; 10
printf("O valor de x eh:\n%d", x);
int a; Primeira linha.
a = 20; Segunda linha (a = 20).
printf("Primeira linha.\nSegunda linha (a = %d). \n\nFIM.", a);
FIM.
O comando de selecao permite que um programa possa realizar diferentes alternativas de sequencias
de instrucoes durante sua execucao. Dependendo do valor de uma expressao ou de uma variavel,
o programa segue executando uma ou outra sequencia de comandos.
Existem tres categorias distintas: selecao simples, selecao dupla e selecao multipla, as quais
sao abordadas a seguir.
2.9. COMANDOS DE SELECAO 39
O Exemplo 2.18 inicia exibindo uma mensagem ao usuario (linha 5), solicitando que sejam
digitados dois numeros. Esses valores sao armazenados em variaveis correspondentes (linha 6)
e, em seguida, sao mostrados ao usuario por meio de uma mensagem (linha 7). A expressao
logica do comando de selecao da linha 9 determina se ha necessidade de inverter os numeros
antes de exibi-los ordenadamente. E importante ressaltar que nao ha necessidade de ordenar os
numeros caso o usuario ja os tenha digitado na sequencia correta. Nesse caso, o programa pode
seguir normalmente e exibir na tela os valores das variaveis. Por ultimo, os valores sao exibidos
em ordem crescente (linha 14).
Ainda no Exemplo 2.18, as linhas 10, 11 e 12 sao importantes o bastante para serem destaca-
das. Elas tratam da operacao de troca de valores entre duas variaveis, a qual e muito utilizada
e deve ser bem entendida.
O comando de atribuicao e ideal para copiar o valor de uma variavel para outra. A questao e
que a variavel de destino tem seu valor alterado permanentemente apos a realizacao do comando.
Ou seja, o valor que estava escrito nessa variavel antes da execucao da atribuicao e apagado e o
novo valor e escrito em seu lugar.
No caso da operacao de troca de valores, se fossem usadas apenas as duas variaveis envolvidas,
uma delas teria seu valor perdido quando a primeira atribuicao fosse realizada. Esse problema
ocorre no Exemplo 2.19, no qual o valor inicial da variavel var1 e perdido.
1 main () {
2 int var1 , var2 ;
3
4 var1 = 100;
5 var2 = 200;
6
7 var1 = var2 ; // var1 guarda 200
8 var2 = var1 ; // var2 guarda 200
9 }
2.9. COMANDOS DE SELECAO 41
A solucao para o problema da perda de valores e simples e pode ser encontrada nas linhas
10, 11 e 12 do Exemplo 2.18. Basta usar uma variavel auxiliar e copiar para ela o valor da
primeira variavel de destino usada nas atribuicoes. Naquele exemplo, o valor da variavel num1
foi salvo na variavel aux para nao ser perdido na atribuicao da linha 11. Em seguida, esse valor
pode ser copiado corretamente para a variavel num2 na linha 12.
if (<express~ao logica>){
<Sequ^
encia de comandos 1>
}else{
<Sequ^
encia de comandos 2>
}
Pseudocodigo 2.4 Informa se um aluno esta aprovado ou nao, de acordo com a media das
notas obtidas no semestre.
Descricao: Algoritmo para decidir se um aluno esta aprovado ou nao, com base na media das notas
semestrais.
Dados de Entrada: matrcula do aluno, notas.
Sada do Programa: mensagem dizendo se o aluno esta aprovado ou nao.
Func~
ao Principal:
Leia a matrcula e as notas do aluno.
Calcule a media parcial.
SE a media parcial for menor que 7 ENT~
AO
Imprima uma mensagem dizendo que o aluno deve fazer prova final.
SEN~
AO
Imprima uma mensagem dizendo que o aluno esta aprovado.
FIM-SE.
FIM-Func~
ao Principal
9
10 if ( mediaParcial < 7.0) {
11 printf ( " \ nO aluno % d deve fazer prova final . " , matricula ) ;
12 } else {
13 printf ( " \ nO aluno % d foi aprovado com media % f " , matricula , media ) ;
14 }
15 }
Exemplo 2.20: Uso do comando de selecao if para a verificacao da media parcial de um aluno.
Pseudocodigo 2.5 Compara dois numeros e informa se foram digitados em ordem crescente,
decrescente ou se sao iguais.
Descricao: Algoritmo para decidir se dois numeros digitados estao em ordem crescente, decrescente ou
se sao iguais.
Dados de Entrada: matrcula do aluno, notas.
Sada do Programa: mensagem dizendo se o aluno esta aprovado ou nao.
Func~
ao Principal:
Leia dois numeros inteiros.
SE o primeiros for maior que o segundo ENT~ AO
Imprima uma mensagem dizendo que e ordem crescente.
SEN~
AO
SE o segundo for maior que o primeiro ENT~ AO
Imprima uma mensagem dizendo que e ordem decrescente.
SEN~
AO
Imprima uma mensagem dizendo que os numeros s~
ao iguais.
FIM-SE.
FIM-SE.
FIM-Func~
ao Principal
10
11 } else {
12 if ( num2 > num1 ) {
13 printf ( " \ nOrdem crescente . " ) ;
14
15 } else {
16 printf ( " \ nNumeros iguais . " ) ;
17 }
18 }
19 }
A estrutura dos comandos de selecao do Exemplo 2.21 permite testar todos os casos. Observe
que nao ha uma verificacao explcita para a igualdade dos numeros. Isso acontece porque todos
os demais casos possveis sao testados antes e, se nao se tratar de nenhum desses, entao os
numeros certamente sao iguais. Nesse caso, basta apenas exibir a mensagem correspondente.
Em muitos casos, alem de ser elegante, o uso da estrutura de selecao aninhada pode ser
mais eficiente. Verifica-se esse fato nos Exemplos 2.22 e 2.23. Ambos os programas leem tres
numeros digitados pelo usuario e identificam o maior deles, porem, um dos programas faz menos
verificacoes que o outro, ganhando eficiencia.
3 int n1 , n2 , n3 ;
4
5 printf ( " Entre com os tres numeros : " ) ;
6 scanf ( " % d % d % d " , & n1 , & n2 , & n3 ) ;
7
8 if (( n1 > n2 ) && ( n1 > n3 ) ) {
9 printf ( " O maior numero digitado foi : % d . " , n1 ) ;
10 }
11
12 if (( n2 > n1 ) && ( n2 > n3 ) ) {
13 printf ( " O maior numero digitado foi : % d . " , n2 ) ;
14 }
15
16 if (( n3 > n1 ) && ( n3 > n2 ) ) {
17 printf ( " O maior numero digitado foi : % d . " , n3 ) ;
18 }
19 }
Exemplo 2.22: Programa que identifica o maior de tres numeros sem o uso do comando de
selecao aninhado.
Exemplo 2.23: Programa que identifica o maior de tres numeros, usando o uso do comando de
selecao aninhado.
Pseudocodigo 2.6 Informa se tres numeros podem constituir os lados de um triangulo e o tipo
do triangulo que pode ser formado.
Descricao: Algoritmo para calcular se tres numeros podem representar os lados de um triangulo e,
caso seja possvel, o tipo do triangulo.
Dados de Entrada: tres valores inteiros.
Sada do Programa: mensagem dizendo o tipo do triangulo ou se nao pode ser formado um triangulo.
Func~
ao Principal:
Leia tr^
es numeros inteiros.
SE os tr^es valores forem iguais ENT~
AO
Imprima uma mensagem dizendo que e tri^
angulo equilatero.
~
SENAO
SE cada valor e menor do que a soma dos outros dois ENT~AO
es valores forem diferentes ENT~
SE os tr^ AO
Imprima uma mensagem dizendo que e tri^
angulo escaleno.
SEN~
AO
Imprima uma mensagem dizendo que e tri^
angulo isosceles.
FIM-SE.
SEN~
AO
Imprima uma mensagem dizendo que os valores n~ao podem ser lados de um tri^
angul
FIM-SE.
FIM-SE.
FIM-Func~
ao Principal
No Exemplo 2.24, o comando if mais externo (linha 13) confere se os numeros formam um
triangulo equilatero, verificando se os tres sao iguais. Caso nao formem um triangulo equilatero,
na linha 17 e verificado se os tres numeros formam realmente um triangulo, comparando a soma
de dois lados com a medida do terceiro (para todas as possibilidades). Caso verdadeiro, o if mais
interno confere se os tres lados sao diferentes um do outro, formando um triangulo escaleno; o
resultado falsopara esse ultimo teste implica necessariamente na formacao de um triangulo
isosceles.
switch (<express~ao>){
case <valor1>:
<sequ^
encia de comandos 1>
break;
case <valor2>:
<sequ^
encia de comandos 2>
break;
.
2.9. COMANDOS DE SELECAO 47
.
.
case <valorN>:
<sequ^
encia de comandos N>
break;
default :
<sequ^
encia de comandos>
}
3 int numero ;
4
5 printf ( " URNA ELETR O ^ NICA - SEU VOTO PARA PREFEITO : " ) ;
6 scanf ( " % d " , & numero ) ;
7
8 switch ( numero ) {
9 case 1:
10 printf ( " Candidato escolhido : Hort^ e ncia da Silva " ) ;
11 break ;
12 case 2:
13 printf ( " Candidato escolhido : Jose dos Cravos " ) ;
14 break ;
15 case 3:
16 printf ( " Candidato escolhido : Margarida S . Pereira " ) ;
17 break ;
18 default :
19 printf ( " Numero digitado invalido . Voto anulado . " ) ;
20 }
21 }
Exemplo 2.25: Uso do comando switch para simular uma urna eletronica de eleicao.
As aplicacoes para os comandos de repeticao sao praticamente infinitas porque quase todas
as tarefas contem partes que devem ser executadas mais de uma vez.
Na linguagem C, existem tres principais estruturas de repeticao. A opcao pelo uso de uma
ou outra depende normalmente da preferencia do programador e do problema em questao,
normalmente buscando-se o maximo de clareza ou facilidade de escrita do codigo. Essas tres
estruturas sao abordadas a seguir.
Exemplo 2.26: Comando de repeticao while para imprimir os n primeiros numeros inteiros.
O proximo programa, Exemplo 2.27, imprime os n primeiros numeros mpares. Essa quan-
tidade n e definida pelo usuario.
2.10. COMANDOS DE REPETICAO 51
Exemplo 2.27: Programa que imprime os n primeiros numeros mpares, utilizando o comando
de repeticao while.
No codigo do Exemplo 2.28, o usuario tem a possibilidade de corrigir o seu voto sem que o
programa termine.
Exemplo 2.28: Comando de repeticao while utilizado para melhorar o codigo do Exemplo 2.25.
do{
2.10. COMANDOS DE REPETICAO 53
<Sequ^
encia de comandos>
} while(<express~ao logica>);
O Pseudocodigo 2.9 pode ser modificado para melhorar sua legibilidade. Com o emprego do
comando FACA-ENQUANTO, o algoritmo fica mais simples.
Pseudocodigo 2.10 Urna eletronica com opcao de corrigir voto (versao melhorada).
Descricao: programa para imprimir na tela os n primeiros numeros positivos.
Dados de Entrada: numero do candidato, resposta da confirmacao.
Sada do Programa: mensagem indicando o candidato escolhido.
Func~
ao Principal:
FACA:
Leia o numero do candidato.
Imprima o nome do candidato escolhido.
Leia a resposta da confirmac~
ao.
ENQUANTO resposta for igual a zero.
FIM-Func~ao Principal
O programa do Exemplo 2.28 pode ser reescrito utilizando-se o comando do-while. Nesse
caso, o uso do do-while permite ao programador ter a opcao de nao inicializar a variavel resposta
antes da execucao da sequencia de comandos, responsabilizando o usuario pela inicializacao
(conforme se nota no Exemplo 2.29).
Exemplo 2.29: Aplicacao para o comando do-while, modificando o codigo do Exemplo 2.28.
No Exemplo 2.29, nota-se que nao ha mais a inicializacao explcita da variavel resposta.
Com o comando do-while, garante-se que a variavel sera inicializada pelo usuario antes de seu
valor ser testado na expressao logica da linha 25, utilizada como criterio de parada.
Pode parecer estranho, a primeira vista, como a expressao logica !resposta funciona. Na
verdade essa espressao e equivalente ao teste resposta == 0.
Para entender essa equivalencia, basta lembrar que o operador logico de negacao inverte o
valor logico da variavel ao qual ele e aplicado. E bom recordar tambem que, para os numeros
inteiros, o valor zero equivale a falso enquanto que qualquer numero diferente de zero e tomado
como valor logico verdadeiro. Assim sendo, sempre que o programador desejar testar se um
valor inteiro e igual a zero, basta testar o resultado do operador de negacao, pois o unico caso
em que ele retornara verdadeiro sera quando o valor numerico testado for zero.
Um outro exemplo simples para o uso do comando do-while e um algoritmo no qual o usuario
informa uma sequencia de valores positivos e, em seguida, a media desses valores e apresentada.
O usuario devera digitar zero quando quiser finalizar a sequencia e saber a media. O algoritmo
desse programa esta listado no Pseudocodigo 2.11.
Exemplo 2.30: Programa que calcula a media dos valores de uma sequencia digitada pelo usuario.
for (<inicializac~
ao>; <express~
ao logica>; <incremento>){
<Sequencia de comandos>
}
1 main () {
2 float primeiroTermo , razao , n , i , termo ;
3
4 printf ( " Informe o valor do primeiro termo da PG : " ) ;
5 scanf ( " % f " ,& primeiroTermo ) ;
6 printf ( " Informe o valor da razao da PG : " ) ;
7 scanf ( " % f " ,& razao ) ;
8 printf ( " Informe quantos termos devem ser impressos : " ) ;
9 scanf ( " % f " ,& n ) ;
10 termo = primeiroTermo ;
11 for ( i =0; i < n ; i = i +1) {
12 printf ( " % f " , termo ) ;
13 termo = termo * razao ;
14 }
15 }
Exemplo 2.31: Programa para exibir os n primeiros termos de uma progressao geometrica utilizando o
comando de repeticao for.
Exemplo 2.32: Programa para realizar a operacao de exponenciacao utilizando o comando de repeticao
for.
No Exemplo 2.32, os valores da base e do expoente sao fornecidos pelo usuario via comando
de entrada de dados (linha 6). A variavel de controle i e inicializada com o valor zero dentro
do comando for e, a cada iteracao, e incrementada de 1. Quando o valor de i atinge valor igual
ao valor da variavel expoente, as iteracoes sao terminadas e o programa segue para a linha 13,
58 CAPITULO 2. CONCEITOS BASICOS
Considera-se, como exemplo, o problema de um professor que ministra uma disciplina para
diversas turmas. Nesse caso, e interessante saber qual turma obteve melhor rendimento, o aluno
2.11. PROBLEMA DOS LOTES ENCAIXANTES 59
que mais se destacou em cada turma e os que menos renderam nos estudos. Os dados, nesse
caso, podem ser observados em quatro nveis.
O nvel de notas e o mais simples. Ele e indivisvel e e caracterizado pelas notas dos trabalhos
e provas realizados no semestre. O nvel seguinte e o das medias semestrais em que as notas sao
agrupadas por aluno. O terceiro agrupamento e o das turmas. Os alunos sao reunidos de acordo
com as turmas a que pertencem. Por ultimo, o nvel mais complexo, e a propria disciplina. Ela e
a entidade mais abrangente, que e composta diretamente pelas turmas existentes no determinado
semestre.
Problemas de lotes encaixantes, normalmente, sao resolvidos por meio do emprego de coman-
dos de repeticao aninhados. Quanto mais externo for o comando de repeticao, maior o nvel de
complexidade das entidades que estao sendo examinadas. A Figura 2.8 exibe essa ideia, usando
como exemplo o problema das notas escolares.
Figura 2.8: Comandos de repeticao aninhados, utilizados para resolver um problema de lotes
encaixantes.
Pseudocodigo 2.12 Problema dos Lotes Encaixantes para o problema das notas escolares
Descricao: programa para analisar as notas de uma disciplina escolar.
Dados de Entrada: codigo da turma, matrculas dos alunos, notas dos alunos.
Sada do Programa: media de cada aluno, matrculas dos alunos de melhor e de pior medias de cada
turma, codigo da turma com melhor rendimento.
Func~
ao Principal:
Leia o codigo de uma turma.
ENQUANTO codigo for diferente de -1 FACA:
Leia o numero de matrcula de um aluno.
ENQUANTO o numero de matrcula for diferente de -1 FACA:
Leia uma nota do aluno.
ENQUANTO a nota digitada n~ ao for negativa FACA:
Leia outra nota do aluno.
Atualize os dados do aluno.
FIM-ENQUANTO
Atualize os dados da turma.
Imprima a media das notas do aluno.
Leia o numero de matrcula de um aluno.
FIM-ENQUANTO
Atualize os dados da disciplina.
Imprima os numeros de matrcula e as medias dos alunos com maior
e menor medias da turma.
Leia o codigo de uma turma.
FIM-ENQUANTO
Impria o codigo da turma com melhor aproveitamento.
FIM-Func~
ao Principal
15 numAprovadosTurma = 0;
16 numAlunosTurma = 0;
17
18 printf ( " Informe o numero de matricula do aluno : " ) ;
19 scanf ( " % d " , & matricula ) ;
20 while ( matricula != -1) {
21 numNotasAluno = 0;
22 somaNotasAluno = 0;
23 numAlunosTurma = numAlunosTurma + 1;
24
25 printf ( " Informe a nota do aluno : " ) ;
26 scanf ( " % f " , & nota ) ;
27 while ( nota != -1) {
28 somaNotasAluno = somaNotasAluno + nota ;
29 numNotasAluno = numNotasAluno + 1;
30 printf ( " Informe a nota do aluno ou -1 para encerrar : " ) ;
31 scanf ( " % f " , & nota ) ;
32 }
33
34 mediaAluno = somaNotasAluno / numNotasAluno ;
35
36 if ( mediaAluno >= MEDIAMINIMA ) {
37 numAprovadosTurma = numAprovadosTurma + 1;
38 }
39
40 if ( mediaAluno >= maiorMediaTurma ) {
41 maiorMediaTurma = mediaAluno ;
42 alunoMaiorMedia = matricula ;
43 }
44
45 if ( mediaAluno <= menorMediaTurma ) {
46 menorMediaTurma = mediaAluno ;
47 alunoMenorMedia = matricula ;
48 }
49 printf ( " Media do aluno : % f \ n " , mediaAluno ) ;
50 printf ( " Informe o numero de matricula do aluno ou -1 para encerrar : "
);
51 scanf ( " % d " , & matricula ) ;
52 }
53
54 a p roveitamentoTurma = ( numAprovadosTurma / numAlunosTurma ) * 100;
55 if ( melhorAproveitamento < aproveitamentoTurma ) {
56 melhorAproveitamento = aproveitamentoTurma ;
57 melhorTurma = codTurma ;
58 }
59 printf ( " O aluno % d obteve a melhor media da turma (% f ) \ n " , alunoMaiorMedia
, maiorMediaTurma ) ;
60 printf ( " O aluno % d obteve a pior media da turma (% f ) \ n " , alunoMenorMedia ,
menorMediaTurma ) ;
61 printf ( " Informe o codigo da turma ou -1 para encerrar : " ) ;
62 scanf ( " % d " , & codTurma ) ;
62 CAPITULO 2. CONCEITOS BASICOS
63 }
64 printf ( " A turma % d obteve o melhor aproveitamento (% f %%) " , melhorTurma ,
m e l horAproveitamento ) ;
65 }
Exemplo 2.33: Problema dos lotes encaixantes aplicado a notas de uma disciplina
Fazendo uma comparacao com a Figura 2.8 e com o Pseudocodigo 2.12, nao fica complicado
entender o funcionamento do codigo do Exemplo 2.33. Apos a declaracao das variaveis, o
programa inicializa a variavel melhorAproveitamento (linha 8), que armazena o percentual de
aprovacoes da turma de melhor aproveitamento. O valor ajustado e o menor valor possvel (zero)
e sua escolha sera justificada adiante. Em seguida, o programa solicita ao usuario que digite um
codigo para identificar a primeira turma que sera analisada (linhas 10 e 11).
O primeiro comando de repeticao, na linha 12, serve para percorrer todas as turmas, uma
a uma. Sempre que sua expressao logica resultar verdadeiro, e sinal que uma nova turma tera
seus dados digitados.
Aqui vale destacar um artifcio util que e empregado nesse codigo. Todos os comandos de
repeticao fazem a verificacao de suas variaveis de controle comparando-as com o mesmo valor
1. Ocorre que, para evitar que o usuario seja obrigado a saber, a priori, da quantidade de
elementos de um conjunto de dados que sera passado ao programa, utiliza-se um valor absurdo
(qualquer valor que nao pertenca ao conjunto em questao) para servir de sinalizacao para o
programa de que o cunjunto de dados foi inteiramente percorrido. Esse valor e, comumente,
chamado de flag.
No Exemplo 2.33, considera-se que os codigos das turmas e das matrculas sao inteiros
positivos e que as notas sao valores racionais nao negativos. Assim sendo, o valor 1 foi utilizado
para indicar ao programa quando todas as notas de um aluno foram ja digitadas. Ou que todos
os dados dos alunos de uma determinada turma foram digitados. Ou ainda, que todas as turmas
tiveram seus dados informados.
A sequencia de comandos comeca, entao, com a inicializacao das variaveis da turma em
questao (linhas 13 a 16). As variaveis maiorMediaTurma e menorMediaTurma guardam, res-
pectivamente, a melhor e a pior media obtidas na turma. Ja numAprovadosTurma e nu-
mAlunosTurma armazenam, respectivamente, a quantidade de alunos aprovados na turma e a
quantidade total de alunos da turma. Os valores dessas duas variaveis servem para calcular o
aproveitamento da turma.
Nas linhas 18 e 19, o programa solicita ao usuario que informe a matrcula do primeiro aluno
da turma que tera lidas suas notas. Na linha seguinte aparece o segundo comando de repeticao,
o qual interage com os dados dos alunos de uma turma.
Assim como no comando de repeticao mais externo, o comando da linha 21 inicia sua
sequencia de comandos realizando a inicializacao das variaveis pertinentes. As variaveis num-
NotasAluno e somaNotasAluno servem para calcular a media aritmetica simples das notas do
aluno em questao armazenando, respectivamente, a quantidade de notas do aluno e a soma
delas. Na linha 23, a variavel numAlunosTurma e incrementada, indicando a adicao dos dados
de mais um aluno.
2.11. PROBLEMA DOS LOTES ENCAIXANTES 63
As linhas 25 e 26 requerem ao usuario que ele digite o valor da primeira nota do aluno em
questao. Em seguida, o programa executa o comando de repeticao mais interno, que serve para
registrar todas as notas de um determinado aluno.
Esse comando coleta os dados das notas, solicitando ao usuario que digite o valor da nota
(linhas 30 e 31) e atualizando os valores das variaveis que calculam a media do aluno no semestre.
Encerrada a digitacao das notas, o usuario deve digitar 1 para encerrar e, quando o faz, o
programa passa para a rotina de calculo da media (linha 34).
Com o valor da media, algumas verificacoes sao feitas para atualizar as estatsticas da turma
em questao. Em primeiro lugar, e verificado se a media alcancada pelo aluno e suficiente para
aprovacao. Em caso afirmativo, a variavel numAprovadosTurma e incrementada.
E verificado, em seguida (linha 40), se a media do aluno e maior que a maior media encontrada
ate entao. Em caso verdadeiro, a variavel maiorMediaTurma e atualizada, assim como a variavel
alunoMaiorMediaTurma, que guardara a matrcula desse aluno.
O comando de selecao da linha 45 e equivalente ao da linha 40. Desta vez, a intencao e
atualizar os dados do aluno com pior rendimento.
Vale um comentario sobre os valores de inicializacao das variaveis maiorMediaTurma e me-
norMediaTurma. E preciso ter em mente que, para a primeira verificacao de melhor e pior
aluno de cada turma, os respectivos comandos de selecao devem comparar o valor da media
calculada com os valores ja armazenados. O problema e que, para o primeiro aluno de cada
turma, essas variaveis ainda nao contem valores validos. Sendo assim, para evitar erros, a
variavel maiorMediaTurma e inicializada com o menor valor possvel para a media e a variavel
menorMediaTurma, ao contrario, e inicializada com o maior valor possvel. Essa inicilizacao
garante que os dados do primeiro aluno serao corretamente assimilados como o melhor e o pior
resultado encontrado. Essa condicao e correta no incio, pois os dados do primeiro aluno sao os
unicos passados ao sistema ate entao.
O comando da linha 49 imprime os dados do aluno, informando sua matrcula e a media
alcancada. Em seguida o usuario deve informar o codigo da matrcula do proximo aluno ou
encerrar o cadastro dos dados da turma em analise, digitando 1.
Caso tenha encerrado as digitacoes de uma turma, o programa executa o calculo do respectivo
aproveitamento (linha 54).
Feito isso, um comando de selecao verifica se o aproveitamento calculado e melhor que o
melhor ja encontrado. Em caso afirmativo, as variaveis melhorAproveitamento e melhorTurma
sao atualizadas para apontar para a turma recem analisada (linhas 56 e 57, respectivamente).
Terminando a sequencia de instrucoes do segundo comando de repeticao, os dados do melhor
e do pior aluno da turma sao exibidos (linhas 59 e 60). Por ultimo, e perguntado ao usuario
se ele deseja incluir os dados de uma outra turma, bastando, para isso, digitar seu codigo. Se
digitar a flag 1, o comando de repeticao e encerrado e o programa exibira, em sua ultima linha,
os dados da melhor turma (linha 64).
64 CAPITULO 2. CONCEITOS BASICOS
1 main () {
2
3 float a , b , c , delta , raizDelta , raiz1 , raiz2 ;
4
5 printf ( " Informe os valores das constantes a , b e c ( separados por
espacos ) : " ) ;
6 scanf ( " % f % f % f " , &a , &b , & c ) ;
7
8 delta = b * b - 4* a * c ;
9
10 if ( delta >= 0) {
11 if ( delta == 0) {
12 raiz1 = -b / (2* a ) ;
13 printf ( " As duas raizes sao iguais a : % f . " , raiz1 ) ;
14 } else {
15 raizDelta = sqrt ( delta ) ;
16 raiz1 = ( - b + raizDelta ) / (2 * a ) ;
17 raiz2 = ( - b - raizDelta ) / (2 * a ) ;
18 printf ( " As duas raizes s~ a o : % f e % f " , raiz1 , raiz2 ) ;
2.12. EXERCICIOS RESOLVIDOS 65
19 }
20 } else {
21 printf ( " Nao existem raizes reais desta equacao ! " ) ;
22 }
23 }
Exemplo 2.34: Programa para calcular as razes reais de uma equacao do segundo grau.
1 main () {
2 int n , termoAtual , penultimoTermo , antePenultimoTermo , i ;
3
4 printf ( " Informe o numero de termos a serem impressos : " ) ;
5 scanf ( " % d " , & n ) ;
6
7 if ( n >= 1) {
8 printf ( " 0 " ) ;
9 }
10 if ( n >= 2) {
11 printf ( " 1 " ) ;
12 }
13
14 antePenultimoTermo = 0;
15 penultimoTermo = 1;
16 termoAtual = 2;
17 for ( i =0; i < n -2; i = i +1) {
18 printf ( " % d " , termoAtual ) ;
19 antePenultimoTermo = penultimoTermo ;
20 penultimoTermo = termoAtual ;
21 termoAtual = penultimoTermo + antePenultimoTermo ;
22 }
23 }
Exemplo 2.35: Programa para calcular as razes reais de uma equacao do segundo grau.
1 main () {
2 int n1 , n2 , r ;
3
4 printf ( " \ nTABUADA de 1 a 9\ n \ n " ) ;
5 n1 =1;
6 n2 =1;
7 while ( n1 <10) {
8 while ( n2 <10) {
66 CAPITULO 2. CONCEITOS BASICOS
9 r = n1 * n2 ;
10 printf ( " % d * % d = % d \ n " ,n2 , n1 , r ) ;
11 n2 = n2 +1;
12 }
13 printf ( " \ n " ) ;
14 n2 =1;
15 n1 = n1 +1;
16 }
17 }
4. Calcular o Maximo Divisor Comum (MDC) de dois numeros. O MDC de dois numeros
pode ser obtido escolhendo o maior deles e subtraindo-lhe o valor do menor. Esta operacao
e repetida ate que os dois sejam iguais, cujo valor sera o MDC dos numeros iniciais:
33 15 45 18
18 15 27 18
03 15 09 18
03 12 09 09
03 09 MDC = 09
03 06
03 03
MDC = 03
1 main () {
2 int numero1 , numero2 , auxiliar1 , auxiliar2 ;
3
4 printf ( " Digite dois numeros para calcular seu MDC : " ) ;
5 scanf ( " % d % d " , & numero1 , & numero2 ) ;
6
7 auxiliar1 = numero1 ;
8 auxiliar2 = numero2 ;
9
10 while ( auxiliar1 != auxiliar2 ) {
11 if ( auxiliar1 > auxiliar2 ) {
12 auxiliar1 = auxiliar1 - auxiliar2 ;
13 } else {
14 auxiliar2 = auxiliar2 - auxiliar1 ;
15 }
16 }
17 printf ( " O MDC vale % d \ n " , auxiliar1 ) ;
18 }
5. Faca um programa que converta um valor em base binaria para base decimal.
1 main () {
2 int binario , aux1 , aux2 , aux3 , decimal ;
3
4 printf ( " Digite um valor em base binaria : " ) ;
5 scanf ( " % d " , & binario ) ;
6
7 aux1 = binario ;
8 aux2 = 1;
9 decimal = 0;
10 while ( aux1 > 0) {
11 aux3 = aux1 % 10;
12 decimal = decimal + aux3 * aux2 ;
13 aux2 = aux2 *2;
14 aux1 = aux1 /10;
15 }
16 printf ( " O valor % d em base binaria equivale a % d em base decimal \ n " ,
binario , decimal ) ;
17 }
2.13 Resumo
O computador guarda as informacoes dos programas na memoria. A memoria pode ser
entendida como uma sequencia de celulas, cada uma identificada por um numero conhecido
como endereco de memoria. Nas linguagens de programacao, as variaveis permitem o
acesso as celulas de memoria sem a necessidade de manipular seus enderecos diretamente.
Na linguagem C, para a declaracao de uma variavel, o programador deve informar qual o
tipo de dados que ela ira manipular.
O comando de atribuicao permite atribuir uma determinada informacao (ou valor) a uma
variavel do programa.
Os identificadores sao nomes que identificam uma variavel ou constante. Eles sao formados
por caracteres alfanumericos, sendo que o primeiro deve ser obrigatoriamente alfabetico.
Na programacao, os quatro tipos de dados mais comuns sao: tipo inteiro, tipo ponto
flutuante, tipo booleano e tipo caractere.
Variaveis e constantes podem ser combinadas com operadores para formarem expressoes.
No comando de atribuicao, o valor da expressao (termos a direita do comando) e atribudo
a variavel (a esquerda do comando).
Os principais tipos de expressoes sao aritmetica, relacional e logica.
68 CAPITULO 2. CONCEITOS BASICOS
6. Fazer um programa que calcule e escreva uma tabela de graus centgrados em funcao de
graus farenheit que variam de 50 a 150 de 1 em 1.
7. Faca um programa para ler uma sequencia de n numeros inteiros positivos (um por vez),
e verificar se eles sao pares ou mpares.
2.14. EXERCICIOS PROPOSTOS 69
8. Suponha que a populacao de um pas A seja de 9.000 habitantes com uma taxa anual de
crescimento de 3% e que a populacao de um pas B seja, aproximadamente, de 20.000 de
habitantes com uma taxa anual de crescimento de 1,5%, fazer um programa que calcule
e escreva o numero de anos necessarios para que a populacao de pas A ultrapasse ou se
iguale a populacao do pas B, mantidas estas taxas de crescimento.
9. Uma certa firma fez uma pesquisa de mercado para saber se as pessoas gostaram ou nao de
um novo produto lancado no mercado. Para isso, obteve, para cada pessoa entrevistada,
informacoes a respeito do sexo do entrevistado e sua resposta (S = Sim ou N = Nao).
Sabe-se que foram entrevistados 2000 pessoas, fazer um programa que calcule e escreva:
10. A fabrica de chocolates MENINO esta com problemas financeiros e pretende fazer um
corte na folha de pagamento. Para isso, o setor de financas adotou o seguinte criterio para
reduzir a despesa com pessoal:
funcionarios com tempo de servico menor que 2 anos (24 meses) serao demitidos;
funcionarios com tempo de servico superior (ou igual) a 2 anos (24 meses) e menor
que 10 anos (120 meses) terao seus salarios reduzidos em 10%;
funcionarios com tempo de servico superior (ou igual) a 10 anos (120 meses) nao serao
demitidos e nem terao seus salarios reduzidos. Eles poderao optar por um plano de
demissao voluntaria com a seguinte proposta de indenizacao: 2 salarios atuais para
cada ano de servico.
Modularizacao
Co-autor:
Gilberto Segundo
Objetivos:
Os programas sao escritos a fim de resolverem varios tipos de problemas. Alguns desses
problemas exigem mais tempo e maiores esforcos do programador. Por isso, e indispensavel que
o programador utilize de tecnicas que visam uma maior organizacao e consequente entendimento
facilitado do programa. Uma das tecnicas mais utilizadas e a modularizacao.
Este captulo trata sobre o conceito e a pratica de programas modularizados.
3.1 Introducao
Quando se trabalha em qualquer problema complexo, seja em programacao, seja em outra area,
o ser humano sente a necessidade de dividir esse problema maior em problemas menores. Cada
problema menor e entao trabalhado de forma a solucionar as questoes que lhe cabem e, juntando-
se cada uma dessas solucoes, tem-se a solucao do problema maior. Esse processo e chamado de
modularizacao e baseia-se na conhecida tecnica de dividir para conquistar.
Pode-se aplicar a modularizacao em diversas areas na vida cotidiana. Para a fabricacao de
um automovel, por exemplo, sao necessarios diversos servicos: funilaria, montagem, estofamento,
pintura, soldagem, etc. A princpio, poderia existir apenas um robo que fizesse todas as etapas
da fabricacao do carro, mas isso acarretaria em alguns problemas, tais como: complexidade
70
3.2. SUBPROGRAMAS 71
do robo, que deveria fazer todos os servicos de forma completa e eficiente; alto prejuzo em
eventuais servicos de manutencao, que teria que parar todo o processo de montagem; falta de
clareza no entendimento do processo de fabricacao, o que prejudicaria a expansao do processo
de montagem; entre outras.
Uma otima alternativa para esse problema e fazer varios robos, cada um com sua funcao
especfica. O que se estaria fazendo na verdade e uma modularizacao.
Pode-se facilmente mover as ideias contidas no problema apresentado para a elaboracao de
um programa. O programa pode ser dividido em varias partes, sendo que cada parte trata de
uma determinada funcionalidade.
Dessa forma, o programa tem a sua legibilidade melhorada, uma vez que fica mais facil
entender o que o programa faz por completo ou como o programa faz determinada subtarefa. A
modificabilidade do programa tambem e melhorada, uma vez que para se alterar determinada
funcionalidade, e necessario apenas alterar um pequena parte do codigo, nao sendo necessario
modificar varios pontos do codigo.
Como as funcionalidades do programa estando separadas logicamente, o programador tem a
oportunidade de reutilizar uma parte do programa para escrever um outro programa que utilize,
em uma de suas tarefas, o mesmo bloco de instrucoes. A confiabilidade do programa tambem e
aumentada pelo fato dele ser mais facil de ser entendido e corrigido.
Por fim, a produtividade para a elaboracao de um programa tambem e aumentada, uma vez
que cada parte do programa pode ser trabalhada por uma equipe diferente, alem de que cada
equipe so precisa saber o que as partes da outra equipe fazem e nao como fazem.
Nas secoes seguintes serao mostradas tecnicas de como fazer um programa modularizado
utilizando a linguagem C.
3.2 Subprogramas
Um subprograma e um trecho de um programa que realiza qualquer operacao computacional.
Ele efetua parte de uma tarefa que um algoritmo maior deveria executar, ou seja, ele e uma
parte do programa, especializado em alguma funcionalidade. Na linguagem C, o uso de funcoes
e a principal forma de modularizacao.
Uma funcao matematica f (x) e uma relacao que mapeia um dado valor x de um domnio em
um unico valor y de um conjunto imagem. Em programacao, a ideia e semelhante: uma funcao
e um conjunto de instrucoes que recebe alguns valores como dados de entrada e, a partir deles,
produz um valor como sada. A Figura 3.1 ilustra o funcionamento de uma funcao matematica.
3.3.1 Cabecalho
O cabecalho do subprograma e onde estao definidos o nome do subprograma, os tipos dos seus
parametros de entrada e o tipo de dado de retorno.
Os parametros da funcao sao os ingredientes que ela precisa para executar as suas instrucoes
e seus tipos devem ser fielmente respeitados. Ja o tipo de retorno da funcao simboliza o tipo de
produto gerado por ela. Se a funcao diz retornar um numero float, quer dizer que o programador
deve esperar receber apenas esse tipo de dado e tomar as medidas necessarias para manipula-lo
posteriormente.
O nome do subprograma serve para identifica-lo. O programador deve escolher nomes auto-
explicativos sobre a funcionalidade da funcao, ou seja, para que ela serve. Isso torna o codigo
mais legvel e menos susceptvel a erros por parte do programador.
3.3. PARTES DE UM SUBPROGRAMA 73
No Exemplo 3.1, a funcao calculaMedia possui dois parametros de entrada do tipo float
e retorna um valor tambem do tipo float. O primeiro parametro recebe o nome a e o segundo
recebe o nome b. A funcao retorna um dado do tipo float, ou seja, retornara um numero que
contem casas decimais. Mais detalhes sobre passagem de parametros, assim como o retorno de
funcoes serao mostrados posteriormente.
Percebe-se que o nome da funcao ja nos da a ideia do que que ela faz: calcula a media de dois
numeros. Porem, maiores explicacoes sobre essa media, se e aritmetica, geometrica ou outra,
devem ser explicadas nos comentarios da funcao.
Quando a quantidade de parametros de uma funcao nao e respeitada, a funcao nao tem todos
os dados necessarios para a realizacao de suas instrucoes. Na funcao do Exemplo 3.1, quando
o programador chama a funcao calculaMedia passando apenas um numero, ao inves de dois,
o compilador avisa o programador sobre tal erro, nao deixando que o programa seja gerado.
Assim, evita-se erros de execucao.
Tambem podem ocorrer erros quando a ordem ou tipo dos parametros nao sao respeitadas.
No cotidiano, tambem ocorrem esses tipos de erros. Por exemplo, para que um carro funcione,
ele precisa, entre outras coisas, de agua e oleo lubrificante. Caso a agua seja colocada no lugar do
oleo lubrificante, ou o contrario, e previsvel que o carro, em algum momento, apresente falhas
na execucao de suas operacoes, nao se locomovendo. Assim, fica facil perceber que um bom
programador deve sempre verificar se esta respeitando os tipos dos parametros das funcoes.
variaveis: quantidade de tinta e cor da tinta. Essa variaveis nao sao visveis para os outros
robos; em particular, para o robo de solda.
3.3.3 Corpo
O corpo do subprograma e onde estao contidas as instrucoes a serem executadas pelo subpro-
grama. Nele, podem ser realizadas operacoes, tais como: atribuicoes de valores as variaveis,
chamadas de outras funcoes, calculos de expressoes, leitura de dados e apresentacao de resulta-
dos.
O Pseudocodigo 3.1 descreve uma funcao que calcula a distancia entre dois pontos no plano
cartesiano.
3.3.4 Comentarios
Para melhor legibilidade e entendimento do codigo do subprograma, e recomendavel fazer co-
mentarios.
Acima de cada subprograma pode-se colocar comentarios com as seguintes finalidades: dizer
para que essa funcao serve, quais os parametros que ela recebe, explicitando como devem ser
esses parametros (unidade de medida, relevancia para a funcao, etc), restricoes para aplicacoes,
entre outras.
No dicionario de dados, os comentarios podem ser usados para explicar o significado de al-
guma variavel cujo nome nao e suficientemente significativo. No corpo da funcao, os comentarios
podem ser usados para explicar o que foi feito em determinado conjunto de instrucoes cujo en-
tendimento nao e facil ou que nao tem utilidade aparente.
Na linguagem C, os comentarios dos subprogramas devem ser feitos da mesma maneira usada
para comentar o programa, entre /* e */ ou apos //, como explicado no Captulo 1.
O Exemplo 3.3 mostra um comentario geral da funcao distancia, explicando os parametros
da funcao e como e feito o calculo.
1 /*
2 Func~
a o para calcular a dist^
a ncia entre dois pontos no plano cartesiano .
3 Dados de entrada :
4 float x1 : a coordenada x do primeiro ponto .
5 float y1 : a coordenada y do primeiro ponto .
6 float x2 : a coordenada x do segundo ponto .
7 float y2 : a coordenada y do segundo ponto .
8
9 Dados de sada :
10 dist : retorna a dist^
a ncia entre os pontos passados
11 */
12
13 float distancia ( float x1 , float y1 , float x2 , float y2 ) {
14
76 CAPITULO 3. MODULARIZACAO
15 float dx , dy , dist ;
16
17 dx = x2 - x1 ;
18
19 dy = y2 - y1 ;
20
21 dist = sqrt ( dx * dx + dy * dy ) ;
22
23 return dist ;
24 }
7
8 dx = x2 - x1 ;
9
10 dy = y2 - y1 ;
11
12 dist = sqrt ( dx * dx + dy * dy ) ;
13
14 return dist ;
15 }
16
17
18 main () {
19 float xa , xb , ya , yb , dist ;
20
21 printf ( " Forneca as coordenadas x e y ( em quil^
o metros ) das cidades A e B ,
respectivamente : " ) ;
22
23 scanf ( " % f % f % f % f " , & xa ,& ya ,& xb ,& yb ) ;
24
25 dist = distancia ( xa , ya , xb , yb ) ;
26
27 printf ( " Dist^
a ncia em quil^
o metros entre as cidades A e B : % f \ n " , dist ) ;
28
29 printf ( " Forneca as coordenadas x e y ( em metros ) da cadeira e da mesa do seu
escritorio : " ) ;
30 scanf ( " % f % f % f % f " , & xa ,& ya ,& xb ,& yb ) ;
31
32 dist = distancia ( xa , ya , xb , yb ) ;
33
34 printf ( " Dist^ a ncia em metros entre a cadeira e a mesa do seu escritorio : % f \ n
" , dist ) ;
35 }
Quando o programa principal chama o subprograma distancia, os valores que estao contidos
nas variaveis xa, ya, xb e yb sao passados para as variaveis x1, y1, x2 e y2 do subprograma.
E importante perceber que, quando e feita uma passagem de parametros, apenas os valores
das variaveis sao passados para o subprograma chamado e nao as variaveis em si. Esse metodo
de passagem de parametro e chamado de passagem por copia, ou entao, passagem por valor.
A primeira implicacao dessa regra e a a nao necessidade de se usar variaveis como entrada
para outra funcao, podendo-se usar diretamente um valor. O Exemplo 3.5 ilustra essa ideia. No
programa principal e feita a chamada ao subprograma distancia passando-se os valores 10 ,
15 , 26 e 23 como parametros. Na funcao distancia, as variaveis x1, y1, x2 e y2 recebem
esses valores, para so entao a funcao iniciar suas instrucoes.
3.5. PASSAGEM DE PARAMETROS 79
Outra implicacao do fato de que apenas valores sao passados como parametro para uma
funcao e a impossibilidade de mudar o conteudo das variaveis que sao usadas como entrada
para a funcao. No Exemplo 3.6, a variavel a, da funcao dobraValor , recebe apenas o valor
da variavel x da funcao principal. Com isso, a variavel x permanece com o seu valor anterior a
chamada da funcao dobraValor , apesar da variavel a dessa funcao ter o seu valor alterado.
19 }
A sada da execucao do programa do Exemplo 3.6 e mostrada no Exemplo 3.7. Note que,
como esperado, a variavel x permaneceu com o seu valor original.
1 Numero original : 10
2 O seu dobro e : 20
3 O valor de x e : 10
Uma outra implicacao do metodo de passagem de valores e a nao visibilidade das variaveis
do programa principal no subprograma chamado, ou seja, o subprograma nao pode utilizar as
variaveis do programa principal ou de algum outro subprograma. No Exemplo 3.8, as variaveis
x e y nao podem ser acessadas dentro da funcao alteraValor . Com isso, o compilador emite
um erro quando o programa e compilado, dizendo que as variaveis x e y nao foram declaradas
anteriormente e a compilacao do programa e interrompida.
O que ocorre no Exemplo 3.8 tambem pode ser comparado com os robos da fabrica de
automoveis. O Robo de Solda nao pode usar a variavel local cor do automovel do Robo de
Pintura. O Robo de Solda nao sabe o que e essa variavel, nem que ela existe, assim, nao
pode usa-la.
Na linguagem C, deve-se declarar o tipo de dado retornado pela funcao em seu cabecalho
e usa-se o comando return para especificar a variavel ou valor que deve ser retornado. Apos a
sua execucao, a execucao da funcao corrente termina, mesmo que existam mais instrucoes apos
o return. Na implementacao da funcao distancia, feita no Exemplo 3.2, o valor contido na
variavel dist e retornado.
Como a main tambem e uma funcao, tambem temos que especificar o tipo de dado retornado
por ela. Por padrao, na linguagem C, a funcao main retorna um valor tipo int. Quando o
programador nao coloca nenhum valor para ser retornado pela main (como no Exemplo 3.9),
alguns compiladores fazem com que o valor 0 seja retornado por padrao. O valor 0 tambem e
usado para simbolizar que nada aconteceu de errado na execucao do programa.
Neste livro, optou-se por nao explicitar o valor retornado pela funcao main, ja que este valor
nao sera usado no programa. Contudo, vale lembrar que alguns compiladores exigem que o
retorno da main esteja escrito no codigo fonte, ficando a cargo do programador a percepcao da
obrigatoriedade de escreve-lo.
Considere um programa que calcula a media de um aluno e diz se ele foi aprovado ou nao.
O Pseudocodigo 3.2 mostra os passos a serem realizados pelo programa.
Uma possvel implementacao do Pseudocodigo 3.2 esta transcrita no Exemplo 3.9. Primei-
ramente, o programa pede que o usuario digite os valores das notas das duas provas realizadas
pelo aluno. Essas notas sao armazenadas nas variaveis nota1 e nota2 respectivamente e, logo
em seguida, sao passadas como parametros para a funcao calculaMedia. A funcao calcula-
Media recebe dois valores do tipo float e faz a media aritmetica deles, retornando, ao final da
82 CAPITULO 3. MODULARIZACAO
sua execucao, o valor dessa media. O valor retornado e armazenado na variavel resultado da
funcao principal, que por sua vez serve de condicao para a aprovacao do aluno. Caso o valor
armazenado em resultado seja maior ou igual a sete, o aluno esta aprovado; caso contrario, o
programa pede a nota da prova final do aluno, que e armazenada na variavel notaFinal. As
variaveis notaFinal e resultado sao entao usadas como parametros da funcao calculaMedia.
Caso o resultado retornado seja maior ou igual a cinco, o aluno esta aprovado.
Note que, na primeira chamada da funcao calculaMedia, o valor retornado foi armazenado
em uma variavel, pois precisa ser usado posteriormente. Ja na segunda chamada da funcao, o
3.6. RETORNO DE DADOS 83
valor retornado e usado diretamente na condicional de aprovacao ou nao do aluno, nao sendo
necessario seu armazenamento em alguma variavel. Mas atencao: caso fosse necessario usar esse
valor posteriormente, deveramos guarda-lo em alguma variavel, ao inves de chamar outra vez
a funcao calculaMedia usando os mesmos parametros. Essa chamada sendo feita novamente
acarretaria em perda de desempenho do programa, ja que a funcao teria que recalcular valores,
o que levaria um certo tempo para ser feito.
Em muitos casos e interessante, ou ate necessario, que o subprograma altere as variaveis
criadas no programa que o chamou. No exemplo da fabrica de automoveis e isso que deve
acontecer. A Figura 3.5 ilustra esse processo.
Nesse exemplo, o carro que e passado para o robo de solda esta com varias pecas soltas.
Entao, o robo solda essas pecas e passa o carro para o robo de pintura. Nesse caso, percebe-
se que deve-se passar o mesmo carro para o proximo robo e nao apenas um copia das suas
caractersticas, que simbolizam os valores das variaveis em programas. Entao, o carro recebe a
pintura e e passado como parametro para uma proxima funcao, ou seja, para um outro robo.
Como discutido na Secao 3.5, nao e possvel para um subprograma que recebe o valor de
uma variavel de um outro subprograma, alterar diretamente o valor da variavel do subprograma
que o chamou. Nesse caso, pode-se retornar o valor calculado e entao armazena-lo na variavel
desejada. A expressao geral e x = f (x).
Por exemplo, pode-se fazer uma variacao do uso da funcao dobraValor usada no Exemplo
3.6 para alterar o valor da variavel do programa que a chamou. Essa alteracao esta descrita no
Exemplo 3.10. Nesse exemplo, o valor retornado pela funcao dobraValor agora e armazenado
na variavel x.
5
6 a = 2* a ;
7
8 printf ( " O seu dobro e : % d \ n " , a ) ;
9
10 return a ;
11 }
12
13 main () {
14 int x = 10;
15
16 x = dobraValor ( x ) ;
17
18 printf ( " O valor de x e : % d \ n " , x ) ;
19 }
A sada da execucao do programa do Exemplo 3.10 e mostrada no Exemplo 3.11. Note que
a variavel x passou a ter o valor 20.
1 Numero original : 10
2 O seu dobro e : 20
3 O valor de x e : 20
Ainda no Exemplo 3.12, o encerramento antecipado de execucao foi feito para evitar um
erro na execucao do programa. Porem, o encerramento antecipado de execucao pode ser feito
naturalmente, sem o objetivo de evitar erros de execucao.
Considere o caso do Exemplo 3.9. O programa principal poderia ser na verdade um sub-
programa que retorna 1 caso o aluno esteja aprovado e 1 caso contrario. Se o aluno obtiver
media 7 apenas com as duas primeiras notas, a execucao do programa e interrompida e o valor
1 e retornado. Essa alteracao esta descrita no Exemplo 3.13. Note que nao e necessario o uso
do else na linha 20, pois caso a o resultado da media seja maior ou igual a sete, a funcao ira
interromper sua execucao, nao acarretando o calculo invalido da media considerando a nota da
prova final do aluno, que nao foi feita.
9 }
10
11 int aprovado ( float nota1 , float nota2 , float notaPF ) {
12 float resultado ;
13
14 resultado = calculaMedia ( nota1 , nota2 ) ;
15
16 if ( resultado >= 7) {
17 return 1;
18 }
19
20 if ( calculaMedia ( resultado , notaPF ) >= 5) {
21 return 1;
22 }
23 else {
24 return 0;
25 }
26 }
27
28 main () {
29 float nota1 , nota2 , notaPF ;
30 int passou ;
31
32 printf ( " Forneca as notas das duas provas do aluno e a nota da prova final .
Caso o aluno n~ a o tenha feito prova final , digite zero : " ) ;
33 scanf ( " % f % f % f " , & nota1 , & nota2 , & notaPF ) ;
34
35 passou = aprovado ( nota1 , nota2 , notaPF ) ;
36
37 if ( passou == 1) {
38 printf ( " O aluno passou de semestre .\ n " ) ;
39 }
40 else {
41 printf ( " O aluno n~a o obteve o rendimento mnimo para passar de semestre .
Esta reprovado .\ n " ) ;
42 }
43 }
Funcoes sem retorno de dados podem guardar os valores gerados em algum arquivo. Caso isso
seja feito, os dados podem ser recuperados lendo-se esses arquivos. A manipulacao de arquivos,
tanto para leitura, tanto para escrita, e discutida na captulo 8.
Ha tambem a opcao da funcao nao ter nenhum parametro de entrada e nenhum dado retor-
88 CAPITULO 3. MODULARIZACAO
nado. O Exemplo 3.16 mostra uma funcao para exibir uma saudacao ao usuario. Note que a
funcao nao possui parametros de entradas nem valor de retorno.
3.9 Recursividade
A recursividade ocorre quando algo e definido a partir de si mesmo. Uma recursao sempre e
definida a partir de uma ou mais relacoes recursivas (quando o proximo elemento e definido a
partir do anterior), e uma ou mais bases de recursao (pontos de parada).
Em programacao, o metodo recursivo pode ser usado para definir uma funcao. Por exemplo,
pode-se definir uma lista como sendo a uniao de um elemento com uma lista. Essa lista poderia
ser mais um elemento com mais uma lista ou apenas uma lista vazia. Pode-se simplificar essa
definicao por:
Por exemplo, o calculo do fatorial de um numero inteiro nao negativo pode ser implementado
atraves de uma funcao recursiva. Pode-se definir o fatorial de um numero n como sendo o proprio
numero n multiplicado pelo fatorial do numero n-1. A definicao de fatorial pode ser representada
por:
Nas definicoes recursivas anteriores sao usados criterios de parada a fim de que o processo
recursivo tivesse um limite. Imagine se uma lista fosse definida somente como um elemento
concatenado com uma lista. Por essa definicao nunca se teria uma lista finita, pois sempre novos
elementos teriam que ser adicionados. Para evitar isso usa-se um criterio de parada, chamado
de base da recursao. No exemplo da definicao da lista, a base da recursao e uma lista vazia. No
exemplo do fatorial de um numero, a base da recursao pode ser o numero 0, ou seja, e como se
dissesse: quando chegar ao numero 0 pare.
O Pseudocodigo 3.4 mostra os passos a serem seguidos para o calculo do fatorial de um
numero de forma recursiva.
3.9. RECURSIVIDADE 89
Em geral, toda funcao recursiva pode ser implementada sem recursao, atraves do uso de coman-
dos de repeticao. A vantagem da versao nao recursiva e a eficiencia. Ja a vantagem da recursiva
e a elegancia, legibilidade e redigibilidade.
O Exemplo 3.18 mostra uma forma nao recursiva da implementacao da funcao fatorial. O
comando for e utilizado para multiplicar o f por i a cada valor de i, ate que i assuma o valor de
n, passado como parametro.
Faca uma funcao que receba um numero inteiro positivo n, calcule a soma dos n primeiros
numeros naturais e retorne esse valor. Considere os numeros naturais comecando do 1. Nao use
a formula de P.A. (Progessao aritmetica).
Solucao Possvel:
Para produzir a soma dos n primeiros inteiros deve-se gerar a serie dos n primeiros valores
inteiros e acumula-los em uma variavel soma.
Na Figura 3.7 e mostrado um exemplo de solucao para n = 4. Note que a medida que os
valores de i sao gerados eles sao acumulados em soma. Fica claro que e necessaria a realizacao
de uma repeticao para produzir os valores de i e atualizar soma.
i soma
1 1
2 3
3 6
4 10
O Pseudocodigo 3.5 mostra o algoritmo que descreve os passos a serem realizados pela funcao.
O Exemplo 3.19 mostra a implementacao desse algoritmo. Note que o pseudocodigo foi
seguido fielmente, acrescentado-se apenas instrucoes necessarias e especficas da linguagem C.
92 CAPITULO 3. MODULARIZACAO
Faca um programa que leia um numero natural n e dois numeros naturais i e j diferentes de
0 e imprima em ordem crescente os n primeiros naturais que sao multiplos de i ou de j. Resolva
o problema utilizando uma funcao que verifique se um numero e multiplo do outro. Exemplo:
Para n = 6 , i = 2 e j = 3 a sada devera ser : 0,2,3,4,6,8.
Solucao Possvel:
O Pseudocodigo 3.6 mostra um algoritmo que descreve os passos a serem realizados pela
funcao.
3.10. EXERCICIOS RESOLVIDOS 93
Faca uma funcao que dados tres numeros naturais, verifique se eles formam os lados de um
triangulo retangulo. Os numeros dados simbolizam o comprimento de cada lado do triangulo.
Solucao Possvel:
Na geometria, o Triangulo Retangulo e um triangulo que possui um angulo reto e outros
dois angulos agudos. Sabe-se ainda que o quadrado da hipotenusa (o maior lado do retangulo)
e igual a soma dos quadrados dos catetos.
Sendo assim, para resolver esse exerccio, precisamos primeiro saber qual dos tres lados
dados, a, b ou c, corresponde a hipotenusa. Para tanto, compara-se todos os lados, dois a dois,
colocando-se o maior lado na variavel a.
Como o maior lado estara guardado em a e certo dizer que os lados dados correspondem a
um triangulo retangulo se, e somente se, a2 = b2 + c2 .
O Pseudocodigo 3.7 mostra um algoritmo que descreve os passos a serem realizados pela
funcao.
O Exemplo 3.21 mostra a implementacao desse algoritmo. Note que para trocar os valores
das variaveis a e b teve-se que ter uma variavel auxiliar, chamada aux, para guardar o valor de
a para so depois atribuir o valor da variavel b para a variavel a e posteriormente atribuir o valor
antigo da variavel a (que esta guardado na variavel aux) para a variavel b.
11 aux = a ;
12 a = c;
13 c = aux ;
14 }
15
16 if ( a * a == b * b + c * c ) {
17 return 1;
18 }
19 else {
20 return 0;
21 }
22 }
Faca um programa que dado dois numeros inteiros, m e n, gere uma tabuada conforme a
Figura 3.9, sendo que para esse exemplo os numeros fornecidos foram: 22 e 37.
Solucao Possvel:
Para gerar a tabuada proposta, e preciso perceber onde sao usados os numeros dados. Note
que a tabuada representa o resultado da multiplicacao de um numero x por um numero y. O
numero x assume valores desde 0 ate o segundo numero dado como argumento, enquanto o
numero y assume valores desde 1 ate o primeiro numero dado como argumento.
E facil perceber que e necessario o uso de comandos de repeticao para solucionar o problema.
Pode-se usar 2 comandos de repeticao, um contido no outro, diretamente ou indiretamente, via
uma funcao externa.
O Pseudocodigo 3.8 mostra um algoritmo que descreve os passos a serem realizados pela
funcao.
3.10. EXERCICIOS RESOLVIDOS 97
Func~a o Principal :
Coletar os valores das duas variaveis ( n e m )
Para um loop com x variando de 0 a m :
Chamar processo componente " mult " , passando como par^
a metro n e x
FIM - Func~
a o Principal
O Exemplo 3.22 mostra a implementacao desse algoritmo. Note que a variavel a da funcao
mult contem o valor da variavel n da funcao main, enquanto a variavel b da funcao mult
contem o valor da variavel x da funcao main.
Seja n inteiro positivo diferente de zero. Faca um programa para exibir os n primeiros
98 CAPITULO 3. MODULARIZACAO
numeros primos.
Solucao Possvel:
Para exibir os n primeiros numeros primos e necessario fazer primeiro uma funcao que verifi-
que se um dado numero x e primo ou nao. Por definicao: Numero primo e um numero natural
que pode ser dividido (o resto da sua divisao e zero) apenas por dois numeros naturais, o 1 (um)
e ele mesmo, com excessao do numero 1, que nao e tido como primo.
Sabe-se tambem que o unico numero que e primo e par ao mesmo tempo e o numero 2. Logo,
qualquer numero que seja par (resto da divisao por 2 igual a zero) que nao seja o dois, nao e
primo.
Entao, o que se tem a fazer para saber se determinado numero e primo e verificar se ele e o
numero dois ou se ele tem apenas 2 divisores. Mas, caso o numero em questao seja o numero
1, multiplo de 2 (com excessao do zero e do dois), ou tiver mais de 2 divisores, entao ele nao e
primo.
Todos os numeros inteiros positivos diferentes de zero tem ao menos 2 divisores: o 1 e ele
mesmo, com excessao do numero 1. Logo, se for encontrado mais algum divisor, o numero
estudado nao e primo. Entao, apos feitas as verificacoes anteriores, para encontrar algum outro
divisor do numero, basta ir dividindo-o por todos os numeros mpares maiores ou iguais a 3 e
menores ou iguais a raiz quadrada do numero em questao. Caso nao encontremos divisores nesse
intervalo, entao nao existem outros divisores alem do 1 e o proprio numero.
O Pseudocodigo 3.9 mostra um algoritmo que descreve os passos a serem realizados pela
funcao.
O Exemplo 3.23 mostra a implementacao desse algoritmo. Note que o valor retornado pela
funcao ehPrimo foi convencionado. Caso o numero passado seja primo, a funcao retornara o
valor um, caso contrario retornara o valor zero. O programador tem a liberdade de convencionar
o valor retornado, mas devera indicar isso no comentario do programa.
3.10. EXERCICIOS RESOLVIDOS 99
Func~a o Principal :
Coletar o valor da variavel quant que representa o numero de primos a serem
impressos
Iniciar numero com valor 1.
Para um loop com cont variando de zero a quant :
Usar a func~a o ehPrimo para verificar se cont e primo
Caso afirmativo ;
Imprimir cont
Incrementar cont
FIM - Func~
a o Principal
19 }
20
21 if ( x % 2 == 0) {
22 return 0;
23 }
24
25 for ( i = 3; i <= sqrt ( x ) ; i = i +2) {
26 if ( x % i == 0) {
27 return 0;
28 }
29 }
30
31 return 1;
32 }
33
34 int main () {
35
36 int quant , numero , cont ;
37
38 printf ( " Digite a quantidade de numeros primos a serem impressos : " ) ;
39
40 scanf ( " % d " , & quant ) ;
41
42 printf ( " Os numeros primos s~
ao : " ) ;
43
44 for ( numero = 1 , cont = 0; cont < quant ; numero ++) {
45 if ( ehPrimo ( numero ) == 1) {
46 printf ( " % d " , numero ) ;
47
48 cont ++;
49 }
50 }
51
52 printf ( " \ n " ) ;
53
54 return 0;
55 }
Um palndromo e uma palavra, frase ou qualquer outra sequencia de unidades (como uma
cadeia de ADN) que tenha a propriedade de poder ser lida tanto da direita para a esquerda
como da esquerda para a direita.
Sabendo-se disso, faca uma funcao que verifique se um numero inteiro e palndromo.
Exemplo:
101 e palndromo
1012 nao e palndromo
3.10. EXERCICIOS RESOLVIDOS 101
Solucao Possvel:
Para fazer essa funcao e necessario ler o numero de tras para frente. Para ler o ultimo
algarismo de um numero guardado numa variavel x basta guardamos numa variavel m o resto
da divisao inteira desse numero por 10. Apos isso, guarda-se em x a parte inteira da divisao de
x por 10, excluindo-se assim o ultimo algarismo do numero. Nesse momento a variavel m possui
o ultimo algarismo original de x, enquanto esta possui agora apenas os primeiros algarismos
originais, excluindo-se apenas o ultimo.
E facil perceber que basta fazer os passos acima recursivamente para obter em m o numero
x original, mas na ordem inversa. Tendo-se esse numero, basta compara-lo com o valor original,
guardado anteriormente em uma outra variavel.
A Figura 3.10 ilustra o comportamento das variaveis x e m com o decorrer das iteracoes
para um x inicial igual a 52325. A coluna x /10 mostra a parte inteira da divisao de x por 10,
enquanto a coluna x % 10 mostra o resto dessa divisao. Percebe-se que o processo iterativo deve
acabar quando o x final for igual a zero. Note que no final desse exemplo a variavel m contem
o numero inicial x do processo, mostrando que 52325 e palndromo.
O Pseudocodigo 3.10 mostra o algoritmo que descreve os passos a serem realizados pela
funcao.
102 CAPITULO 3. MODULARIZACAO
O Exemplo 3.24 mostra a implementacao desse algoritmo. Note que a variavel c guarda o
valor inicial de x e que esta variavel e comparada com o valor de m ao final do processo. Aqui
tambem usou-se a convencao de usar o valor de retorno 1 para indicar sucesso na verificacao e
zero caso contrario.
Faca uma funcao que calcule o maximo divisor comum entre dois numeros naturais passados
como parametros da funcao. Faca a funcao na forma recursiva e na forma iterativa.
Solucao Possvel:
3.10. EXERCICIOS RESOLVIDOS 103
O Pseudocodigo 3.12 mostra o algoritmo que descreve os passos a serem realizados pela
funcao iterativa.
O Exemplo 3.26 mostra a implementacao desse algoritmo. Note que nesse caso a imple-
mentacao na forma iterativa e obtida facilmente a partir da forma recursiva.
3.11 Resumo
A modularizacao baseia-se na conhecida tecnica de dividir para conquistare pode ser
definida como a divisao de um problema em varias partes. Cada uma dessas partes pode
ser desenvolvida independentemente das outras.
As partes principais de um subprograma sao: cabecalho, dicionario de dados, corpo e
comentarios.
Os parametros de um subprograma sao os dados iniciais necessarios para a funcao poder
realizar o seu trabalho. O subprograma pode tambem nao receber nenhum parametro para
realizar suas funcoes. Na linguagem C, quando a funcao nao tem parametros usa-se void
na declaracao da funcao, em sua lista de parametros.
3.12. EXERCICIOS PROPOSTOS 105
Um subprograma podera produzir no decorrer das suas instrucoes um valor que sera retor-
nado, chamado de valor de retorno. Na linguagem C, quando a funcao nao produz nenhum
valor de retorno usa-se void para simbolizar o tipo de dado retornado.
Geralmente e possvel criar versoes recursivas e nao recursivas de uma mesma funcao.
Produtividade: o programa pode ser dividido em modulos e cada modulo pode ser traba-
lhado por uma equipe diferente. Alem disso, uma equipe so precisa saber o que determi-
nado modulo de outra equipe faz e nao como faz;
a b
567890 890 =>encaixa
1243 1243 =>encaixa
2457 245 =>nao encaixa
457 2457 =>nao encaixa
(b) Usando a funcao do item anterior, faca um programa que le dois inteiros positivos a e
b e verifica se o menor deles e segmento do outro.
Exemplo:
a b
567890 678 => b e segmento de a
1243 2212435 => a e segmento de b
235 236 => um nao e segmento do outro
106 CAPITULO 3. MODULARIZACAO
2. Faca uma funcao que receba como argumento os lados de um triangulo e calcule o seu
permetro e em seguida o valor da area.
4. Faca uma funcao que receba como parametro uma quantia em reais (valor inteiro). Calcule
o numero de cedulas de cada tipo (1, 2, 5, 10, 20, 50, 100), necessario para pagar a quantia.
Exiba apenas o numero nao nulo de cedulas de cada tipo.
5. Faca uma funcao que receba como entrada dois valores inteiros, a e b, e exiba a sequencia
de numeros pares do intervalo (a, b), com a < b.
7. Faca uma funcao que calcule e retorne o n-esimo termo de uma PA (progressao aritmetica)
sendo fornecidos como entrada o numero de termos, o primeiro termo e a razao.
8. Elabore uma funcao em C que calcule o valor aproximado de com precisao de cinco
decimos atraves da serie:
1 1 1
= 13
33
+ 53
. . .
Considere que a precisao de cinco decimos requer que a soma dos elementos da serie so
deve ser interrompida quando o valor do termo e inferior a 0,00001. Para implementar a
funcao, use uma subfuncao que, dados x e y, calcule xy .
3.13. TRABALHOS SUGERIDOS 107
+2x4+0x3-25x2+1x1-5x0=
ou seja, a leitura devera ser pelo teclado e sempre na mesma ordem: <operador> <coeficiente>
<variavel> <potencia>. Observacao: a leitura do teclado deve ser feita termo a termo,
por exemplo, +2x4 [enter] +0x3 [enter]. Apenas o caractere = podera ser lido sepa-
radamente.
Uma funcao f (x) = 2x4 25x2 + x 5 seria digitada da seguinte maneira: (Opcao 2)
+2x4-25x2+1x-5=
Ou seja, a leitura devera ser pelo teclado, porem com algumas diferencas em relacao a
opcao anterior. Serao omitidos a potencia 1, a variavel com potencia 0 e os termos
com coeficiente 0. Observacao: nesse caso a leitura tambem deve ser feita termo a termo,
porem, deve-se lembrar que alguns termos podem ser menores que outros (+2x4 e 1x,
por exemplo).
Em ambos os casos, a flag de final de leitura sera o caracter = e os coeficientes serao
numero reais (float). A ideia e que, se o programador optar pela segunda opcao, o algoritmo
devera ser generico ao ponto de receber as duas formas de entrada.
Armazenada a funcao de entrada, o programa devera imprimir, no mesmo formato de
entrada, a derivada e a integral da funcao. Como se nao bastasse a folga do monitor, ele
ainda deseja que o programa imprima tambem as razes das funcoes quando estas forem
do primeiro ou do segundo grau.
Exemplos de Sada
Usando a funcao de entrada: f (x) = x2 5x + 6
Objetivos:
Este captulo busca esclarecer como a criacao e manipulacao de novos tipos de dados permite
facilitar o entendimento, a confeccao e o reaproveitamento de codigo.
109
110 CAPITULO 4. TIPOS ABSTRATOS DE DADOS
As formas de representacao de dados fornecidas pelo computador sao insuficientes para que o
programador possa representar em sua totalidade as possveis colecoes de dados existentes. Pro-
gramadores ha muito tempo reconhecem o valor de se organizar itens de dados correlacionados
em uma unica entidade computacional. As Estruturas sao entidades que representam tipos de
dados e que permitem o agrupamento de varias variaveis de diversos tipos.
Suponha um programa que faca o cadastro de estudantes de uma universidade, cada um
possuindo um conjunto de atributos correlacionados, como por exemplo: matrcula, idade, co-
eficiente de rendimento e perodo. Utilizando as entidades de programacao ja conhecidas, no
cadastro de um unico aluno seriam necessarias quatro variaveis para representar todos os seus
atributos; para dois estudantes, seriam necessarias oito variaveis; ja para diversos estudantes
seria necessario um conjunto de variaveis consideravelmente grande.
A utilizacao de muitas variaveis aumenta o trabalho do programador e dificulta que outra
pessoa entenda a aplicacao. Para solucionar esse problema, muitas linguagens de programacao,
entre elas a linguagem C, possibilitam ao programador criar tipos compostos heterogeneos. Na
linguagem C, os tipos compostos heterogeneos sao chamados struct (estruturas). As estruturas
sao conjuntos de dados correlacionados, que podem ser representados em sua totalidade por uma
unica variavel. No exemplo mencionado, cada estudante poderia ser representado por uma unica
variavel, dessa forma, seria necessario adicionar apenas uma variavel para cada novo estudante
a ser cadastrado.
Dada a impossibilidade de se antecipar todos os tipos de dados utilizados por um programa,
uma linguagem de programacao apenas implementa tipos de dados simples. Desse modo, fica a
cargo do programador usar desses dados para a definicao de novos tipos de dados.
Alem de facilitar e tornar mais clara a implementacao, os tipos compostos heterogeneos tem
como finalidades principais a possibilidade de retorno de mais de um valor por funcao e a criacao
de tipos abstratos de dados. Essas caractersticas sao discutidas mais adiante neste captulo.
4.2. TIPOS COMPOSTOS HETEROGENEOS (ESTRUTURAS) 111
4.2.1 Definicao
Uma estrutura e uma colecao de variaveis, que podem ou nao ser de tipos diferentes, colocadas
sob um unico nome para manipula-las. As estruturas ajudam na organizacao do codigo e fa-
cilitam a vida do programador, pois juntam variaveis relacionadas e permitem que elas sejam
tratadas como uma unidade maior.
Na pratica, uma estrutura e uma caixa onde podem ser agrupados diversos dados correla-
cionados. Essa caixa, na verdade, e o conjunto de alguns bytes de memoria correspondente ao
somatorio dos tamanhos dos dados que se pretende agrupar.
A Figura 4.1 ilustra uma estrutura que representa um estudante, tEstudante. Note que a
estrutura tEstudante ocupa um espaco equivalente ao espaco necessario para guardar todos os
seus atributos.
Sintaxe
Para criar uma estrutura com n atributos, deve-se obdecer a sintaxe exposta abaixo.
A palavra struct indica que a entidade criada e uma estrutura e as chaves delimitam o trecho
onde os atributos da estrutura sao definidos. Cada atributo e definido por seu tipo seguido de
um identificador.
O Exemplo 4.1 apresenta a sintaxe de uma estrutura para o caso do estudante.
1 struct tEstudante {
2 int idade ;
3 int matricula ;
112 CAPITULO 4. TIPOS ABSTRATOS DE DADOS
4 float coeficiente ;
5 int periodo ;
6 };
4.2.2 Uso
Como as estruturas sao conjuntos de dados, existem duas formas basicas de manipula-las, se-
lecionando um unico elemento de dado de forma seletiva, ou manipulando toda a estrutura de
forma integral.
Seletivo
Para manipular cada um dos atributos da estrutura se utiliza o mecanismo de selecao conforme
a sintaxe apresentada a seguir.
O Exemplo 4.2 ilustra o uso seletivo da estrutura estudante. Nesse Exemplo, a leitura de
dados e feita atribuindo-se cada valor lido a um atributo da estrutura.
Integral
Quando se deseja manipular a estrutura como um todo, se utiliza simplesmente o nome da
variavel onde a mesma esta armazenada. O uso integral em atribuicoes e descrito pela sintaxe
a seguir.
4.2. TIPOS COMPOSTOS HETEROGENEOS (ESTRUTURAS) 113
No Exemplo 4.3, ao final da execucao do programa os valores dos atributos de outro sao
iguais aos valores dos atributos de aluno.
O comando typedef pode ser utilizado para renomear tipos simples ou compostos. Para
utilizar o comando typedef, deve-se obdecer a sintaxe exposta abaixo.
O Exemplo 4.5 apresenta a adaptacao do Exemplo 4.3 para a utilizacao do comando typedef,
permitindo que o tipo composto heterogeneo struct tEstudante seja utilizado atraves da palavra
struct tEstudante.
6 int matricula ;
7 float coeficiente ;
8 int periodo ;
9 };
10
11 typedef struct tEstudante tEstudante ;
12
13 main () {
14 tEstudante aluno , outro ;
15 printf ( " Digite os dados do estudante " ) ;
16 scanf ( " % d % d % f % d " ,& aluno . idade ,& aluno . matricula ,& aluno . coeficiente ,& aluno .
periodo ) ;
17 outro = aluno ;
18 }
1 void funcao1 ( int idade , int matricula , float coeficiente , int periodo ) { ... }
2
3 void funcao2 ( tEstudante aluno ) { ... }
A mesma funcao pode ser escrita dos dois modos apresentados no Exemplo 4.6 na funcao1
quatro variaveis relacionadas ao estudante sao passadas como parametro; na funcao2 , o proprio
estudante e passado como parametro. A funcao2 e mais compreensvel que a funcao1 , pois a
pessoa que ler o codigo da funcao1 tera que inferir que aquelas quatro variaveis referem-se a
um mesmo estudante.
Da mesma forma, o retorno de funcoes pode ser simplificado pelo uso de estruturas. Suponha
que se deseje fazer a leitura de dados de um estudante. O Exemplo 4.7 mostra duas maneiras de
fazer isso: utilizando funcoes que retornam estruturas ou funcoes que retornam tipos simples.
8 int periodo ;
9 };
10
11 typedef struct tEstudante tEstudante ;
12
13 int lePeriodo () {
14 int periodo ;
15 scanf ( " % d " ,& periodo ) ;
16 return periodo ;
17 }
18
19 int leMatricula () {
20 int matricula ;
21 scanf ( " % d " ,& matricula ) ;
22 return matricula ;
23 }
24
25 float leCoeficiente () {
26 float coeficiente ;
27 scanf ( " % f " ,& coeficiente ) ;
28 return coeficiente ;
29 }
30
31 int leIdade () {
32 int idade ;
33 scanf ( " % d " ,& idade ) ;
34 return idade ;
35 }
36
37 tEstudante leEstudante () {
38 tEstudante aluno ;
39 scanf ( " % d % d % f % d " ,& aluno . idade ,& aluno . matricula ,& aluno . coeficiente ,& aluno .
periodo ) ;
40 return aluno ;
41 }
42
43 main () {
44 tEstudante aluno1 , aluno2 ;
45 aluno1 . periodo = lePeriodo () ;
46 aluno1 . matricula = leMatricula () ;
47 aluno1 . coeficiente = leCoeficiente () ;
48 aluno1 . idade = leIdade () ;
49 aluno2 = leEstudante () ;
50 }
No Exemplo 4.7 e realizada a leitura de dados de aluno1 sem utilizar o retorno de estruturas,
e de aluno2, utilizando-o. Para realizar a leitura de aluno1 foram necessarias a declaracao de
quatro funcoes diferentes, uma para cada atributo, e quatro chamadas de funcao no programa
principal. Para realizar a leitura de aluno2 foi necessario apenas a declaracao de uma funcao
116 CAPITULO 4. TIPOS ABSTRATOS DE DADOS
e uma chamada de funcao no programa principal. Assim, pode-se perceber que o retorno de
estruturas torna o codigo mais compacto, legivel e eficiente, por ter menos chamadas de funcao.
4.3.1 Definicao
Um tipo abstrato de dados e um tipo de dado definido em termos do seu comportamento e
nao em termos de sua representacao. A ideia de tipo abstrato de dados e desvincular o tipo de
dado (valores e operacoes) de sua implementacao, ou seja, quando definimos um tipo abstrato
de dados estamos preocupados com o que ele faz e nao como ele faz.
Os programas que usam um determinado tipo abstrato de dados sao chamados clientes; e
o programa que define sua estrutura e comportamento e conhecido como implementacao. Um
TAD pode ser definido como a composicao de uma estrutura de dados e das operacoes definidas
sobre estes dados.
inicializaData: funcao que inicializa uma data a partir de valores passados como parametro.
leData: funcao que inicializa uma data a partir de valores lidos do teclado.
alteraData: funcao que altera uma data a partir de valores passados como parametro.
O codigo do Exemplo 4.8 ilustra a definicao de um Tipo Composto com Operacoes Pre-
definidas. O tipo tData contem os atributos de uma data. Uma variavel desse tipo e inicializada
atraves da funcao inicializarValores, essa funcao permite inicializacao dos atributos sem que
estes sejam acessados. A funcao de leitura leData obtem uma data do teclado e retorna uma
variavel do tipo tData contendo os valores lidos. A funcao alteraData apresenta o mesmo
codigo que a funcao de inicializacao, contudo sua utilizacao destina-se a datas ja inicializadas.
A funcao eBissexto verifica se uma dada data esta em um ano bissexto, o algoritmo considera a
seguinte regra: Sao bissextos todos os anos multiplos de 400, nao sao bissextos todos os multiplos
de 100 e nao de 400, sao bissextos todos os multiplos de 4 e nao multiplos de 100, por fim, nao
sao bissextos todos os demais anos. A funcao diasNoMes determina o numero de dias de um
do mes de uma determinada data. Os meses de abril, junho, setembro e novembro possuem 30
dias. Caso a data seja do mes de fevereiro, verifica-se se o ano e bissexto, se for bissexto o mes
4.3. TIPOS ABSTRATOS DE DADOS 119
tem 29 dias, se nao for bissexto o mes tem 28 dias. Caso a data nao esteja em nenhum dos
meses citados, o mes apresenta 31 dias.
Observe que, no Exemplo 4.9, a intencao do programador foi adicionar um dia a data lida,
entretanto, este nao utilizou a funcao do tipo diaSeguinte. Para grande parte dos valores de
data, o programa funcionaria corretamente, entretanto, para datas que sao o ultimo dia do mes,
esse programa gera inconsistencia, pois a data obtida nao sera uma data valida. No decorrer da
aplicacao, um valor invalido de data sera repassado para outras partes do programa. Assim essa
inconsistencia pode ter um efeito colateral que acarrete em problemas no codigo. Sendo assim,
e altamente recomendavel que se utilizem apenas as operacoes ja definidas para manipular as
estruturas do TAD e que nao ocorra a manipulacao de atributos.
Integridade: A manipulacao dos atribuitos por operacoes definidas sobre o tipo impedem
a ocorrencia de inconsistencias.
Na pratica, essas vantagens tornam o codigo mais facil de se escrever, mais compreensvel
para quem le e mais facil de se modificar.
Construtoras
Operacoes construtoras sao aquelas que inicializam variaveis, logo devem ser utilizadas antes de
qualquer outra para garantir que o TAD foi inicializado corretamente. Observe no Exemplo 4.10
diferentes implementacoes de funcoes construtoras para o TAD estudante.
1 tEstudante inicializar () {
2 tEstudante novo ;
4.3. TIPOS ABSTRATOS DE DADOS 121
3
4 novo . idade =0;
5 novo . matricula =0;
6 novo . coeficiente =0;
7 novo . periodo =0;
8 return novo ;
9 }
10
11 tEstudante inicializarValores ( int idade , int matricula , float coeficiente , int
periodo ) {
12 tEstudante novo ;
13
14 novo . idade = idade ;
15 novo . matricula = matricula ;
16 novo . coeficiente = coeficiente ;
17 novo . periodo = periodo ;
18 return novo ;
19 }
A funcao inicializar inicia com zero todos os atributos de uma variavel do tipo tEstudante,
esta variavel e retornada pela funcao de inicializacao. A funcao inicializarValores exerce o
mesmo papel de inicializar que a funcao inicializar . Contudo, esta recebe os valores a serem
incializados como parametros da funcao.
Analisadoras
Modificadoras
As operacoes modificadoras, ou atualizadoras, permitem alteracoes de atributos do TAD. O
Exemplo 4.12 mostra uma funcao modificadora para o TAD tEstudante.
Produtoras
As operacoes produtoras sao aquelas que, a partir dos dados de um TAD, produzem uma nova
informacao. A funcao maiorIdade, do Exemplo 4.13, produz um resultado (a maior idade) a
partir de dois estudantes dados.
Destrutoras
As operacoes destrutoras sao utilizadas para liberar recursos de memoria quando o TAD nao e
mais necessario. Exemplos e explicacoes sobre essa funcao sao dados no Captulo 7.
9
10 data = leData () ;
11 anoBissexto = eBissexto ( data ) ;
12
13 if ( anoBissexto == 1) {
14 printf ( " Ano Bissexto " ) ;
15 } else {
16 printf ( " Ano n~
a o Bissexto " ) ;
17 }
18
19 nDias = diasNoMes ( data ) ;
20 printf ( " Numero de dias no m^
e s : " , nDias ) ;
21 }
O Exemplo 4.14 determina se uma data digitada pelo teclado e de um ano bissexto e o
numero de dias no mes da data digitada. Pode-se afirmar que o programa usuario manipula o
TAD somente atraves das operacoes pre-definidas pelo implementador, assim o codigo usuario
fica mais legvel. Pode-se observar que as operacoes separam o codigo usuario do codigo de
implementacao do TAD. A redigibilidade tambem aumenta, visto que o acesso aos dados e
realizado apenas por simples operacoes. O codigo tambem fica mais confiavel, pois o usuario
nao altera livremente os dados, isso so pode ser feito atraves das operacoes do TAD. E, por fim,
caso a implementacao do TAD precise ser alterada, o codigo usuario nao sofrera mudancas, a
menos que os cabecalhos das funcoes sejam alterados.
TADs de Domnio
Sao aqueles que definem um tipo de dados que esta no domnio do problema. Como por exemplo,
os TADs tEstudante e tData.
TADs Implementacionais
Sao de objetos de programacao que nao tem relacao direta com o problema, mas sim com sua
implementacao. Podemos tomar como exemplos as listas, as arvores, os grafos e as filas. Alguns
desses conceitos sao apresentados nos proximos captulos.
Faca um programa que imprima de uma estrutura do tipo data, a data de nascimento
30/01/1984.
Solucao Possvel:
Nao existem dados de entrada, a data de nascimento a ser informada e armazenada direta-
mente em uma estrutura data. A impressao da data e realizada no formato abreviado utilizando
o comando printf . A sada do programa e a impressao da data 30/01/1984.
Defina, agora, a estrutura do exemplo anterior como um tipo e realize operacoes (sobre este
tipo) de edicao e consulta.
Solucao Possvel:
Este programa utiliza operacoes de alteracao e consulta sobre o TAD para datas. A estrutura
para datas e declara como um tipo utilizando o comando typedef. A operacao alteraData
recebe como parametros o dia, mes e ano de uma determinada data e passa, como retorno de
funcao, a data informada. A operacao consultaData realiza a impressao de uma data. Assim,
pode-se observar as vantangens da utilizacao de TADs, atraves destas duas operacoes e necessario
escrever menos, visto que as operacoes podem ser aproveitadas. A data e inicializada no corpo
do programa em 30/01/1984, em seguida e alterada. A sada do programa e a impressao da
data 16/05/2007.
Escreva um programa em C que leia duas datas e retorne a menor data cronologicamente.
Observacao: As datas devem ser armazenadas em estruturas.
Solucao Possvel:
O programa que determina qual e a menor data entre duas. Agora, a inicializacao das
datas e realizada por leitura do teclado. O retorno da operacao menorData e a menor entre
duas datas, a primeira verificacao e realizada quanto ao ano, caso o ano das datas sejam iguais
verifica-se o mes, se os meses sao iguais verifica-se os dias.
Defina o tipo Tponto para representar os pontos do plano cartesiano de duas dimensoes.
Solucao Possvel:
Para representar um ponto, e criada uma estrutura com dois atributos, as coordenadas x e
y.
4.4. EXERCICIOS RESOLVIDOS 127
1 typedef struct {
2 int x ;
3 int y ;
4 } Tponto ;
Solucao Possvel:
A operacao simetricoOrigem tem como retorno uma variavel do tipo Tponto. Para obter
um ponto simetrico a outro em relacao a origem, basta inverter as coordenadas do ponto dado.
Solucao Possvel:
Dado um ponto, a operacao qualQuadrante retorna a qual quadrante esse ponto pertence.
A funcao retorna 0 quando o ponto for parte do limite de quadrantes, 1 quando o ponto pertencer
ao 1o quadrante, 2 quando pertencer ao 2o quadrante, 3 se pertencer ao 3o quadrante e 4 caso seja
do 4o quadrante. Para determinar a que quadrante o ponto pertence, e utilizado um conjunto
de if e else aninhados que verificam os sinais das coordenadas.
12 if ( ponto . y > 0) {
13 return 2;
14 } else {
15 return 3;
16 }
17 }
18 }
Defina um programa que calcule a distancia entre dois pontos, implemente e use operacoes
construtoras e destrutoras, implemente tambem a operacao float distanciaPontos (Tponto
a, Tponto b), de calculo de distancia entre dois pontos.
Solucao Possvel:
Inicialmente, sao definidas a estrutura Tponto e as operacoes aplicaveis a ela. Alem das
funcoes relacionadas ao problema, deve-se sempre definir as funcoes inicializadora e destrutora.
A funcao ler() inicializa o Tponto a partir de valores fornecidos pelo usuario, a funcao zera()
zera as coodernadas do ponto e a funcao distanciaPontos calcula a distancia entre pontos, dada
pela raiz quadrada da soma do quadrado da diferenca entre as abscissas e entre as ordenadas.
A funcao principal utiliza os metodos declarados de forma a corresponder a funcionalidade
desejada.
25
26 return origem ;
27 }
28
29 // Operac~
a o Produtora :
30 float distanciaPontos ( Tponto a , Tponto b ) {
31 float distancia , deltaX , deltaY ;
32
33 deltaX = a . x - b . x ;
34 deltaY = a . y - b . y ;
35
36 distancia = sqrt ( deltaX ^2 + deltaY ^2) ;
37 return distancia ;
38 }
39
40 main () {
41 Tponto a , b ;
42 float distancia ;
43
44 a = ler () ;
45 b = ler () ;
46
47 distancia = distanciaPontos (a , b ) ;
48 printf ( " Distancia entre pontos : % f \ n " , distancia ) ;
49
50 a = zera () ;
51 b = zera () ;
52 }
4.5 Resumo
Inicialmente foram abordadas as seguintes abordagens na programacao:
bottom-up: que e uma tecnica que propoe considerar o programa como um conjunto
de modulos correlacionados, implementar cada um desses modulos e depois junta-los
por meio de uma estrutura global.
top-down: que e uma tecnica que proporciona que um programa seja visto como
uma descricao de um processo. O processo e dividido em subprogramas, os quais sao
responsaveis por cumprir partes da funcionalidade geral do processo. Por sua vez,
cada subprograma ainda pode ser dividido em novos subprogramas.
Utilizou-se a tecnica dividir para conquistarnas duas abordagens, onde cada abordagem
deve ser utilizada de acordo com as informacoes conhecidas do sistema. Aqui enfatizou-se
a abordagem bottom-up porque ela permite que o programador usuario nao se preocupe
com a implementacao de cada modulo, bastando saber a funcionalidade de cada modulo
para que os mesmos possam ser integrados.
130 CAPITULO 4. TIPOS ABSTRATOS DE DADOS
Uma estrutura agrupa varias variaveis numa so. Funciona como uma ficha pessoal que
tenha nome, telefone e endereco. A ficha seria uma estrutura. A estrutura, entao, serve
para agrupar um conjunto de dados nao similares, formando um novo tipo de dados.
Estas proveem uma grande facilidade para o armazenamento de tipos nao definidos pela
linguagem. O uso de estruturas e considerado uma otima pratica de programacao, visto
que, alem de permitirem uma organizacao melhor, possibilitam realizar atribuicoes ou
passagem de parametros em uma unica vez. Uma estrutura pode ser manipulada de forma
seletiva (acesso a um unico atributo) ou de forma integral (acesso a estrutura como um
todo).
Os TADS sao generalizacoes das estruturas, pois associamos varias operacoes sobre a
estrutura, estas podem ser dos seguintes tipos: construtoras, analisadoras, modificadoras,
produtoras e destrutoras. Assim, os TADS sao definidos como uma estrutura de dados e as
operacoes sobre os dados. A importancia de um TAD esta na transparencia que ele oferece,
o usuario do tipo nao precisa se preocupar em como o TAD e implementado. O uso de
TADs permite inumeras vantagens como abstracao, facilidade de alteracao, reutilizacao,
ocultamento e integridade. Os Tipos Abstratos de Dados sao classificados em TADs de
Domnio e TADs Implementacionais. TADs de domnio sao aqueles que descrevem um tipo
do problema, enquanto TADs Implementacionais descrevem uma estrutura que ira ajudar
a resolver o problema, mas nao se refere ao problema. Exemplos de TADs de domnio sao
os registros de alunos, funcionarios, datas, etc. Exemplos de TADs implementacionais sao
as pilhas, listas, etc.
Contudo, e importante dizer que a ideia do TAD e que o codigo da implementacao do tipo
de dado, fique invisvel para o programador que for usar o tipo. Assim, na verdade, C nao
implementa TAD, o que se faz e uma simulacao de TDAs. No entanto, a simulacao de
TADs em C se torna uma boa pratica de programacao, pois torna o codigo do programa
mais compreensvel e simplifica a definicao das estruturas de dados. Em linguagens que
permitam Programacao Orientada a Objetos (POO), os TADs podem ser implementados
com a confiabilidade que C nao permite.
Implemente um TAD para o armazenamento dos dados de um cilindro. A entrada dos dados
referentes ao cilindro sera realizada via console(teclado). As possveis operacoes sobre o tipo
sao:
Inicializa Cilindro;
Altera Altura;
Altera Raio;
Calcula Volume;
Imprime Dimensoes;
Imprime Volume;
Modifique o TAD tData incluindo uma operacoes que determine a idade de uma pessoa, e
quantos dias faltam para seu aniverario tendo como entradas a data de nascimento e a data
atual. Deve-se considerar anos bissextos e meses com o numero de dias conforme o calendario
ocidental.
Implemente um TAD Ponto para representar um ponto no plano com as seguintes operacoes:
Implemente um TAD Circulo para representar um crculo (Observe que o centro e um ponto)
com as seguintes operacoes:
Voce foi contratado para fazer algumas pesquisas no cadastro de uma imobiliaria. Considere
que cada imovel possui um codigo de identificacao, uma area total e o preco por metro quadrado
do imovel. Implemente um programa que leia os dados dos imoveis, armazene e imprima o
imovel mais caro e o de maior area em um TAD imovel.
O sistema deve ler do ticket do usuario o horario e a data de entrada, deve identificar o
horario atual e a respectiva data, calcular o tempo de uso do estacionamento e o valor a ser
pago.
Caso o motorista permaneca no estacionamento por menos de 15 minutos ele nao devera
pagar pelo uso do estacionamento. Caso o motorista permaneca por menos de uma hora ele
devera pagar pelo preco de uma hora completa.
Implemente um TAD horario que armazene os horarios e as datas de entrada e sada para
cada veculo e que tenha operacoes como:
calculo de custo;
O gerente de uma agencia bancaria te contratou para fazer um programa em C para extrair
algumas informacoes das contas dos correntistas de sua agencia. Para tanto leia sucessivamente
os dados do numero da conta (um valor inteiro positivo) e valor da operacao (um valor ponto
flutuante positivo para as operacoes de credito e negativo para as operacoes de debito) realizadas
no ultimo mes. Seu programa deve usar os dados da listagem para informar:
Nos tens 1, 2, e 3 tambem devem ser apresentados os numeros das contas dos correntistas.
Considere que os dados de cada conta sao fornecidos contiguamente, isto e, sao fornecidos
todos os dados das operacoes de uma conta, depois todos os dados de outra conta e assim
sucessivamente. O processamento deve ser encerrado quando for lido um numero de conta igual
a zero.
134 CAPITULO 4. TIPOS ABSTRATOS DE DADOS
O diretor do NPD da UFES te contratou para fazer um programa em C para extrair algumas
informacoes a respeito dos cursos e dos alunos da UFES. Para tanto leia sucessivamente os dados
codigo do curso (um valor inteiro positivo), a matrcula do aluno (um valor inteiro positivo), a
carga horaria da disciplina (um valor inteiro positivo) e a media final obtida em uma disciplina
cursada pelo aluno (um valor ponto flutuante). Seu programa deve esses dados para informar:
3. O curso mais difcil, isto e, aquele que tem o menor coeficiente de rendimento medio.
Vetores
Autores:
Andrezinho
Thiago Paris Salviato
Objetivos:
135
136 CAPITULO 5. VETORES
vezes (ou a utilizacao de 50 variaveis - nao tente isso em casa!): uma para calcular a media e
outra para verificar quais alunos tiveram notas superiores a media. O Exemplo 5.1 mostra uma
maneira de resolver o problema do professor utilizando a Linguagem C, fazendo a dupla leitura
citada acima.
Todos os problemas citados ate agora no captulo poderiam ser resolvidos com a utilizacao
de vetores.
Um vetor e uma estrutura de dados composta homogenea, isto e, ele possui, em geral, mais
de um elemento - por isso, composta - sendo que estes elementos sao sempre de um mesmo tipo
- por isso, homogenea.
Resumindo, a utilizacao de um vetor se faz necessaria sempre que for preciso armazenar
uma quantidade finita de dados de um mesmo tipo, para ser manuseado durante a execucao do
programa. No Exemplo 5.1, por exemplo, as notas de todos os alunos da turma poderiam ter
sido armazenadas num unico vetor com apenas uma leitura e a partir de entao eles estariam
5.2. REPRESENTACAO 137
5.2 Representacao
A Figura 5.1 mostra a maneira mais comum de se representar um vetor graficamente.
A Figura 5.1 representa um vetor de inteiros chamado vet com os nove primeiros numeros
primos, onde o terceiro elemento (elemento de ndice igual a 2) e o numero 5 e o de ndice 4 e o
numero 11, por exemplo. E importante lembrar que todo o primeiro elemento de um vetor na
linguagem C possui o ndice zero.
Analizando a Figura 5.1 fica evidente outras importantes caractersticas dessa estrutura: a
seqencializacao e a indexacao de seus elementos e a sua dimensao.
Quando se declara um vetor num programa, significa que durante a sua execucao sera re-
servado um espaco de memoria sequencial com o tamanho necessario para que seja possvel
armazenar todos os elementos do vetor. Portanto os elementos serao armazenados sequencial-
mente na memoria.
Essa propriedade facilita bastante o acesso do programa ao elemento desejado dentro do
vetor, pois, para que o dado seja recuperado, fica sendo necessario apenas o nome do vetor, que
localiza o primeiro elemento, e um ndice, que indicara ao programa a que distancia do incio
esta o dado desejado.
Mas para que seja feita a reserva do espaco de memoria, deve ser informado no codigo qual
o tamanho do vetor, isto e, o numero maximo de elementos que ele pode armazenar. A Figura
5.2 mostra uma ilustracao de como um vetor e armazenado na memoria do computador.
Dados os conceitos teoricos do vetor, chega a hora de discutir como essa estrutura pode ser
utilizada na pratica, abordando assuntos como a sua definicao, as operacoes definidas sobre ela
e outros. Isso sera feito a partir de agora.
5.3 Definicao
O primeiro passo para uma utilizacao clara e efetiva dos vetores e uma declaracao que deixe claro
para que a estrutura esta sendo usada. Isso ajuda a dar legibilidade ao codigo, logo, facilida a
implementacao.
Na linguagem C, o vetor e declarado da seguinte maneira:
<tipo_dados> <nome_vetor>[<tam_vetor>];
138 CAPITULO 5. VETORES
onde tipo dados representa o tipo dos dados dos elementos que serao armazenados no vetor,
nome vetor o nome pelo qual o vetor sera referenciado e tam vetor o numero de elementos que
cabem la dentro. O Exemplo 5.2 mostra alguns exemplos de declaracao de vetores.
No Exemplo 5.2, o primeiro vetor declarado pode ser representado graficamente pela Figura
5.1 mostrada na secao anterior, ou seja, seu nome e vet e ele pode armazenar ate nove elementos
5.3. DEFINICAO 139
do tipo int. O segundo e o terceiro foram os representados pela Figura 5.2. Aquele, chamado
nome, armazena no maximo 6 elementos do tipo char, e este, chamado notas, ate seis do tipo
float. Ja o ultimo poderia ser utilizado no problema do professor citado anteriormente, por
exemplo. Neste vetor, chamado notasAlunos, podem ser armazenados cinquenta valores do tipo
float, que poderiam representar as notas dos cinquenta alunos.
A estrutura proposta no Exemplo 5.3 possui um vetor chamado nome, do tipo char e de
tamanho igual a 50 para armazenar o nome completo de um aluno; duas variaveis do tipo int
chamadas rg e ano, para armazenar, respectivamente, o numero do RG do aluno e seu ano
(serie); uma variavel do tipo char chamada turma, para armazenar sua turma; e um vetor do
tipo float chamado notas e de tamanho igual a 8, para armazenar as notas desse aluno nas
diferentes disciplinas que ele pode cursar.
140 CAPITULO 5. VETORES
Repare que as notas e o nome do aluno podem ocupar um espaco menor que o tamanho
maximo do vetor. Essa caracterstica dos vetores definidos em tempo de compilacao pode acar-
retar em desperdcio de memoria, pois os espacos armazenados para eles podem nao ser utilizados
totalmente.
Repare que declarando o vetor dessa forma, o desperdcio de memoria pode ser eliminado,
ja que o vetor e utilizado por completo, desde que se tenha conhecimento previo do numero de
elementos que serao armazenados e que se possa informa-lo a aplicacao.
E importante ressalvar que essa forma dinamica de definicao de tamanho nao funciona no
caso de vetores dentro de estruturas, como o vetor nota da estrutura tAluno do Exemplo 5.3,
por exemplo.
5.4 Operacoes
Na linguagem C nao existem operacoes pre-definidas para a manipulacao de um vetor como um
todo. Por exemplo, nao e permitida a leitura de um vetor por inteiro com o comando scanf. As
operacoes so podem ser feitas para cada elemento do vetor, individualmente.
5.4. OPERACOES 141
Como ja mencionado na Secao 5.2, para acessar um elemento de um vetor, sao necessarios
apenas seu nome e o ndice que informa sua localizacao. Na linguagem C a sintaxe de acesso a
um elemento de um vetor e dada por:
<nome_vetor> [<ndice>]
onde o ndice pode ser tanto um numero inteiro maior ou igual a zero quanto uma expressao
inteira composta por variaveis e numeros.
Sabendo acessar o elemento ele pode ser manipulado como uma variavel qualquer e ser
utilizado em operacoes das mais diversas como atribuicao, operacoes aritmeticas, etc. O Exemplo
5.5 mostra algumas dessas operacoes em Linguagem C.
No Exemplo 5.5, primeiramente sao declarados a variavel i, do tipo int; os vetores vet1 e
vet2, ambos do tipo int e de tamanho igual a 10; o vetor notas do tipo float e de tamanho
igual a 8; e as variaveis soma e media. Em seguida, o primeiro elemento do vetor vet1, ou seja,
o elemento de ndice 0 (zero), recebe o valor 43 (linha 11); todos os elementos de vet1 de ndice
mpar recebem o valor 10 (linhas 13 a 15); todos os elementos de vet2 recebem o valor zero
(linhas 17 a 19); cada uma das 8 posicoes do vetor notas e preenchido por um valor lido do
teclado (linhas 21 a 23); os elementos do vetor vet1 sao copiados para vet2 (linhas 25 a 27); e,
por ultimo, e calculada a media dos valores contidos no vetor notas e armazenada na variavel
media (linhas 29 a 32).
E importante ressaltar a grande importancia da utilizacao de expressoes como ndice, pois
ela e essencial para o acesso rapido a todos os elementos do vetor.
Um outro exemplo interessante para ilustrar as operacoes sobre os vetores e a resolucao do
problema do professor citado na secao 5.1. Ja foi proposta uma solucao fazendo uma dupla
leitura das notas dos alunos (Exemplo 5.1). O Exemplo 5.6 mostra uma solucao utilizando
vetores.
Repare que, dessa vez, os valores referentes as notas sao lidos do teclado apenas uma vez e
sao, entao, armazenados no vetor notas e imediatamente somados para o calculo da media da
turma, valor este armazenado na variavel media. Para a contagem de alunos com nota acima da
media, nao e necessaria uma nova leitura do teclado, apenas uma consulta ao vetor notas que
ja possui todos os valores armazenados.
Os vetores tambem podem ser passados como parametro de funcoes, mas nunca como retorno.
O Exemplo 5.7 mostra um codigo em linguagem C que resolve o problema anterior, mas com a
utilizacao de funcoes que recebem um vetor como parametro.
41
42 for ( i = 0 ; i < 50 ; i ++) {
43 printf ( " Digite uma nota " ) ;
44 scanf ( " % f " ,& notas [ i ]) ;
45 }
46
47 media = calculaMedia { notas };
48
49 quant = alunosAcimaMedia ( notas , media ) ;
50
51 printf ( " \ n % d alunos obtiveram nota acima da media \ n " , quant ) ;
52 }
5.5 Strings
O vetor de elementos do tipo char do Exemplo 5.2, chamado nome, assim como todos os vetores
do tipo char, podem ser considerados de um novo tipo, o tipo string. Strings correspondem a
uma sequencia de caracteres. Geralmente, sao usadas para realizar a entrada e sada de dados
em um programa e para armazenar dados nao numericos.
E comum representar uma string como um vetor de caracteres (tipo char), dispondo das
mesmas operacoes comuns a essa estrutura de dados. A Figura 5.3 exemplifica a representacao
de uma string como um vetor de caracteres.
Toda string armazenada em um vetor e terminada por um caracter especial, conhecido como
caracter nulo \0 (barra-zero). A principal funcao desse caracter e alertar o fim de uma string,
5.5. STRINGS 145
evitando um acesso indesejado a uma posicao do vetor que se encontra vazia. O programa
do Exemplo 5.8 demonstra uma forma nao usual de imprimir strings, mas aplica o conceito de
caracter nulo.
No Exemplo 5.8, a condicao de parada do laco for e encontrar o caracter \0, e, durante cada
iteracao, cada caracter do vetor e impresso separadamente. A forma mais comum (e simples)
de imprimir uma string e a apresentada no Exemplo 5.9.
Exemplo 5.9: Impressao de uma string sem a utilizacao direta do caracter barra-zero.
O programa do Exemplo 5.9 produz o mesmo resultado do Exemplo 5.8, porem, naquele o
caracter \0 e verificado implicitamente pela funcao printf.
A biblioteca padrao de C ainda oferece algumas funcoes muito uteis para a manipulacao de
strings, como a strcpy (copia), a strlen (tamanho) e a strcmp (comparacao). O Exemplo 5.10
demonstra o uso dessas funcoes.
A primeira linha dentro da funcao main mostra uma maneira de inicializar um vetor, no
caso do exemplo o vetor chamado vet1, e ja preenche-lo com valores, no mesmo caso com o texto
ola enfermeira (linha 5). Em seguida sao declarados mais dois vetores de caracteres: vet1 e
vet2, ambos de tamanho igual a 20. Depois disso as funcoes da biblioteca padrao string.h sao
utilizadas para, respectivamente: imprimir o tamanho da string em vet1, com a funcao strlen
(linha 9); copiar o conteudo de vet1 em vet2, com a funcao strcpy e em seguida imprim-los na
tela com a funcao printf da biblioteca stdio.h (linhas 11 e 12); copiar a string sim senhor para
dentro do vetor vet3 e em seguida imprimir seu conteudo na tela (linhas 14 e 15); e, finalmente,
comparar o conteudo das strings contidas em vet1 e vet2 (linhas 17 a 21), e vet1 e vet3 (linhas
23 a 27) com a funcao strcmp.
Para o uso da funcao strcpy, a ordem dos parametros e de suma importancia; o primeiro
parametro e o vetor de destino da string que se deseja copiar, enquanto o segundo e o vetor de
origem da string. Em relacao a funcao strcmp, ela retorna o valor zero se as strings armazenadas
em ambos os vetores forem iguais (letras em caixa alta sao consideradas diferentes das em caixa
baixa).
5.6.1 Atributos
O Exemplo 5.11 mostra a definicao da estrutura tVetorInt. Ela possui um vetor de inteiros
chamado vet, de tamanho definido por TAM e um atributo n do tipo int que representa o
numero corrente de elementos em vet, ou seja, o numero de elementos armazenados no vetor
num determinado momento da execucao do programa. Esse valor deve ser zero quando o vetor
estiver vazio e deve ser incrementado ou decrementado sempre que se adicionar ou excluir um
elemento do vetor, respectiviamente.
A primeira linha do codigo do Exemplo 5.11 faz com que toda a palavra TAM existente
no codigo do programa seja substituda pelo valor 100 na hora da compilacao. Esse artifcio e
bastante utilizado quando se trabalha com vetores com tamanho maximo, ja que permite que
uma alteracao no valor de TAM no incio do codigo modifique os tamanhos de todos os vetores
do codigo que possuem TAM como definicao de tamanho.
A Figura 5.4 mostra uma maneira simples de representar graficamente a estrutura proposta
no Exemplo 5.11.
A figura mostra duas estruturas do tipo. Como pode ser observado, apesar de vet possuir
tamanho igual a 100, ele possui apenas 9 elementos na primeira estrutura e 5 na segunda, valores
representados pelo atributo n.
5.6.2 Operacoes
Agora serao ilustradas as funcoes que devem ser utilizadas como operacoes para manipular a
estrutura tVetorInt. Tal como visto no captulo anterior, essas operacoes se dividem em quatro
categorias diferentes: contrutoras, analizadoras, produtoras e modificadoras.
148 CAPITULO 5. VETORES
Construtoras
Para o caso do TAD tVetorInt, podem ser criadas duas funcoes dessa natureza, uma que inicializa
a estrutura com um vetor vazio e outra que a inicializa ja lendo para dentro do vetor um
determinado numero de elementos ignorando o que havia antes. Para efeito de comparacao,
essas duas funcoes sao semelhantes a operacao = utilizada sobre as variaveis dos tipos
primitivos (int, float, char, etc). Uma dessas duas funcoes deve ser utilizada antes de qualquer
outra operacao sobre a estrutura.
A funcao do Exemplo 5.12 e uma maneira de se implementar uma funcao que inicializa a
estrutura tVetorInt sem nunhum elemento dentro do vetor.
Repare que a funcao tem como parametro de entrada uma estrutura do tipo tVetorInt, ou
seja, antes de ser utilizada, uma variavel do tipo deve ser declarada no corpo principal do
programa, a funcao main da linguagem C.
O Exemplo 5.13 mostra o codigo da outra funcao construtora citada.
A funcao do Exemplo 5.13 recebe como parametro de entrada a estrutura do tipo tVetorInt
que se quer inicializar e o numero de elementos que se quer ler para dentro do vetor. Ela, entao,
le do teclado os elementos desejados e armazena-os dentro do vetor da estrutura.
Analisadoras
As operacoes analizadoras sao utilizadas para que se possa obter informacoes relacionadas com
os valores do TAD. Serao mostradas aqui tres funcoes desse tipo.
A primeira funcao, que pode ser visualizada no Exemplo 5.14 e para que possam ser visua-
lizados em tela os valores de todos os elementos armazenados no vetor da estrutura.
A funcao simplesmente acessa todos os elementos do vetor e imprime seus valores na tela
(linha 7).
As outras duas funcoes citadas como analizadoras sao ambas utilizadas para determinar a
existencia de um determinado elemento dentro do vetor. Essas funcoes, alem de funcionarem
como uma simples consulta dentro de um vetor, podem ser utilizadas dentro de outras funcoes
como, por exemplo, para achar um determinado elemento que se queira apagar.
A funcao do Exemplo 5.15 varre o vetor sequencialmente a procura de um elemento e retorna
seu ndice se encontra-lo. Caso contrario, ela retorna -1.
A funcao do Exemplo 5.15 recebe como parametro de entrada a estrutura do tipo tVetorInt
que possui o vetor que se deseja pesquisar e o elemento a ser encontrado. Ela, entao, compara,
a partir do primeiro, o valor de todos os elementos com o valor procurado. Se achar um igual,
150 CAPITULO 5. VETORES
retorna o valor de seu ncide. Se chegar ao final do vetor e nao tiver encontrado nenhum valor
igual ao que se procura, a funcao retorna o valor -1.
Repare que se existir no vetor dois elementos iguais, a funcao obtem apenas o ndice do
primeiro. Isso acontece, porque ela retorna quando o primeiro valor e encontrado. Outra carac-
terstica dessa funcao que deve ser evidenciada e sua ineficiencia quando o valor procurado esta
nas ultimas posicoes ou quando ele nao existe no vetor.
Para resolver o problema da ineficiencia citada, outras funcoes foram desenvolvidas para
otimizar o processo de localizacao de elementos. O Exemplo 5.16 mostra uma delas: a Pesquisa
Binaria. Essa funcao deve ser utilizada apenas em vetores ordenados de forma crescente. Ela
percorre o vetor partindo-o ao meio a procura do elemento e retorna seu ndice se encontra-lo e
-1 caso contrario.
A funcao do Exemplo 5.16 recebe como parametro de entrada a estrutura do tipo tVetorInt
que possui o vetor que se deseja pesquisar e o elemento a ser encontrado. Sao declaradas,
entao, tres variaveis: inicio, fim e meio. A variavel inicio recebe, o valor do ndice do menor
elemento do vetor, ou seja, zero, e fim recebe o valor do ndice do maior elemento, ou seja,
v.n 1. Enquanto o valor de inicio for menor ou igual ao valor de fim, meio recebe o ndice do
elemento central da regiao por eles delimitada. E exatamente esse elemento central que sempre
sera comparado ao elemento que se esta procurando. Se aquele for maior que este, fim recebe o
valor do ndice anterior a meio. Caso contrario, inicio recebe o valor do ndice posterior a meio.
Se nenhuma das duas alternativa acontecerem, quer dizer que o elemento foi encontrado e seu
ndice e igual a meio. Se em algum momento inicio passar a ser maior que fim, o elemento nao
existe na lista e a funcao retorna -1.
As Figuras 5.5 e 5.6 mostram o funcionamento do algoritmo do Exemplo 5.16 para duas
pesquisas diferentes.
A Figura 5.5 ilustra as etapas da busca do elemento 4 dentro do vetor vet da estrutura
5.6. TAD IMPLEMENTACIONAL 151
Figura 5.5: Etapas da pesquisa binaria com o elemento procurado no incio do vetor.
do tipo tVetorInt mostrada na figura. Primeiramente inicio e fim adquirem os valores 0 e 10,
respectivamente. Como inicio e menor que fim, meio recebe o ndice 5, e o elemento apontado
por ele, o valor 10, e comparado ao valor procurado. Como 10 e maior que 4, fim recebe o ndice
4. Como inicio continua menor que fim, meio recebe o ndice 2. Dessa vez, o elemento apontado
por meio e igual ao elemento procurado. A funcao retorna, entao, o valor de seu ndice: 2.
Ja a Figura 5.6 mostra a busca do elemento 23 dentro do mesmo vetor apresentado na Figura
5.5. Primeiramente inicio e fim adquirem os valores 0 e 10, respectivamente. Como inicio e
menor que fim, meio recebe o ndice 5, e o elemento apontado por ele, o valor 10, e comparado
ao valor procurado. Como 10 e menor que 23, inicio recebe o ndice 6. Como inicio continua
menor que fim, meio recebe o ndice 8, e o elemento apontado por ele, o valor 19, e comparado
ao valor procurado. Novamente 19 e menor que 23 e inicio recebe o ndice 9. Como inicio
continua menor que fim, meio recebe o ndice 9 tambem. Dessa vez, o elemento apontado por
meio e igual ao elemento procurado. A funcao retorna, entao, o valor de seu ndice: 9.
E importante observar que, para esta ultima busca, o algoritmo de pesquisa binaria faz
apenas tres comparacoes enquanto que o algoritmo de pesquisa sequencial teria de fazer dez.
Produtoras
As funcoes dessa natureza geram algum tipo de produto como resultado de operacoes feitas
sobre dois ou mais vetores. Dentre elas podem ser citadas a Adicao e o Produto Escalar.
A Adicao, Exemplo 5.17, soma cada elemento de mesmo ndice de um vetor com outro
com o mesmo numero de elementos correntes e armazena o resultado em um terceiro vetor. Se
os vetores a serem somados nao possuirem o mesmo numero de elementos correntes, a funcao
retorna uma mensagem de erro.
152 CAPITULO 5. VETORES
Figura 5.6: Etapas da pesquisa binaria com o elemento procurado no final do vetor.
A funcao somaVetorInt recebe como parametros de entrada tres estruturas do tipo tVetorInt.
Duas delas, v1 e v2, devem possuir os vetores que se deseja somar, e a outra, soma, o vetor onde
a soma sera armazenada. Primeiramente verifica-se se os vetores de v1 e v2 possuem o mesmo
numero de elementos correntes. Se nao possurem, uma mensagem de erro e exibida na tela.
5.6. TAD IMPLEMENTACIONAL 153
Caso contrario, cada elemento de mesmo ndice de v1 e v2 sao somados e armazenados com o
mesmo ndice no vetor de soma e este e retornado pela funcao.
Ja a funcao Produto Escalar, cujo codigo pode ser encontrado no Exemplo 5.18 calcula, como
o proprio nome ja diz, o produto escalar entre dois vetores. Para isso, os vetores devem possuir o
mesmo numero de elementos correntes. Caso contrario a funcao tambem retorna uma mensagem
de erro.
A funcao Produto Escalar recebe como parametros de entrada duas estruturas tVetorInt:
v1 e v2. Primeiro o numero de elementos correntes de seus vetores sao verificados. Caso seja
diferente, uma mensagem de erro e exibida na tela. Se forem iguais, o produto escalar de seus
vetores, que vem a ser a soma dos produtos dos elementos de mesmo ndice, sao calculados.
Modificadoras
As operacoes a seguir sao responsaveis por realizar alteracoes no vetor do TAD de forma a
garantir a coerencia da estrutura. Para excluir um elemento no meio de um vetor, por exemplo,
a funcao de exclusao deve, alem de apaga-lo, cuidar para que o numero de elementos correntes
do vetor seja atualizado e que todos os elementos com ndice maior que o dele sejam transferidos
uma posicao para a esquerda. Alem da operacao de exclusao, serao apresentadas a seguir funcoes
de insercao e ordenacao.
A funcao do Exemplo 5.19 mostra um algoritmo para insercao de um elemento numa posicao
pre-determinada. Ela insere o elemento na posicao desejada considerando a posicao zero como
a primeira do vetor.
comparando os elementos nele contidos com o valor do elemento a ser excludo. Se for igual, ele
move todos os elementos seguintes para a esquerda apagando, assim, o elemento alvo. Depois
disso ele continua a procura por outros elementos a que tambem devem ser excludos. Quando
chega ao final do vetor, ela retorna a estrutura modificada.
As duas proximas funcoes a serem apresentadas sao de ordenacao. Ambas colocam em ordem
crescente os elementos de um vetor, mas utilizando metodos diferentes. A funcao do Exemplo
5.21 mostra a ordenacao pelo metodo do menor.
A funcao ordenaMenosInt recebe a estrutura tVetorInt cujo vetor se deseja ordenar. Pri-
meiramente ela varre o vetor procurando o elemento de menor valor e o troca de posicao com
o primeiro elemente do vetor. Feito isso, ela procura o menor elemento no restante do vetor e
o troca de posicao com o segundo elemento e assim sucessivamente com o terceiro, quarto, . . .,
v.n 1 elementos. A Figura 5.7 ilustra o funcionamento desse algoritmo.
Ja a funcao ordenaBolhaInt, Exemplo 5.22, utiliza o metodo da bolha.
11 v . vet [ b ] = temp ;
12 }
13 }
14 }
15 return ( v ) ;
16 }
Sera apresentado agora um exemplo de utilizacao do TAD tVetorInt. Vale lembrar que o Exemplo
5.23 apresenta apenas o programa principal contendo a chamada das funcoes definidas ate aqui.
Para seu funcionamento correto, todas as funcoes dever estar contidas no arquivo do programa.
1 int main () {
2 tVetorInt vet1 ;
3 tVetorInt vet2 ;
4 tVetorInt vet3 ;
5 int elem ;
6 int pos ;
7 int prodEscalar ;
8 int enter ;
9
10 vet1 = leituraVetorInt ( vet1 ,10) ;
11 vet2 = leituraVetorInt ( vet2 ,10) ;
12 vet3 = in iciaVazioVetorInt ( vet3 ) ;
13
14 printf ( " \ n \ n " ) ;
15 printf ( " Vet1 : " ) ;
16 escritaVetorInt ( vet1 ) ;
17 printf ( " Vet2 : " ) ;
18 escritaVetorInt ( vet2 ) ;
19
20 printf ( " \ n \ nQual elemento deseja localizar utilizando a pesquisa sequencial ?\ n \ n
");
21 scanf ( " % d " ,& elem ) ;
22
23 pos = p e s qu isaSe quen cialI nt ( vet1 , elem ) ;
24 if ( pos >= 0) printf ( " \ n \ nElemento encontrado na posicao % d \ n \ n " , pos ) ;
5.6. TAD IMPLEMENTACIONAL 157
5.8 Resumo
Um vetor e uma estrutura de dados composta homogenea, isto e, ele possui, em geral,
mais de um elemento de um mesmo tipo.
Deve-se tomar cuidado para nao se acessar um vetor indevidamente, como, por exemplo
utilizando um ndice maior que seu tamanho.
Quando acessado individualmente, cada elemento de um vetor pode ser encarado como
uma variavel basica.
Primeira lista:
abcdjklm
Segunda lista:
efghi
Ordem:
5
Resultado:
abcdefghijklm
160 CAPITULO 5. VETORES
M: 5
N: 4
Primeiro vetor:
1 7 13 14 30
Segundo vetor:
2 3 7 16
Resultado:
1 2 3 7 7 13 14 16 30
Obs.: O terceiro vetor nao pode ser gerado copiando-se primeiramente os dois vetores e
fazendo a ordenacao posteriormente.
7. Faca um programa em linguagem C que leia N (N < 1000) numeros de matrculas de alunos
que fazem PD1 e os coloque em ordem crescente num vetor a medida que vao sendo lidas
as matrculas. Posteriormente escreva uma funcao que identifique se um certo conjunto
de M alunos fazem PD1. Deve-se utilizar o algoritmo de pesquisa binaria para fazer esta
verificacao.
(a) leia N numeros reais colocando-os num vetor de 100 elementos (considerar N < 1000);
(b) ordene crescentemente os elementos de ndices mpares utilizando o metodo da bolha
para tal(considerar apenas os N elementos);
(c) escreva os N numeros apos o ajuste do tem (b);
10. Um armazem trabalha com 100 mercadorias diferentes identificadas pelos numeros de 1 a
100. O dono anota as quantidades de cada mercadoria vendida no mes. Ele tem uma tabela
que indica para cada mercadoria o preco de venda. Escreva um algoritmo em linguagem
C que calcule o faturamento mensal do armazem, onde:
12. Dado um vetor contendo uma frase com 80 caracteres (incluindo brancos), escreva um
algoritmo em linguagem C para:
13. Faca um algoritmo em linguagem C para ler um vetor de comprimento N (par menor ou
igual a 50) e imprimir seu conteudo depois de feita a troca dos elementos das posicoes
pares com os elementos das posicoes mpares.
14. Faca um programa em linguagem C que, dado um nome terminado por ponto, devolva o
numero de vogais nele presentes.
15. Faca um programa em linguagem C que, dada uma frase terminada por ponto, retire todos
os espacos em branco da mesma e retorne a frase modificada.
16. Faca um programa em linguagem C que, dada duas listas de nomes, compare-as e retorne
o numero de vezes que cada palavra da segunda lista aparece na primeira lista. Assuma
que cada nome seja composto de no maximo 15 caracteres.
Matrizes
Autores:
Clebson Oliveira
Julio Dalfior
Objetivos:
6.1 Introducao
Uma matriz e um tipo de dado composto homogeneo na qual seus elementos sao organizados
em uma estrutura multidimensional. Por exemplo, uma matriz bidimensional e formada pela
combinacao de linhas horizontais e verticais. A figura 6.1 mostra uma matriz 5 por 3. Note que
a matriz possui 5 linhas e 3 colunas. Embora matrizes possam ter mais que 2 dimensoes, neste
livro serao abordadas apenas matrizes bidimensionais. Uma matriz com m linhas e n colunas e
chamada matriz m por n.
Para mostrar que matrizes bidimensionais sao muito comuns no nosso dia-a-dia podem-
se citar varios exemplos: um cartao de bingo, uma agenda de compromissos (6.2), etc. E
interessante que haja uma maneira de representar esse tipo de estrutura, para que se possa
utiliza-la na solucao de problemas.
Na figura 6.2 tem-se que as linhas representam os dias do mes, as colunas representam os
meses do ano e os elementos da matriz representam um determinado dia do mes.
Considerando que essa matriz sera utilizada para representar se ha ou nao compromisso
naquele dia do mes, temos uma matriz bidimensional porque a cada celula dessa matriz estao
164
6.2. DEFINICAO E ACESSO 165
associadas duas informacoes (mes e dia). Por outro lado, se for necessario listar varios compro-
missos para cada dia do mes ter-se-a uma matriz tridimensional, porque a cada celula da matriz
estarao associadas tres informacoes (mes, dia e hora).
E fato que a visualizacao das matrizes utilizadas facilita o entendimento deste conceito. Por
exemplo, uma matriz bidimensional pode ser visualizada como um quadro e uma tridimensional
pode ser visualizada como um cubo formado por varios cubos menores. Apesar de ser difcil
visualizar matrizes com mais de tres dimensoes, vale ressaltar que elas existem e que e possvel
declara-las em C. Como exemplo da utilizacao de matrizes com mais de tres dimensoes pode-se
utilizar o exemplo anterior supondo que se queira representar tambem o ano. Entao, teremos
uma matriz de quatro dimensoes (ano, mes, dia e hora).
Para que um problema seja modelado atraves de uma matriz e necessario que se defina uma
variavel para representa-la, para que assim se possa utilizar seus elementos para guardar in-
formacoes e posteriormente aplicar operacoes sobre essa estrutura.
166 CAPITULO 6. MATRIZES
6.2.1 Definicao
Para definir uma variavel do tipo matriz e necessario que se saiba o tamanho de cada dimensao.
No entanto, existem duas maneiras de estabelecer o tamanho dessas dimensoes: estaticamente
e dinamicamente.
Estaticamente
No momento em que se define a matriz e necessario estabelecer um tamanho para cada dimensao,
sendo que esse tamanho nao podera ser alterado durante a execucao do programa. O codigo do
exemplo 6.1 mostra como isso pode ser feito em C:
O numero entre o primeiro par de colchetes ([5]) define o tamanho da primeira dimensao
(numero de linhas). O numero entre o segundo par de colchetes ([10]) define o tamanho da
segunda dimensao (numero de colunas), e assim por diante. No caso acima a variavel matriz
possui 5 linhas e 10 colunas, como se pode ver na figura 6.3. Os tracos na matriz representam a
informacao armazenada na celula, a qual e o compartimento onde estao localizados os elementos
da matriz.
Inicializacao
Vale ressaltar que na declaracao de matrizes mostrada anteriormente, os valores das celulas da
matriz nao foram inicializados e isso pode gerar resultados inesperados. Uma vez que nao se
sabe quais valores estavam armazenados anteriormente nas areas de memoria utilizadas. Para
evitar esse tipo de problema e uma boa pratica de programacao inicializar os elementos das
matrizes antes de tentar acessar seus valores, mesmo que posteriormente esses elementos sejam
configurados com outros valores. A forma mais simples de se fazer isso, em C, pode ser vista no
exemplo 6.2.
6.2. DEFINICAO E ACESSO 167
1 int main () {
2
3 int i , j ;
4 int matriz [2][2] = {0};
5
6 return 0;
7 }
Na linha 4 do exemplo 6.2 temos a inicializacao de todos os elementos da matriz com o valor
zero.
6.2.2 Acesso
Agora que a matriz ja foi inicializada, para se utilizar os dados contidos nela sera necessario
fazer uma operacao de acesso, como no exemplo 6.3:
1 # define NMESES 12
2 # define NDIAS 31
3 # define NHORAS 24
4
5 main () {
6
7 int reservaSala [ NMESES ][ NDIAS ][ NHORAS ] = {0};
8 int resposta =0;
9 .
10 .
11 .
12 resposta = reservaSala [5][1][10];
13
14 if ( resposta ==0) {
15
16 reservaSala [5][1][10]=1;
17 }
18
19 }
6.3.1 Atributos
O primeiro passo para definir o TAD tMatriz envolve escolher os seus atributos. Como a ideia
e modelar o problema por uma matriz, e necessario que um dos atributos seja a propria matriz
que armazenara os elementos. Alem disso e necessario guardar a quantidade de elementos de
cada dimensao.
Apesar de ser possvel definir uma matriz com um numero qualquer de dimensoes e que
armazene um tipo qualquer de dado (int, float, char, etc), neste captulo serao utilizadas apenas
matrizes bidimensionais de inteiros, por serem comumente utilizadas.
Portanto, os atributos de uma matriz bidimensional de inteiros sao:
No exemplo 6.4 pode ser vista uma maneira de se definir o TAD tMatrizInt, de forma a
representar matrizes bidimensionais de inteiros.
1 # define MAX_DIM 5
2
3 typedef struct {
4
5 int valores [ MAX_DIM ][ MAX_DIM ];
6 int nLinhas ;
7 int nColunas ;
8 } tMatrizInt ;
No exemplo 6.4 a matriz valores e declarada estaticamente, sendo seu tamanho definido pela
constante MAX DIM. Como os proprios nomes evidenciam, nLinhas e nColunas sao variaveis
inteiras que representam o numero de linhas e colunas, respectivamente, da matriz.
Na figura 6.4 pode-se visualizar a estrutura da matriz dados de uma variavel do tipo tMa-
trizInt inicializada como no exemplo abaixo:
1 tMatrizInt dados ;
6.3. O TAD IMPLEMENTACIONAL TMATRIZ 169
2 int i , j ;
3
4 dados . nLinhas = 3;
5 dados . nColunas = 3;
6
7 for ( i =0; i < dados . nLinhas ; i ++) {
8 for ( j =0; j < dados . nColunas ; j ++) {
9 dados . valores [ i ][ j ]=0;
10 }
11 }
Nas linhas 4 e 5 o tamanho da matriz e definido como 3 por 3. Na linha 7, o comando for
percorre as linhas da matriz de ndice zero a nLinhas-1. O segundo comando for percorre todas
as colunas (para cada laco do for anterior) de ndice zero a nColunas-1. Dessa forma percorrem-
se todos os elementos da matriz valores, dentro das dimensoes passadas, atribuindo-se a eles o
valor zero.
A figura 6.4 representa a matriz valores da estrutura dados do exemplo 6.5.
Figura 6.4: Estrutura da matriz valores pertencente a variavel dados (para MAX DIM igual a
5)
Apesar da matriz valores pertencente a variavel dados ter 5 linhas e 5 colunas, somente os
elementos pertencentes as 3 primeiras linhas e colunas foram inicializados. Isso porque definiu-se,
nas linhas 4 e 5, que a matriz deveria ter somente esse numero de linhas e colunas. Dessa forma,
em todas as operacoes efetuadas sobre essa variavel somente os elementos pertencetes a essa
submatriz (formada pelos zeros na figura) devem ser considerados. Todos os outros (marcados
com sinal -) possuem valores nao determinados, por nao terem sido inicializados, e devem ser
ignorados.
6.3.2 Operacoes
Uma das vantagens de se implementar um tipo abstrato de dado e facilitar a solucao de pro-
blemas, atraves do uso de operacoes que atuem sobre o tipo abstrato de dado definido. Dessa
170 CAPITULO 6. MATRIZES
Inicializacao
Uma operacao essencial para se realizar sobre tMatrizInt e a inicializacao. Ela deve ser a primeira
a ser realizada, caso contrario as demais podem gerar resultados inesperados (como termino do
programa, por exemplo). Isso porque, durante a inicializacao, preparam-se as estruturas internas
do TAD para se adequar ao caso desejado. No caso do tipo tMatrizInt proposto nesse captulo,
a operacao de inicializacao definira as dimensoes iniciais da matriz.
O codigo do exemplo 6.6 mostra como isso pode ser implementado:
Na definicao da funcao Inicializa (linha 1), os argumentos nLinhas e nColunas servem para
definir a quantidade de linhas e colunas da matriz respectivamente, e esses valores sao atribudos
a estrutura tMatriz nas linhas 6 e 7. Finalmente, na linha 16 a estrutura e retornada atraves da
variavel resultado.
Atualizacao de Valor
Como nao e interessante que uma matriz apresente todos os elementos com os mesmos valores,
e necessario definir uma forma de atualiza-los individualmente. Isso pode ser feito atraves da
operacao de escrita definida pela funcao do exemplo 6.7.
1 tMatrizInt atualizaMatriz ( tMatrizInt matriz , int linha , int coluna , int valor ) {
2
3 if ( ( linha >=0 && linha < matriz . nLinhas ) && ( coluna >=0 && coluna < matriz .
nColunas ) ) {
4 matriz . valores [ linha ][ coluna ] = valor ;
5 }
6
7 return matriz ;
8 }
6.3. O TAD IMPLEMENTACIONAL TMATRIZ 171
Na linha 3 e verificado se o elemento pertence a matriz(se o numeros de sua linha e de sua coluna
estao dentro das dimensoes da matriz). Caso tudo esteja correto, o valor passado e atribudo ao
elemento desejado e a funcao retorna o numero 0, informando que a operacao foi realizada com
sucesso. Caso contrario a funcao retorna o numero -1 para informar que a escrita nao pode ser
efetuada.
Acesso a valor
Tendo em vista a importancia de utilizar o valor de um determinado tem, e necessario construir
uma funcao para acessar os dados da matriz (exemplo 6.8).
Exibicao
Para poder visualizar todos os elemetos de uma matriz de inteiros e necessario definir uma
operacao de exibicao (ou impressao) da matriz na tela (exemplo 6.9).
No exemplo 6.9 tem-se, na linha 5, um comando for para varrer as linhas da matriz, e para
cada linha tem-se outro comando for, na linha 6, para varrer os elementos dessa linha. Na linha
7, o trecho %5d faz com que os numeros sejam exibidos sempre com 5 dgitos, o que faz com
que a forma de exibicao seja regular, como pode ser visto na figura 6.5.
A variavel soma e inicializada com o valor zero. O for da linha 5 percorre todos os elementos
da linha da matriz e, para cada laco, adiciona o valor da celula da matriz a soma. A variavel
soma funciona, entao, como um acumulador e, ao final dos lacos, armazena o valor da soma dos
elementos da linha.
6.3. O TAD IMPLEMENTACIONAL TMATRIZ 173
A variavel soma e inicializada com o valor zero. O for da linha 5 percorre todos os elementos
da coluna da matriz e, para cada laco, adiciona o valor da celula da matriz a soma. A variavel
soma funciona, entao, como um acumulador e, ao final dos lacos, armazena o valor da soma dos
elementos da coluna.
Produto Escalar
Agora, sera definida uma operacao de composicao chamada produto escalar, que gera uma matriz
M de cujos elementos sao resultados de uma composicao dos elementos de A e B 1 (M=A.B).
Nesta operacao os elementos mij da matriz M sao resultantes do somatorio dos produtos entre
os elementos das linhas i da primeira matriz, e os elementos das colunas j da segunda matriz,
para que esta operacao seja executada e necessario que o numero de colunas da matriz A seja
igual ao numero de linhas da matriz B.
15
16 produto . valores [ i ][ j ] += matriz1 . valores [ i ][ k ] *
17 matriz2 . valores [ k ][ j ];
18 }
19 }
20 }
21
22 produto . nLinhas = matriz1 . nLinhas ;
23 produto . nColunas = matriz2 . nColunas ;
24 } else {
25
26 produto . nLinhas = produto . nColunas =0;
27 }
28
29 return produto ;
30 }
Solucao Possvel:
Para se armazenar as informacoes de reserva da sala sera utilizada uma matriz com 31 linhas
(representando os dias) e 12 colunas (representado os meses). Dessa forma, para se reservar a
sala para o dia 1o de janeiro, atribui-se ao elemento da linha 0 e coluna 0 o valor 1.
Para resolver o problema sera criada uma funcao para cada operacao.
1. Reservar a sala:
A primeira coisa a ser feita e verificar a reserva da sala para a data informada. Se a sala
estiver livre sera reservada, uma mensagem de sucesso impressa na tela e a matriz alterada
sera retornada. Caso contrario, a matriz e retornada inaltarada e uma mensagem de erro
impressa na tela.
6.4. EXERCICIOS RESOLVIDOS 175
2. Cancelar a reserva:
Novamente, deve-se verificar a reserva da sala para a data informada. Se a sala estiver
reservada a reserva sera cancelada, uma mensagem de sucesso impressa na tela e a matriz
alterada sera retornada. Caso contrario, a matriz e retornada inaltarada e uma mensagem
de erro impressa na tela.
Uma vez definidas as funcoes que realizam as operacoes, falta entao definir a funcao principal
do programa onde a interacao com o usuario sera feita e as funcoes serao chamadas.
1 int main ()
2 {
3 tMatrizInt reservas ;
4 int codigo , dia , mes ;
5
6 reservas = inicializa (31 , 12) ;
7
8 for ( mes =0; mes <12; mes ++) {
9 for ( dia =0; dia <31; dia ++)
10 reservas . valores [ dia ][ mes ] = 0;
11 }
6.4. EXERCICIOS RESOLVIDOS 177
12
13 do {
14 printf ( " \\ n \\ n \\ n \\ n \\ n \\ n \\ n " ) ;
15 printf ( " ## Reserva de Salas ##\\ n \\ nEscolha uma operac~ a o :\\ n " ) ;
16 printf ( " 1 - Reservar a sala \\ n " ) ;
17 printf ( " 2 - Cancelar uma reserva \\ n " ) ;
18 printf ( " 3 - Listar dias livres \\ n " ) ;
19 printf ( " 4 - Exibir ndice de ocupac~ a o \\ n " ) ;
20 printf ( " 5 - Sair \\ n " ) ;
21 printf ( " Digite o codido da opecac~ a o escolhida : " ) ;
22 scanf ( " % d " , & codigo ) ;
23
24 switch ( codigo ) {
25 case 1:
26 printf ( " \\ n \\ nDigite a data da reserva ( dia / mes ) : " ) ;
27 scanf ( " % d /% d " , & dia , & mes ) ;
28 reservas = reservaSala ( reservas , dia , mes ) ;
29 break ;
30
31 case 2:
32 printf ( " \\ n \\ nDigite a data da reserva a cancelar ( dia / mes ) : " ) ;
33 scanf ( " % d /% d " , & dia , & mes ) ;
34 reservas = cancelaReserva ( reservas , dia , mes ) ;
35 break ;
36
37 case 3:
38 printf ( " \\ n \\ nDigite o mes : " ) ;
39 scanf ( " % d " , & mes ) ;
40 listaDiasLivres ( reservas , mes ) ;
41 break ;
42
43 case 4:
44 imp rime Indic eOcu pacao ( reservas ) ;
45 break ;
46 }
47 } while ( codigo != 5) ;
48
49 return 0;
50
51 }
Dada uma matriz bidimensional de inteiros, defina operacoes que realizem os seguintes
calculos:
A soma dos elementos da diagonal principal
A soma dos elementos da submatriz triangular
178 CAPITULO 6. MATRIZES
Solucao Possvel:
Para calcular a soma dos elementos da diagonal principal de uma matriz M[i,j] e necessario
notar que todos os elementos da diagonal principal apresentam ndices referentes a coluna
e a linha iguais (M[i,i]). Assim, varrer-se a diagonal principal da matriz atraves de um
loop, na linha 6, onde a cada laco soma-se o valor do elemento M[i,i] a variavel soma e
incrementa-se o valor do ndice i de uma unidade.
Como os elementos da matriz triangular superior sao os elementos que estao situados acima
dos elementos da diagonal principal, pode-se notar que para cada linha esses elementos
estao situados nas colunas a direita do elemento da diagonal principal. Dessa forma, varre-
se a matriz triangular superior atraves de um loop, na linha 6, que varre todas as linhas i
da matriz, e para cada linha tem-se outro loop, linha 7, para varrer as colunas de ndice
i+1 ate nColunas. Entao, soma-se o valor de cada elemento visitado a variavel soma,
linha 8.
10 }
11
12 return soma ;
13 }
Dada uma matriz bidimensional nxn de zeros e uns, defina uma operacao para calcular
o numero de uns isolados dessa matriz. Considere que um numero um esta isolado se nenhum
dos elementos adjacentes a ele, apenas na horizontal e vertical, sao uns.
Solucao Possvel:
Para cada celula que contenha o valor um, sera necessario checar no maximo quatro celulas
adjacentes a ela para saber se a mesma contem um numero um isolado. Os loops das linhas 5 e
7 servem para varrer a matriz completamente, e a cada celula visitada checa-se se suas celulas
adjacentes sao zeros, dependendo de quantas ela possa ter. Assim, se o elemento visitado estiver
na primeira linha (linha 9) ele pode ser: o primeiro da linha (11), e entao so tera adjacentes
abaixo e a direita (12); o ultimo da linha (linha 16), e entao so tera adjacentes abaixo e a esquerda
(17); ou caso nao seja nem o primeiro e nem o ultimo, tera adjacentes abaixo, a esquerda e a
direita. Caso o elemento esteja na ultima linha (linha 27) ele pode ser: o primeiro da linha
(linha 29), e entao so tera adjacentes acima e a direita (linha 30); o ultimo da linha (linha 34),
e entao so tera adjacentes acima e a esquerda (linha 35); ou caso nao seja nem o primeiro e nem
o ultimo tera adjacentes acima, a esquerda e a direita (linha 39). Apos checar se o elemento
visitado esta na primeira ou na ultima linha da matriz, temos o caso dele estar na primeira
ou na ultima coluna, desconsiderando os elementos dessas colunas que estejam na primeira ou
na ultima linha. Se ele estiver na primeira coluna (linha 45) tera adjacentes acima, abaixo e a
direita (linha 46); e se ele estiver na ultima coluna (linha 50), tera adjacentes acima, abaixo e a
esquerda (linha 51). Finalmente, se o elemento visitado nao estiver na primeira linha, na ultima
linha, na primeira coluna e nem na ultima coluna (linha 53); ele tera adjacentes acima, abaixo,
a esquerda e a direita (linha 55).
6.5 Resumo
Uma matriz e um tipo de dado composto homogeneo na qual seus elementos sao organi-
zados em uma estrutura multidimensional.
E necessario inicializar a matriz para evitar situacoes de acesso a elementos que contenham
valores indeterminados.
O TAD tMatriz e composto pelos seguintes atributos: numero de linhas da matriz, numero
de colunas da matriz e a estrutura que armazenara os elementos.
Quando se tenta acessar um elemento com posicao nao definida na matriz, e necessario
retornar uma mensagem de erro para indicar que ocorreu um erro na execucao do programa.
2. Altere o subprograma anterior para exibir na tela todas as posicoes da matriz em que se
encontra tal valor maximo.
3. Faca um programa que calcule a matriz resultante da soma de duas matrizes de dimensoes
m linhas e n colunas, com valores inteiros.
182 CAPITULO 6. MATRIZES
4. Escreva um subprograma que calcule a transposta de uma dada matriz. Considere como
matriz trasposta At de A a matriz de cujos elementos At [i,j] sao iguais a os elementos
A[j][i] para 1<=i<=m e 1<=j<=n, onde m representa o numero de linhas e n o numero
de colunas da matriz A.
5. Escreva uma funcao que verifica se uma matriz nxn e simetrica. Uma matriz A e simetrica
se A[i,j] = A[j,i] para todo 1<=i,j<=n.
6. Faca um subprograma que calcule a soma dos termos que se situam na diagonal secundaria
ou abaixo dela numa matriz quadrada com elementos inteiros. Assuma que o numero
maximo de linhas e colunas e 100, mas que a matriz pode estar preenchida apenas parci-
almente.
Observacao: Antes de escrever o subprograma, voce deve apresentar a declaracao do tipo
da matriz que deve ser colocada nos programas que usarao este subprograma.
1 2 3
4 5 6
7 8 9
antes
1 4 7
2 5 8
3 6 9
depois
8. Uma matriz quadrada inteira e chamada de quadrado magico se a soma dos elementos
de cada linha, a soma dos elementos de cada coluna e a soma dos elementos das diagonais
principal e secundaria sao todos iguais.
Exemplo de um quadrado magico:
8 0 7
4 5 6
3 10 2
6.6. EXERCICIOS PROPOSTOS 183
Escreva uma funcao que verifica se uma matriz de n linhas e n colunas representa um
quadrado magico.
9. Um quadrado latim de ordem n contem os numeros de 1 ate n de forma que nenhum numero
e repetido na mesma linha ou coluna. Este quadrado pode ser usado, por exemplo, em
alguns torneios esportivos para assegurar que cada equipe joga com todas outras equipes
uma e somente uma vez. Escreva uma funcao booleana que recebe uma matriz quadrada
e checa se ele e realmente um quadrado latim.
Exemplo de quadrado latim:
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
(numero de
10. Considere 2 matrizes quadradas do tipo tMatrizInt. Faca um subprograma para cada item
abaixo (note que as matrizes devem obrigatoriamente ser passadas como parametros aos
subprogramas):
12. Considere uma matriz de inteiros. Define-se como vizinhanca de uma posicao da matriz a
soma de todos os numeros que estejam em posicoes adjacentes. Escreva um programa que
determine a posicao do elemento de maior vizinhanca.
a)Exibir uma matriz com as caractersticas definidas acima (considere que a dimensao
N, isto e, seu numero de linhas ou colunas, da matriz sera lida no programa principal e
passada como parametro para o subprograma);
b)determinar se as duas matrizes sao identicas;
c)determinar se a segunda matriz e uma rotacao de 90 graus a direita sobre a primeira;
184 CAPITULO 6. MATRIZES
d)determinar se a segunda matriz e uma rotacao de 180 graus a direita sobre a primeira;
e)determinar se a segunda matriz e a imagem espelhada vertical da primeira;
f)determinar se a segunda matriz e a imagem espelhada horizontal da primeira;
As figuras abaixo ilustram cada tipo de matriz com um exemplo de dimensao
3 x 3:
1 2 3
4 5 6
7 8 9
original
1 2 3
4 5 6
7 8 9
identica
7 4 1
8 5 2
9 6 3
90 a direita
9 8 7
6 5 4
3 2 1
180 a direita
13. Escreva ainda um programa que leia 2 matrizes e determine as relacoes existentes entre
elas utilizando os subprogramas do exerccio anterior.
14. Escreva uma funcao que receba uma matriz preenchida com caracteres maiusculos do
tipotMatrizChar e um vetor do tipo tVetor contendo uma palavra em letras maiusculas
e retorne o numero de ocorrencias da palavra na matriz. A palavra pode estar escrita
na matriz de cima para baixo, da esquerda para a direita ou nas diagonais paralelas a
diagonal principal ou na propria.
6.7. TRABALHOS SUGERIDOS 185
F L O R E S T A R S
H J V N O A C I W Z
C A P E L A P I S C
I K V D I P M B S F
T Y G C S A L U O P
Q L K C A D U O L C
A A S A R P R Y O A
N E N E F T E B A U
J K D D S H W L F L
I O T U G J J V A A
Arquivo de sada:
Lista de palavras encontradas em ordem alfabetica:
186 CAPITULO 6. MATRIZES
AULA
CAPELA
FLORESTA
LAPIS
Lista de palavras nao encontradas em ordem alfabetica:
ALEGRIA
MANGA
Lista de palavras encontradas, ordenadas pelo numero de ocorrencias na matriz:
CAPELA 2 (2, 0) horizontal (4, 3) diagonal
AULA 1 (6,9) vertical
FLORESTA 1 (0, 0) horizontal
LAPIS 1 (2,4) horizontal
Direcao em que mais palavras foram encontradas: horizontal
Numero de palavras encontradas nesta direcao: 3
Nesse exemplo a matriz so sera definida apos a execucao da funcao scanf, onde o tamanho
de suas dimensoes sera lido do teclado.
Captulo 7
Apontadores
Autores:
Objetivos:
Neste captulo procurou-se dar enfase a aspectos relevantes, de caracter introdutorio, para
que este texto seja utilizado como primeira leitura, e posterior consulta, sobre apontadores.
187
188 CAPITULO 7. APONTADORES
No lugar do tipo do apontador, devem-se utilizar alguns dos tipos padroes da linguagem C,
como char, int e float, ou ate mesmo novos tipos definidos pelo programador. No Exemplo 7.1,
tem-se como criar apontadores dos tipos char, int, float e tAluno.
1 typedef struct {
2 char nome [30];
3 int matricula ;
4 } tAluno ;
5
6 main ( ) {
7 char * c ;
8 int * p ;
9 float * f ;
10 tAluno * x ;
11 }
190 CAPITULO 7. APONTADORES
Assim, no codigo do Exemplo 7.1, c pode apontar para uma area de memoria que guarda
uma variavel do tipo caracter, enquanto p, f e x dos tipos inteiro, ponto flutuante e tAluno,
respectivamente.
Dois outros operadores tambem estao associados a sintaxe de apontadores: o operador en-
dereco de memoria e o operador seta. Eles serao discutidos nas duas proximas subsecoes.
1 int * p ;
2 int d = 10;
3 p = &d;
4 printf ( " % p \ n % p \ n " , &d , p ) ;
1 typedef struct {
2 char nome [30];
3 int matricula ;
4 } tAluno ;
5
6 main ( ) {
7.3. ACESSO A VARIAVEL POR MEIO DE APONTADORES 191
7 tAluno * x ;
8 tAluno y ;
9
10 x = &y;
11 x - > matricula = 2031;
12
13 printf ( " % d \ n " , y . matricula ) ;
14 }
Exemplo 7.3: Acesso ao atributo de uma estrutura por meio do operador seta
Note que no Exemplo 7.3, x e um apontador para uma estrutura tAluno e y uma variavel
tAluno. Apos fazer x apontar para o endereco de y, para acessar a matrcula de y atraves de x,
deve ser utilizado o operador >. Ja para imprimir o atual valor da matricula de y, que passou
a ser 2031, basta utilizar o ponto.
1 int *p;
2 int c = 10;
3 int d;
4 p = &c;
5 d = *p;
1 float * p ;
2 float c = 15;
3 p = &c;
4 * p = * p + 1;
5 printf ( " % f " , c ) ;
Pode ser observado no Exemplo 7.5, que p aponta para c. Assim, a expressao na linha 4
incrementa o conteudo da variavel apontada por p em uma unidade, ou seja, c passa a ter o
valor 16, o qual e impresso na tela.
Uma variavel pode receber o valor de outra por meio de apontadores. Na linha 7 do Exemplo
7.6, a variavel i apontada por v tem seu valor alterado para o de d, pois d esta apontado por j.
1 float * v ;
2 float * j ;
3 float d = 15.0;
4 float i = 17.0;
5 j = &d;
6 v = &i;
7 *v = *j;
8 v = j;
Um apontador tambem pode ser atribudo a outro apontador. No Exemplo 7.6, linha 8, v
passa a apontar para o mesmo endereco que j aponta.
Por fim, esse acesso a variaveis atraves de apontadores e muito importante, pois e por
meio deste recurso que as variaveis passadas como parametros de uma funcao sao alteradas
definitivamente, dentro da propia funcao.
7 }
8
9 main ( ) {
10 int a = 0;
11
12 adicionaX1 (10 , a ) ;
13 printf ( " a = %d\n", a);
14 adicionaX2 (10 , & a ) ;
15 printf ( " a = %d\n", a);
16 }
Na Figura 7.5, repare que na passagem por copia, o valor de a e copiado para o conteudo de
b, enquanto que na passagem por apontador, b passa a apontar para o endereco de a.
Uma aplicacao da passagem de valor por apontadores e proporcionar que uma funcao retorne
multiplos valores. O Exemplo 7.8 mostra como isso e feito.
3
4 aux = * x ;
5 *x = *y;
6 * y = aux ;
7 }
8
9 main ( ) {
10 int a, b;
11 a = 10;
12 b = 20;
13
14 troca (& a , & b ) ;
15 printf ( " a = %d , b = % d \ n " , a , b ) ;
16 }
No Exemplo 7.8, desejava-se uma funcao para trocar o conteudo de a e b. Contudo, uma
funcao pode retornar apenas um valor no return. Assim, por meio dos apontadores, os dois
conteudos sao alterados e retornados no escopo da funcao, pois a e b sao passados sem copia,
ou seja, sao passados seus enderecos como parametros da funcao troca.
que era livre, passa a ser apontado por uma variavel apontador, podendo assim ser utilizado, o
que e ilustrado na Figura 7.7.
1 int * p
2 p = ( int *) malloc (4*( sizeof ( int ) ) ) ;
No Exemplo 7.9, a expressao sizeof retorna o numero de bytes de um tipo passado como
parametro, no caso int. Alem disso, como a funcao malloc devolve um apontador do tipo void
(void *) para um bloco de bytes consecutivos, esse apontador deve ser convertido em apontador
para o tipo desejado, por meio do cast (int *) nesse caso, uma vez que a variavel p e do tipo int,
garantindo assim, a consistencia de tipos.
A Figura 7.8 ilustra um espaco de 4 inteiros, alocados dinamicamente pelo Exemplo 7.9.
Um espaco de memoria deve ser desalocado quando nao e mais necessario. Isto significa
que a area de memoria que foi apontada por um apontador agora passa a ser novamente livre.
Somente variaveis alocadas dinamicamente podem ser desalocadas em tempo de execucao.
A desalocacao de memoria, em C, esta no Exemplo 7.10.
1 int * p ;
2 p = ( int *) malloc (4 * sizeof ( int ) ) ;
3 free ( p ) ;
No Exemplo 7.10, a funcao free, assim como malloc, esta na biblioteca stdlib.h e desaloca
uma area de memoria. Note que a funcao free deve receber como parametro uma variavel
196 CAPITULO 7. APONTADORES
apontador. Assim, o area de memoria apontada por ele sera entao liberada. A Figura 7.9 ilustra
a desalocacao de memoria.
Para contornar o problema de perda de memoria com espacos nao utilizados, faz-se uso da
alocacao dinamica. A cada necessidade de se ler uma linha com certo numero de caracteres,
aloca-se o exato tamanho da linha, ou seja, a quantidade de caracteres da string que sera lida.
O Exemplo 7.11 mostra a implementacao em linguagem C de uma matriz de caracteres,
dinamicamente alocada. Esta matriz possui 4 linhas de tamanhos alocados de acordo com o
tamanho da string armazenada, e cada linha da matriz recebe uma linha do texto da Figura
7.10. A matriz declarada no Exemplo 7.11 e simplesmente um vetor de apontadores para strings
e os elementos do vetor g contem os enderecos dos primeiros elementos de cada string. Esse
vetor de apontadores para strings e chamado matriz de apontadores ou matriz de strings. Na
Figura 7.11 esta ilustrada a matriz de strings correspondente ao texto.
1 main ( ) {
2 char * g [4];
3 g [0] = ( char *) malloc ( strlen ( " Joao ama Maria " ) * sizeof ( char ) ) ;
4 strcpy ( g [0] , " Joao ama Maria " ) ;
5 g [1] = ( char *) malloc ( strlen ( " Maria ama pedro " ) * sizeof ( char ) ) ;
6 strcpy ( g [1] , " Maria ama pedro " ) ;
7 g [2] = ( char *) malloc ( strlen ( " Ana ama quem ama Maria " ) * sizeof ( char ) ) ;
8 strcpy ( g [2] , " Ana ama quem ama Maria " ) ;
9 g [3] = ( char *) malloc ( strlen ( " Quem Ana ama ? " ) * sizeof ( char ) ) ;
10 strcpy ( g [3] , " Quem Ana ama ? " ) ;
198 CAPITULO 7. APONTADORES
11 }
1 float *p , h = 15.0;
2 *p = h;
1 int * p ;
2 int * h ;
3 h = ( int *) malloc ( sizeof ( int ) ) ;
200 CAPITULO 7. APONTADORES
4 p = h;
5 free ( h ) ;
1 typedef struct {
2 int numerosala ;
3 int numeroalunos ;
4 } tsala ;
5
6 acresentaAluno ( tsala * g ) {
7 g - > numeroalunos = g - > numeroalunos + 1;
8 }
9
10 main ( ) {
11 int p ;
12 tsala sala ;
13 sala . numerosala = 1;
14 sala . numeroalunos = 0;
15 tsala * x ;
16 tsala * y ;
17
18 y = & sala ;
19 x = y;
20 acrescentaAluno ( x ) ;
21 acrescentaAluno ( y ) ;
22 }
No Exemplo 7.15, o numero de alunos da variavel sala e alterado duas vezes, uma pelo
apontador x, outra por meio do apontador y. Portanto, quando apontadores sao usados de uma
maneira indiscriminada, pode-se diminuir a legibilidade do codigo.
indica a quantidade atual destes na lista. Cada um dos nos, por sua vez, possuem informacoes
armazenadas, um apontador para o proximo no e no caso de uma lista duplamente encadeada,
tambem um apontador para o no anterior. Na Figura 7.14, tem-se um esquema de uma lista
simplesmente encadeada, com tres nos e com apontadores para o primeiro e o ultimo elemento.
Note que cada um dos nos possui um apontador para o proximo, com excecao do ultimo, no
qual ha um apontador para null, pois nao ha proximo. Repare tambem que a estrutura da lista
possui apontadores para o incio e o fim da lista, alem de armazenar seu tamanho corrente, no
caso, tres elementos.
Pode-se perceber que incluir em uma lista encadeada torna-se um processo simples. Se um
novo dado necessita ser includo, basta alocar um espaco de memoria para o no, atualizar os
apontadores da sequencia de nos e a quantidade deles na lista. Excluir um dado da lista e um
processo semelhante, com a diferenca de que, ao inves de se alocar, desaloca-se uma area da
memoria.
Vale destacar que as estruturas da lista e de cada um dos dados armazenados em um no
sao exemplos do uso de abstracao, pois se encapsulam informacoes, seleciona-se o que deve
ser apresentado de uma funcao a outra e possibilita-se o trabalho em nveis de implementacao
e uso, pratica fundamental na modularizacao de um codigo. Conforme visto no captulo 4,
pode-se dizer que o tipo de abstracao utilizada na lista encadeada e uma abstracao de dados
implementacional, por meio do uso de Tipo Abstratos de Dados - TADs.
Anteriormente foi visto que TADs sao conjuntos de valores com comportamento uniforme
definido por operacoes, que sao tipicamente os componentes responsaveis pela interface entre
os diferentes nveis e modulos de implementacao. Essa interface e responsavel por encapsular e
proteger os dados.
A implementacao do TAD lista encadeada tLista sera apresentada a seguir. Tambem serao
detalhadas varias operacoes fundamentais sobre a lista. Inicialmente, sera definido o tipo tNo,
o qual e um componente importante de tLista.
202 CAPITULO 7. APONTADORES
1 typedef struct {
2 tNo * primeiro ;
3 tNo * marcador ;
4 tNo * ultimo ;
5 int tam ;
6 } tLista ;
Repare que tres atributos de tLista sao apontadores para o tipo tNo, o qual foi definido na
secao anterior. Dois deles, primeiro e ultimo, apontam para o comeco e para o fim da lista,
enquanto marcador aponta para qualquer posicao. A variavel inteira tam e a responsavel por
guardar o atual tamanho, ou numero de nos, da lista.
O apontador marcador e responsavel por apontar o no corrente num processo de busca
sequencial. Por exemplo, na operacao de imprimir a lista, que sera explicada mais adiante,
a informacao e extrada do no apontado por marcador e, este vai sendo posicionado desde o
primeiro no ate o ultimo.
Operacoes Construtoras
As operacoes construtoras sao responsaveis por garantir a alocacao da estrutura tLista, alem de
inicializa-la com os valores desejados, geralmente com os apontadores apontando para NULL e
as variaveis numericas com valor zero.
Inicializacao (vazia)
indevida. Em seguida, o tamanho da lista e zerado uma vez que o atual tamanho da lista e nulo.
Operacoes Analisadoras
Em algumas situacoes e importante analisar a posicao atual do marcador, tal como se ele esta
no fim da lista, e se a lista esta vazia. Por exemplo, ao se percorrer a lista, uma condicao de
parada importante e quando o final da lista for alcancado e, ao se incluir ou excluir um elemento
da lista, e necessario saber se a lista esta vazia.
Essas analises sao importantes para que nao se cometa acessos indevidos com os apontadores,
um dos problemas explicados neste capitulo.
Final
Ja na Figura 7.16 o marcador aponta para NULL, ou seja para o fim da lista. Assim, o
retorno da funcao e 1.
Vazia
Assim como na analise do fim da lista, vaziaLista retorna o resultado de uma comparacao.
Nesse caso, porem, se o tamanho da lista e igual zero. Como o tamanho da lista e inicializado
com este valor e, sempre que um novo no for adicionado, ou removido, este tamanho deve ser
atualizado, a unica possibilidade de ele ser zero e quando a lista estiver realmente vazia.
Um exemplo da utilizacao de vazia e quando se deseja incluir, ou excluir um no da lista. E
necessario um tratamento especial na primeira inclusao, pois o primeiro e ultimo elemento sao
os mesmos, e na exclusao, uma vez que seria um erro tentar remover elementos de uma lista
vazia.
Operacoes Modificadoras
Uma das caractersticas interessantes da lista encadeada e a facilidade de se incluir e excluir
elementos, em tempo de execucao, com praticidade, uma vez que e necessario somente alocar e
desalocar nos, respectivamente. E importante, entao, conhecer as funcoes de insercao e exclusao,
tratadas como operacoes modificadoras, pois alteram a composicao corrente da lista. Seguem,
assim, os exemplos de implementacao.
Insercao no Final
No Exemplo 7.22, o novo elemento e a lista no qual ele sera inserido sao passados como argu-
mentos. A passagem do cabecalho lista e feita por apontador, pois assim, qualquer modificacao
em seus elementos e feita diretamente na funcao, sem necessidade de passar toda lista por copia
e retorna-la ao fim da execucao da funcao. O elemento sera includo no fim da lista, mas dife-
rentes implementacoes, nas quais ele e inserido no comeco, ou numa dada posicao, tambem sao
possveis.
206 CAPITULO 7. APONTADORES
Como pode ser visto no exemplo acima, inicialmente o novo no deve ser criado para armazenar
o elemento a ser includo. Para isso e utilizada a funcao inicializadora criaNo, que aloca
um espaco de memoria do tamanho de tNo, faz a chave ser igual ao elemento passado como
argumento e retorna um apontador para o espaco alocado. A Figura 7.17 mostra o estado da
lista, antes da inclusao, e o novo no a ser includo.
Agora que se ja se tem o novo no, e necessario inclu-lo na lista, ou seja, atualizar o valor de
tam e os apontadores. Como a lista tem mais um no, seu tamanho deve ser incrementado em
uma unidade. Quanto a atualizacao dos apontadores, os apontadores ultimo e marcador devem
apontar para o no includo, respectivamente, porque ele e includo no fim da lista e no ultimo
no acessado. Antes, porem, e importante verificar se a lista esta vazia, pois caso esteja, quem
tambem vai apontar para no a ser includo, sera o apontador primeiro da lista, caso contrario,
sera apontador proximo do ultimo no da lista. Vale destacar que a analise se a lista esta vazia
e um exemplo de uso da funcao vaziaLista, que foi explicada anteriormente.
A Figura 7.18 exibe a lista apos a inclusao do novo no.
Exclusao
7.7. TAD IMPLEMENTACIONAL LISTA ENCADEADA TLISTA 207
Na funcao de exclusao, sao passadas como parametros a lista e a posicao do no a ser excludo.
Assim, e importante saber, inicialmente, se o valor de pos e valido e, caso nao seja, a funcao e
encerrada.
Sao criados dois apontadores auxiliares do tipo tNo: auxA e auxB. Para buscar o no da
posicao pos, auxA aponta para o primeiro no da lista e inicia-se um laco for, ate que o valor de
i, que inicialmente e zero, seja pos. A cada iteracao desse laco, auxB vai apontar para o ultimo
no apontado por auxA e este, por sua vez, aponta para o proximo no da lista. Portanto, quando
auxA estiver apontando para o no a ser excludo, o anterior a ele e apontado por auxB, o que e
importante na hora de se atualizarem os apontadores, conforme sera explicado.
Apos ter encontrado o no da posicao pos, tem que se analisar quatro situacoes possveis: 1)
esse no a ser excludo nao ser nem o primeiro e nem o ultimo no da lista; 2) ser o ultimo; 3) ser
o primeiro; 4) e ser o unico no. No caso 1, basta fazer o apontador proximo do no apontado por
auxB apontar para o no apontado pelo proximo do no apontado por auxA. No caso 2, tambem
se deve fazer o ultimo no da lista ser auxB. Vale lembrar que, no caso de auxA ser o ultimo,
ao se fazer o proximo de auxB ser o proximo de auxA, esta se fazendo simplesmente auxB ter
como proximo o valor NULL. Ja no caso 3, coloca-se o proximo de auxA como o no inicial e,
no caso 4, faz-se tambem o apontador ultimo da lista apontar para o proximo de auxA. Nesse
ultimo caso, esta se redirecionando os apontadores, primeiro e ultimo da lista, para NULL.
Agora que ja se tem os apontadores atualizados e retirado o no que sera excludo da sequencia
de nos, coloca-se o marcador da lista como NULL, pois sera perdido o ultimo no acessado. Entao,
diminui-se o tamanho da lista e, finalmente, libera-se o espaco de memoria apontado por auxA.
A Figura 7.19 ilustra a lista da Figura 7.16 apos a exclusao do no 2.
Operacoes Produtoras
As funcoes produtoras possibilitam extrair alguma informacao da lista. Dividem-se em duas
categorias: as que permitem posicionar o marcador no no, cuja informacao sera extrada, e as
que realmente obtem a informacao. Serao mostradas inicialmente as operacoes que marcam a
posicao de um elemento. Apresentam-se a que posiciona o marcador no primeiro no da lista e a
que posiciona no proximo no do marcador atual. A fim de ilustrar a utilizacao destas funcoes,
sera dado como exemplo o uso delas na funcao imprimir.
O Exemplo 7.24 e a implementacao da funcao que posiciona o marcador no primeiro no da
7.7. TAD IMPLEMENTACIONAL LISTA ENCADEADA TLISTA 209
lista.
Para fazer o marcador de uma lista apontar para o primeiro elemento dela, basta igualar
o marcador ao apontador primeiro. O objetivo da operacao primeiroLista e posicionar o
marcador no incio da lista, para permitir que seja feita uma busca nos elementos da lista a
partir do seu incio.
A operacao proximoLista e mostrada no Exemplo 7.25.
A funcao proximoLista e para posicionar o marcador, fazendo-o apontar para o seu proximo
elemento da lista. A verificacao se o marcador atual e diferente de NULL evita o acesso indevido
a memoria.
O Exemplo 7.26 mostra a operacao responsavel por retornar a informacao do no apontado
pelo marcador da lista.
A funcao obterInfo tem por objetivo retornar o conteudo do no apontado pelo marcador.
E gravado no endereco x se houve sucesso, ou nao, em se obter a informacao. Assim, impede-se
de acessar um local indevido da memoria.
O Exemplo 7.27 ilustra o uso dessas informacoes por meio do uso de uma funcao que imprime
todos os dados de uma lista. Para a funcao e passada a lista cujos elementos serao impressos.
Inicialmente primeiroLista e utilizada para posicionar o marcador no incio da lista e, em
seguida, comeca um laco enquanto o final dela nao e alcancado, verificacao feita pela funcao
finalLista, conforme descrito anteriormente.
210 CAPITULO 7. APONTADORES
Para cada posicao do marcador, obterInfo retorna o valor de info do no apontado pelo
marcador da lista. Esse valor e impresso caso tenha ocorrido sucesso na obtencao da informacao,
ou seja, a variavel erro ter o valor 1. O valor a ser impresso e inteiro, visto que x e do tipo tInfo
(int, como declarado anteriormente). Por fim, proximoLista posiciona o marcador no proximo
no da lista e, somente quando este marcador for NULL, o laco enquanto sera encerrado.
Operacoes Destrutoras
Quando a lista nao e mais necessaria, e importante desalocar o espaco de memoria ocupado por
seus nos. As funcoes responsaveis por desalocar a lista e seus elementos sao conhecidas como
destrutoras.
Finalizacao
Exemplo 7.28: Desalocacao das areas de memoria ocupados pelos nos da lista
7.7.4 Uso
A fim de ilustrar a utilizacao do TAD implementacional lista encadeada, sera utilizado um
cadastro simples de alunos na universidade. Veja o Pseudocodigo 7.1.
O Pseudocodigo 7.1 descreve os passos para o programa de cadastro simples, no qual apenas
pode-se ler e armazenar algumas entradas. Apos a leitura, alguns dados podem ser excludos e
as informacoes restantes serem impressas.
212 CAPITULO 7. APONTADORES
No cadastro, havera apenas o nome e o numero de matrcula do aluno, mas vale lembrar que,
para um registro de informacoess mais detalhado, basta acrescentar mais atributos no exemplo
de estrutura tInfo do exemplo apresentado.
Observe o Exemplo 7.29, no qual se tem a transcricao do Pseudocodigo 7.1. Foram utilizadas
todas as funcoes estudadas nesta secao, porem com algumas modificacoes, uma vez que o campo
info, da estrutura do no, nao e mais um inteiro, e sim, uma outra estrutura. So estao mostradas
as estruturas e as operacoes anteriores que sofreram modificacao.
1 typedef struct {
2 int matricula ;
3 char nome [30];
4 } tInfo ;
5
6 void imprimirLista ( tLista lista ) {
7 tInfo x ;
8 int erro = 0;
9
10 primeiroLista (& lista ) ;
11 while (! finalLista (& lista ) ) {
12 x = obterInfo (& lista , & erro ) ;
13 if ( erro ) {
14 printf ( " Aluno : % s \ n " , x . nome ) ;
15 printf ( " Matricula : % d \ n \ n " , x . matricula ) ;
16 }
17 proximoLista (& lista ) ;
18 }
19 }
20
21 main ( ) {
22 tLista lista ;
23 tInfo dados ;
24 int pos ;
25
26 iniciaLista (& lista ) ;
27
28 do {
29 printf ( " Entre com o nome do aluno : " ) ;
30 scanf ( " % s " , & dados . nome ) ;
31 printf ( " Entre com a matricula do aluno : " ) ;
32 scanf ( " % d " , & dados . matricula ) ;
33 if ( dados . matricula <= 0)
34 break ;
35 incluirFimLista (& lista , dados ) ;
36 } while (1) ;
37
38 imprimirLista ( lista ) ;
39
40 printf ( " Digite a posic~ a o do elemento a ser excludo : " ) ;
41 scanf ( " % d " , & pos ) ;
7.8. EXERCICIOS RESOLVIDOS 213
Para o trecho de codigo do Exemplo 7.30, ilustre os estados das variaveis apontadores e
nao-apontadores. Utilize para a ilustracao os padroes adotados na Figura 7.3 e Figura 7.4, para
representar uma variavel nao apontador e apontador respectivamente.
1 int *p;
2 int *q;
3 int a;
4 int b;
5 int c;
6
7 b = 10;
8 c = 15;
9 a = c;
10 p = &b;
11 q = &c;
12 *p = *q;
Solucao Possvel:
Inicialmente sao criados dois apontadores para inteiro, p e q, e tres variaveis inteiras: a b e
c. Em seguida, b passa a armazenar o valor 10, enquanto c, 15. Ja a rebece o valor de c, ou
seja, 15. Isso e ilustrado nas tres primeiras cenas da Figura 7.20.
5 }
6
7 main ( ) {
8 int x ;
9 int quadrado ;
10
11 scanf ( " % d " , & x ) ;
12 quadrado = calculaPot ( x ) ;
13 printf ( " Quadrado de % d = % d \ n " , x , quadrado ) ;
14 }
Solucao Possvel:
O dado de entrada do programa e um numero inteiro, e as sadas devem ser as pontencias
quadrada e cubica deste numero, calculadas pela funcao apresentada no Exemplo 7.31, com suas
devidas alteracoes. O Pseudocodigo 7.2 exibe as etapas para a solucao.
Para atender a etapa Calculo das potencias pela funcao calculaPot, a funcao calcu-
laPot deve ser alterada para tambem calcular e retornar o cubo do valor. Contudo, o retorno
dessa funcao ja e o quadrado do parametro de entrada. Nesse sentido, a passagem por apon-
tadores e a ferramenta para multiplos retornos numa funcao, o que torna importante o passo
Passagem de parametros para a funcao calculaPot
As modificacoes no Exemplo 7.31 podem ser observadas no Exemplo 7.32.
Como pode ser observado, calculaPot passa a ter um parametro que ira receber o endereco
da variavel cubo, declarada na main. Dessa forma, ao termino da execucao de calculaPot,
cubo ira conter a potencia cubica do valor.
Foi visto anteriormente como inserir um elemento no final da lista. Agora, pretende-se criar
uma funcao que insere no comeco de uma lista simplesmente encadeada.
Solucao Possvel:
Para inserir um novo no, no comeco da lista, os passos que devem ser seguidos pela funcao
aparecem no Pseudocodigo 7.3, onde tambem pode-se observar os dados de entrada e o retorno
da funcao.
O Exemplo 7.33 mostra a implementacao em C. Para tal funcao, a passagem da lista sera
feita por apontador, pois assim, qualquer modificacao em seus elementos sera feita diretamente
na funcao e, por conseguinte, o retorno e void. A lista poderia ser passada por copia, mas a
lista modificada teria estar apos um comando return.
2 tNo * no ;
3
4 no = criaNo ( elem ) ;
5 if ( vaziaLista ( lista ) ) {
6 lista - > ultimo = no ;
7 } else {
8 no - > proximo = lista - > primeiro ;
9 }
10 lista - > primeiro = lista - > marcador = no ;
11 lista - > tam ++;
12 }
Para ilustrar a odenacao de uma lista, considere que ela e formada por nos cuja informacao
armazenada seja um nome e uma idade. Ulizando as estruturas e funcoes ja apresentadas, alem
de novas funcoes, crie um programa que le as informacoes do teclado ate que uma idade invalida
seja digitada (menor que zero). O programa deve imprimir a lista ordenada crescentemente por
218 CAPITULO 7. APONTADORES
Solucao Possvel:
De forma geral, o programa deve seguir o Pseudocodigo 7.4.
O passo Ordenacao da lista pode ser decomposto como segue no Pseudocodico 7.5. A
ideia consiste em posicionar o marcador no comeco da lista e, enquanto o final desta nao e
atingido, buscar o menor elemento a frente do atual marcador. Se encontrar algum, deve-se
trocar as informacoes entre o no marcador e o que guarda a menor idade a frente.
1 typedef struct {
2 char nome [30];
3 int idade ;
4 } tInfo ;
5
6 void imprimirLista ( tLista lista ) {
7 tInfo x ;
8 int erro = 0;
9
10 primeiroLista (& lista ) ;
11 while (! finalLista (& lista ) ) {
12 x = obterInfo (& lista , & erro ) ;
13 if ( erro ) {
14 printf ( " Nome : % s \ n " , x . nome ) ;
15 printf ( " Idade : % d \ n \ n " , x . idade ) ;
16 }
17 proximoLista (& lista ) ;
18 }
19 }
20
21 tNo * menorIdade ( tLista lista ) {
22 tNo * aux , * menor ;
23
24 menor = lista . marcador ;
25 aux = menor - > proximo ;
26 while ( aux != NULL ) {
27 if ( aux - > info . idade < menor - > info . idade )
28 menor = aux ;
29 aux = aux - > proximo ;
30 }
31 return menor ;
32 }
33
34 void ordenaPorIdade ( tLista * lista ) {
35 tNo * aux ;
36 tInfo x ;
37
38 primeiroLista ( lista ) ;
39 while (! finalLista ( lista ) ) {
40 aux = menorIdade (* lista ) ;
41 if ( lista - > marcador != aux ) {
42 x = lista - > marcador - > info ;
43 lista - > marcador - > info = aux - > info ;
44 aux - > info = x ;
45 }
46 proximoLista ( lista ) ;
47 }
48 }
49
220 CAPITULO 7. APONTADORES
50 main ( ) {
51 tLista lista ;
52 tInfo dados ;
53 int pos ;
54
55 iniciaLista (& lista ) ;
56 do {
57 printf ( " Entre com o nome : " ) ;
58 scanf ( " % s " , & dados . nome ) ;
59 printf ( " Entre com a idade : " ) ;
60 scanf ( " % d " , & dados . idade ) ;
61 if ( dados . idade < 0)
62 break ;
63 incluirFimLista (& lista , dados ) ;
64 } while (1) ;
65 ordenaPorIdade (& lista ) ;
66 imprimirLista ( lista ) ;
67 destroiLista (& lista ) ;
68 }
Considere uma lista de funcionarios de uma empresa na qual sao armazenados o nome e o
salario destes. Pretende-se imprimir na tela todos os funcionarios cujos salarios estao acima da
7.8. EXERCICIOS RESOLVIDOS 221
media.
Solucao Possvel:
O programa principal segue os passos descritos no Pseudocodigo 7.6.
O passo Verificar quem esta acima da media pode ser descrito como o Pseudocodigo
7.7. Para achar a media, basta somar todos os salarios e dividir pelo total de nos. Tendo a
media, procura-se, no por no, quem tem o salario maior que ela.
Pseudocodigo 7.7 Processo para ver quem esta com salario acima da media
Processo componente "Verificar quem esta acima da media":
Posicionar o marcador no comeco da lista;
Enquanto n~ao chegar no fim da lista:
Somar os salarios;
Posicionar o marcador no proximo no da sequ^
encia da lista;
Achar a media (Divide-se o total somado pelo tamanho da lista);
Posicionar o marcador no comeco da lista;
Enquanto n~ao chegar no fim da lista:
Se existir um no com salario maior que a media:
Imprimir as informac~
oes do funcionario.
Posicionar o marcador no proximo no da sequ^
encia da lista;
FIM-Processo componente "Verificar quem esta acima da media".
222 CAPITULO 7. APONTADORES
1 typedef struct {
2 char nome [30];
3 float salario ;
4 } tInfo ;
5
6 void imprimirLista ( tLista lista ) {
7 tInfo x ;
8 int erro = 0;
9
10 primeiroLista (& lista ) ;
11 while (! finalLista (& lista ) ) {
12 x = obterInfo (& lista , & erro ) ;
13 if ( erro ) {
14 printf ( " Nome : % s \ n " , x . nome ) ;
15 printf ( " Salario : % f \ n \ n " , x . salario ) ;
16 }
17 proximoLista (& lista ) ;
18 }
19 }
20
21 void acimaMedia ( tLista lista ) {
22 tInfo x ;
23 int erro = 0;
24 float total , media ;
25
26 total = 0;
27 primeiroLista (& lista ) ;
28 while (! finalLista (& lista ) ) {
29 x = obterInfo (& lista , & erro ) ;
30 if ( erro )
31 total += x . salario ;
32 proximoLista (& lista ) ;
33 }
34
35 media = total / lista . tam ;
36
37 primeiroLista (& lista ) ;
38 while (! finalLista (& lista ) ) {
39 x = obterInfo (& lista , & erro ) ;
40 if ( erro ) {
41 if ( x . salario > media ) {
42 printf ( " Nome : % s \ n " , x . nome ) ;
43 printf ( " Salario : % f \ n \ n " , x . salario ) ;
44 }
45 }
46 proximoLista (& lista ) ;
7.9. RESUMO 223
47 }
48 }
49
50 main ( ) {
51 tLista lista ;
52 tInfo dados ;
53 int pos ;
54
55 iniciaLista (& lista ) ;
56 do {
57 printf ( " Entre com o nome : " ) ;
58 scanf ( " % s " , & dados . nome ) ;
59 printf ( " Entre com o salario : " ) ;
60 scanf ( " % f " , & dados . salario ) ;
61 if ( dados . salario < 0)
62 break ;
63 incluirFimLista (& lista , dados ) ;
64 } while (1) ;
65 acimaMedia ( lista ) ;
66 destroiLista (& lista ) ;
67 }
7.9 Resumo
Apontadores sao um tipo de variavel que guarda o endereco de outras variaveis. Trata-se
de um conceito de baixo nvel, ligado essencialmente a arquitetura de computadores.
O operador & retorna o endereco de memoria de uma variavel. Ja o operador seta ( >)
e utilizado para acessar, por meio de apontadores, os atributos de uma estrutura.
224 CAPITULO 7. APONTADORES
Os apontadores podem alterar o conteudo da variavel apontado por ele. Por meio de
apontadores tambem e possvel a passagem de parametros sem copia. A passagem de
parametros por apontadores permite multiplos retornos em uma funcao, evita a copia de
muitos dados e possibilita a alteracao das variaveis do programa no decorrer da execucao
da funcao.
Dentre os principais problemas com o uso de apontadores estao: apontadores nao iniciali-
zados, objetos pendentes, referencias pendentes e programacao macarronica.
O TAD Implementacional Lista Encadeada e uma forma eficiente de armazenar dados num
programa, pois aloca-se e desaloca-se os espacos para os dados dinamicamente, o que torna
muito simples as operacoes de incluir e excluir elementos.
1 main ( ) {
2 int p ;
3 int * d ;
4 int q = 10;
5 float * j ;
6 float t = 15.0;
7 j = &t;
8 p = &q;
9 d = j;
10 }
1 main ( ) {
2 int * p = ( int *) malloc ( sizeof ( int ) ) ;
3 int * q = ( int *) malloc ( sizeof ( int ) ) ;
4 int * j ;
5 int * h ;
6 int * v ;
7 int d = 20;
8 int e = 30;
9 *q = e;
10 *j = d;
11 p = &d;
12 h = &e;
13 v = q;
14 free ( q ) ;
15 }
9. Crie uma funcao que insere um novo elemento na lista numa dada posicao. Considere que
os argumentos da funcao sao:
- Um apontador para a lista no qual o elemento sera inserido.
- Um elemento de um tipo tInfo previamente definido.
- Um inteiro que indica a posicao na qual ocorrera a insercao.
Sugestao: verifique se a posicao fornecida a funcao e valida.
lista resultante nao possua chaves repetidas e que Ae Btambem nao possuem chaves
repetidas.
11. Numa lista duplamente encadeada, os nos possuem um apontador para o no anterior a
ele na lista, alem do ja apresentado apontador para o proximo. Com base nisso, imple-
mente todas as funcaoes e estruturas discutidas no captulo na forma de lista duplamente
encadeada.
12. Pilhas sao um cso particular da lista simplesmente encadeada, no qual insere-se e retira-se
um elemento apenas do fim, uma poltica conhecida como LIFO (last in first out). Seja
entao P=( a(1), a(2), ..., a(n) ) uma pilha. Assim, a(1) e o elemento da base da pilha;
a(n) e o elemento topo da pilha; e a(i+1) esta acima de a(i). As operacoes associadas sao:
Com base nessas informacoes, implemente uma pilha de dados pilha de inteirosem C,
utilizando as estruturas e funcoes apresentadas sobre lista simplemente encadeada.
13. Duas pilha sequenciais numericas estao ordenadas crecentemente a partir do topo. Trans-
fira os elementos dessas pilhas para uma terceira pilha, inicialmente vazia, de modo que ela
fique ordenada decrescentemente (maior valor no topo). Suponha que nao haja restricoes
quanto a capacidade das pilhas.
14. O problema do abre/fecha parenteses. Este problema consiste em verificar se uma ex-
pressao matematica esta corretamente formada em termos de abre/fecha parenteses.
Exemplos:
7-((X*((X+Y)/(J-3))+Y)/(4-2.5))
((A+B)
)A+B(-C
(A + B)) - (C + D
Numa expressao correta o numero de )deve ser igual ao numero de (. Cada )deve
ser precedido por um (. Para isto, pode utilizar-se de um contador inicialmente igual a
zero que, ao percorrer a expressao da direita para a esquerda, e decrementado quando se
encontra um (e incrementado quando se encontra um ). Assim, o contador no final
da expressao deve ser igual a zero e, em nenhum momento, o ele deve ser menor que zero.
7.10. LISTA DE EXERCICIOS 227
Entao, implemente um programa C que leia uma expressao com parenteses e verifique
se ela esta corretamente formada em termos de abre/fecha parenteses usando o metodo
acima, utilizando as estruturas e funcoes de pilha criadas nos exerccios anteriores, mas
modificadas para receber caracteres.
15. Outro caso particular da lista simplesmente encadeada e a fila. Agora, a insercao e feita
apenas no fim da lista e a remocao no incio. Seja entao F=( a(1), a(2), ..., a(n) ) uma
fila. Dessa forma, a(1) e o comeco da fila; a(n) e o final da pilha; e a(i+1) esta atras de
a(i). As operacoes associadas sao:
Com base nessas informacoes, implemente uma fila de dados fila de nomesem C, utili-
zando as estruturas e funcoes apresentadas sobre lista simplemente encadeada.
16. Duas filas sequenciais de nomes estao ordenadas crecentemente a partir do incio. Transfira
os elementos que ocorrem nessas duas filas para uma terceira fila, inicialmente vazia, de
modo que ela tambem fique ordenada crescentemente, ou seja, o primeiro nome em ordem
alfabetica no comeco da fila.
17. Uma palavra e um palndromo se tem a mesma sequencia de letras, quer seja lida da
esquerda para a direita ou da direita para a esquerda (exemplo: raiar). Implemente uma
solucao para verificar se uma palavra e um palndromo, usando pilha(s) e/ou fila(s).
18. Suponha uma fila de inteiros F. Mude a posicao de um elemento desta fila, tendo apenas
uma pilha vazia P e uma variavel do tipo inteiro x como auxiliares. Considere apenas as
operacoes associadas aos tipos fila e pilha.
19. Implemente uma lista encadeada que armazene as informacoes sobre os DVDs de uma
locadoras. Tais informacoes sao ttulo do filme, nome do diretor, principais atores e numero
de copias disponveis na locadora. Apresente um menu e crie funcoes que atendam as
seguintes opcoes:
- Dado um ator, imprimir as informacoes sobre os filmes nos quais ele foi um dos atores
principais.
20. Sabe-se que um texto e uma sequencia de caracteres contendo apenas letras, espacos em
branco e sinais de pontuacao. Uma palavra e definida como um segmento do texto que
consiste apenas de letras. Escreva uma funcao que recebe um texto do teclado e imprime
uma relacao de todas as palavras que ocorrem no texto juntamente com o numero de
ocorrencias de cada palavra.
(a) Ler os dados da listagem e coloca-los em quatro listas. O usuario deve poder incluir
tantos dados quanto quiser. A indicacao de fim de entrada de dados e feita atraves
de um aluno de Matrcula 0 (zero).
(b) Mostrar as notas de um aluno numa disciplina. Lembre-se que o aluno pode ter
cursado mais de uma vez uma mesma disciplina por motivo de reprovacao.
(c) Incluir uma nota de um aluno em uma disciplina.
(d) Retificar uma nota de um aluno em uma disciplina.
(e) Excluir uma nota de um aluno em uma disciplina.
(f) Excluir todos os dados de um aluno jubilado.
(g) Calcular o o coeficiente de rendimento (C.R.) de um aluno. Lembre-se que o C.R. e
calculado da seguinte maneira:
7.11. TRABALHOS SUGERIDOS 229
Exemplo de Programa
Escolha Operacao:
0 - Sair
1 - Ler dados
2 - Mostrar Notas
3 - Incluir Nota
4 - Corrigir Nota
5 - Excluir Nota
6 - Excluir Aluno
7 - CR de Aluno
8 - Melhor Aluno
9 - Maus Alunos
10 - Formando
Opcao: 1
Dados do aluno: 18 1 60 4.5
Dados do aluno: 1111 3232 30 8.7
Dados do aluno: 234 500 75 9.0
Dados do aluno: 18 1 60 7.0
... ... ...
Dados do aluno: 0 0 0 0.0
Opcao: 2
Matrcula: 18
Disciplina: 1
Notas: 4.5 7.0
Opcao: 3
Matrcula: 1111 Disciplina: 316
Carga Horaria: 60
Nota: 7.0
230 CAPITULO 7. APONTADORES
Opcao: 4
Matrcula: 1111 Disciplina: 316
Carga Horaria: 60
Nota: 7.0
Nova Nota: 8.5
Opcao: 5
Matrcula: 1111 Disciplina: 316
Nota: 7.0
Opcao: 6
Matrcula: 234
Opcao: 7
Matrcula: 1111
CR: 7.2
Opcao: 8
Matrcula: 234
Opcao: 9
Matrculas: 194
Opcao: 10
Matrcula: 1111
Nao cumpriu carga horaria mnima.
Opcao: 0
Ate a proxima!
Descricao
Controle de Trafego Aereo e um servico prestado por controladores, em terra, que guiam
aeronaves (geralmente avioes) no ar e no solo, para garantir um fluxo de trafego seguro e
7.11. TRABALHOS SUGERIDOS 231
Espaco Aereo
Em muitos pases, os servicos de trafego aereo sao prestados em toda a extensao do espaco
aereo e estes servicos sao utilizados por todos os usuarios (aeronaves privadas, militares e
comerciais). Os espacos aereos onde o controlador e responsavel por prover separacao entre
as aeronaves sao chamados de espaco aereo controlado em oposicao ao espaco aereo
espaco aereo nao controlado no qual pilotos das aeronaves sao responsaveis por manter
a separacao entre a sua aeronave e outras. Dependendo do tipo de voo e de classe do espaco
aereo, o controlador de trafego aereo pode emitir instrucoes que os pilotos devem seguir
ou apenas informacoes de voo para ajudar os pilotos operando no espaco aereo. Em todos
os casos, entretanto, o piloto tem a responsabilidade final pela seguranca da aeronave, e
pode nao cumprir as intrucoes numa emergencia.
Os servicos de controloe de trafego aereo incluem o controle de rotas das aeronaves que
estao no espaco aereo do aeroporto, o controle da autorizacao de pousos e decolagens e o
controle das pistas de taxi-aereo e patios.
O Trabalho
Controle de rotas
rotas envolvidas. Existem duas ocoasioes em que ocorrerao colisoes: se duas aero-
naves estiverem utilizando a mesma rota ou se duas aeronaves estiverem utilizando
rotas que apresentem interseccao. Considere que duas rotas apresentam interseccao
se uma delas e multipla da outra.
Controle de aproximacao
Dado que o controle de rotas ja foi realizado sera necessario definir qual sera a ordem
das aeronaves a pousar. Para isso devera ser considerada a sequencia de prioridades
abaixo:
CLASSE > ALTITUDE > VELOCIDADE
Assim, define-se que existem apenas duas classes (militar e comercial) e que duas
alttudes sao consideradas iguais se a diferenca entre elas for menor que 2000 pes.
Alem disso, a classe militar tem prioridade frente a classe comercial; quanto menor
a altitude de uma aeronave maior sera a sua prioridade e quanto maior a velocidade
de uma aeronave maior sera a sua prioridade. Considere que cada operacao de pouso
dura 5 minutos.
Controle de pista
Apos o pouso de uma aeronave e necessario que o trafego em solo tambem seja
controlado. Com isso, a segunda parte do consistira em controlar o momento em
que cada aeronave devera encostar em uma plataforma que esta livre. Caso todas
as tres plataformas estejam ocupadas no momento em que uma aeronave pouse, esta
devera esperar no patio de espera ate que alguma das plataformas seja desocupada.
Considere que cada operacao de desembarque dure 30 minutos.
Especificacao
Modulo 1
Essa primeira parte do trabalho consiste em determinar a fila de aeronaves que ira pusar e
as novas rotas das aeronaves que precisam mudar de rota. Para isso, considere que quando
duas aeronaves apresentem chance de colisao a aeronave que apresentar numero de rota
maior devera ser redirecionada para uma rota com numero igual ao primeiro numero primo
maior que a rota de maior numero.
Modulo 2
7.11. TRABALHOS SUGERIDOS 233
Entrada
Exemplo:
0103
2
militar
7100
670
14:30
0307
10
comercial
8200
770
14:29
0200
13
militar
1000
710
14:30
234 CAPITULO 7. APONTADORES
0708
10
comercial
8600
640
14:29
1102
4
militar
9100
700
14:30
Sada
Os dados de sada do programa devem ser exibidos no console. O formato da sada deve
seguir o exemplo abaixo.
Exemplo:
0200
13
15:00
0103
2
15:05
1102
17
15:10
0307
19
15:30
0708
23
15:35
Captulo 8
Arquivos
Autores:
Objetivos:
Definir e apresentar arquivos;
235
236 CAPITULO 8. ARQUIVOS
resolver esse problema, existem as variaveis persistentes, as quais tem seu conteudo armazenado,
e possvel de ser acessado, independentemente do programa que as criou estar na memoria princi-
pal. Variaveis persistentes devem ser armazenadas em algum dispositivo de memoria secundaria,
como discos rgidos, CDs, memoria flash e DVDs, os quais tem a capacidade de manter por
um longo tempo o valor da informacao neles contida, independente do conteudo da memoria
principal do computador, ou mesmo de ele estar ligado. Deve-se ressaltar que o tempo de acesso
e de gravacao das variaveis armazenadas em meio secundario e muito maior que o das variaveis
armazenadas na memoria principal, por isso essa ultima e tao importante.
O conceito por tras das variaveis persistentes e o de arquivo, e esse sera o tema desse
captulo. Por meio de arquivos, essas variaveis podem ser armazenadas e as informacoes que
elas guardam, acessadas e processadas no futuro, tanto pelo programa que as criou quanto por
outros programas.
Por meio de um arquivo binario, as variaveis armazenadas na memoria principal podem ter seus
bytes armazenados num arquivo fsico, sem traducao para caracteres e com correspondencia
de um para um. Isso significa que, usando-se arquivos binarios, torna-se possvel criar um
espelhoda memoria principal, mas salvo em memoria secundaria.
A fim de exemplificar esse conceito na linguagem C, sera citada a gravacao de uma struct,
uma das razoes pela qual arquivos binarios sao tao uteis nessa linguagem. Ja foi estudado que
uma struct e um tipo especial de variavel que armazena outras variaveis. Tome, por exemplo, a
struct
struct pessoa {
char nome[50];
int idade;
float salario;
}
Como guardar essa informacao em um arquivo? Pode-se guarda-la num arquivo texto,
escrevendo-se o nome da pessoa, entao sua idade e finalmente seu salario. Sabendo o formato
como essa informacao foi escrita, o programador pode criar um programa que a le.
De modo alternativo, pode-se armazena-la num arquivo binario usando um comando (que
sera mostrado depois) que trata toda a struct como uma unica variavel. Ela pode ser lida
tambem se usando um comando que a trata da mesma forma. Isso e possvel, pois se sabe o
formato como essa struct foi armazenada no arquivo binario. Esse formato poderia ser, para
uma arquitetura de 32 bits de palavra, na linguagem C, de 50 bytes que armazenam cada uma
das variaveis char, mais 32 bits (4 bytes) que armazenam uma variavel int, e mais 32 bits (4
bytes) que armazenam uma variavel float.
FILE *arq;
8.4.1 Abertura
Apos a declaracao de uma variavel do tipo arquivo, essa deve ser associada a um nome de
arquivo, o qual identifica um arquivo de fato, com correspondencia na memoria secundaria.
Se o arquivo com o nome especificado ja existir, entao o programador tera acesso a seu
conteudo, e podera le-lo e altera-lo. Mas um nome de arquivo que ainda nao existe tambem
pode ser utilizado. Nesse caso, um novo arquivo, em branco, com o nome especificado, sera
criado. Esses detalhes sao resolvidos passando-se as informacoes corretas a funcao responsavel
por abrir o arquivo.
Na linguagem C, essa funcao e a fopen(). Seu prototipo e:
A funcao retorna um ponteiro para FILE, que devera ser recebido pela variavel usada para
manipular o arquivo. O primeiro argumento de fopen(), nomearq, e uma string, e refere-se ao
nome do arquivo que sera aberto. O segundo argumento, modo, tambem uma string, e um
codigo passado a fopen(), responsavel por indicar que tipo de arquivo e operacao sera realizada
sobre esse arquivo. Os codigos sao descritos na tabela 8.1:
Parametro Efeito
r Abre um arquivo-texto para leitura
w Cria um arquivo-texto para escrita
a Abre um arquivo-texto para gravar ao fim dele
r+ Abre um arquivo-texto para leitura/escrita
w+ Cria um arquivo-texto para leitura/escrita
a+ Abre ou cria (se nao existir) um arquivo-texto para ler dele ou gravar ao fim dele
rb Abre um arquivo binario para leitura
wb Cria um arquivo binario para escrita
ab Abre um arquivo binario para gravar ao fim dele
r+b Abre um arquivo binario para leitura/escrita
w+b Cria um arquivo binario para leitura/escrita
a+b Abre ou cria um arquivo binario para gravar ao fim dele
No exemplo 8.1, a seguir, serao criados dois arquivos: o arquivo texto somente escrita.txt,
o qual nao existia antes da execucao de fopen(), e que sera usado somente para gravacao de
informacao (no caso, caracteres); e leio e escrevo.meu, binario, no qual as informacoes poderao
ser lidas e escritas. Note como as variaveis FILE* sao declaradas, inicialmente, e depois recebem
o retorno da funcao fopen().
Um ponto importante a ser ressaltado sobre a funcao fopen() e a avaliacao de seu valor de
retorno. Muitas vezes ocorrem erros na abertura de um arquivo, como, por exemplo, passar para
fopen() o codigo rmas o arquivo com o nome especificado nao existir no disco. Esses erros
podem ser detectados porque a funcao fopen() retornara NULL para indicar que houve falha na
abertura de um arquivo.
No exemplo 8.2, e declarada uma variavel de arquivo. Em seguida, e chamada fopen() para
realizar a abertura de arquivo. Imediatamente depois, e verificado se ele foi aberto corretamente.
Se nao foi, o usuario sera notificado e o programa, encerrado. Esse encerramento e feito com o
240 CAPITULO 8. ARQUIVOS
uso da funcao exit() (definida na biblioteca stdlib.h), a qual finaliza o programa e retorna para
o Sistema Operacional o codigo numerico passado como argumento.
1 FILE * arq_ex3 ;
2 arq_ex3 = fopen ( " alunos . txt " , " r " ) ;
3 if ( arq_ex3 == NULL ) {
4 printf ( " Erro com a abertura de arquivo ! O programa sera abortado ... " ) ;
5 exit (1) ;
6 }
Todas as vezes que fopen() for usada deve-se verificar imediatamente se a abertura de arquivo
ocorreu sem problemas. Essa abordagem evitara muitos problemas posteriores.
Quando um arquivo e aberto, passa a estar associado a ele um indicador de posicao, res-
ponsavel por indicar em qual ponto do arquivo novas informacoes sao lidas ou gravadas (ele pode
ser imaginado como o cursor em um texto sendo editado num editor de texto). No momento em
que um arquivo e aberto, esse indicador estara no comeco do arquivo; e sempre que as variaveis
persistentes do arquivo forem sendo lidas ou gravadas, o indicador de posicao se movimenta,
ficando sempre a frente da ultima variavel lida ou gravada.
8.4.2 Fechamento
Quando um arquivo aberto nao for mais utilizado num programa, ele deve ser fechado. Esse
fechamento desassocia o arquivo fsico, em disco (por exemplo), com a variavel do tipo arquivo
usada para abr-lo. Um arquivo aberto pode ser fechado usando-se uma funcao que execute essa
tarefa e que o receba como argumento.
E muito importante lembrar-se de fechar um arquivo. Arquivos abertos sao recursos que
o sistema operacional deve gerenciar; dessa forma, muitos arquivos abertos significam mais
recursos sendo gerenciados.
Na linguagem C, a funcao fclose() e usada para fechar um arquivo. Seu prototipo e:
8.5.1 Leitura
A linguagem C fornece uma serie de funcoes para leitura de informacoes em arquivos de texto.
Serao mostradas a seguir duas delas: fscanf() e fscanf()
fscanf()
A funcao fscanf() funciona de forma semelhante a scanf(), ja estudada em captulos anteriores.
A diferenca esta no fato de que fscanf() trabalha com arquivos, e nao com a entrada padrao
(normalmente o teclado), caso de scanf()
Seu prototipo e:
1 \ begin { verbatim }
2 # include < stdio .h >
3 # define NOME_ARQUIVO " meu_texto . txt "
4 # define TAM_STR 50
5
6 int main ( void )
7 {
8 int inteiro ;
9 float real ;
10 char str [ TAM_STR ];
11 FILE * arq ;
12
13 if ( !( arq = fopen ( NOME_ARQUIVO , " r " ) ) ) {
242 CAPITULO 8. ARQUIVOS
1145
segunda_linha
45.99
caso lido com o codigo acima, resultaria o valor 1145 na variavel inteiro, segunda linhaem
str e 45.99 na variavel real.
fgets()
Outra funcao de C utilizada para ler informacoes de um arquivo texto e fgets(), que permite ler
strings completas. Seu prototipo e:
Essa funcao le os caracteres do arquivo fp passado como argumento e armazena-os na string str,
tambem passada como argumento, ate que ou length-1 caracteres sejam lidos ou um caractere de
nova linha (\n) seja lido. Se um caractere de nova linha for lido, ele sera armazenado em str. Ja
quando a leitura de caracteres do arquivo finaliza, fgets() encerrara a string str armazenando \0 no
proximo caractere (ou seja, finalizando a string).
Dessa forma, um arquivo texto que contenha os seguintes caracteres: primeira linha\nsegunda
linha, tal como mostrado a seguir,
primeira linha
segunda linha
pode ser lido usando-se duas chamadas a funcoes fgets(). Duas strings serao lidas e gravadas
nas variaveis string: str1 contera primeira linha\n, e str2 contera segunda linha. O exemplo 8.4
mostra como isso pode ser feito:
7 exit (1) ;
8 }
9
10 fgets ( str1 , 50 , arquivo ) ;
11 fgets ( str2 , 40 , arquivo ) ;
Note que o tamanho maximo passado a fgets() corresponde ao tamanho das strings definidas.
8.5.2 Escrita
A linguagem C oferece a funcao fprintf(), que pode ser usada para gravar em arquivos de texto. Ela
sera estudada a seguir.
fprintf()
A funcao fprintf() e semelhante a printf().
Seu prototipo e:
A passagem de parametros e semelhante a de printf(), com o acrescimo de que deve ser colocado
como primeiro argumento o ponteiro para FILE representando o arquivo no qual serao gravadas as
informacoes.
A gravacao de strings pode ser feita sem problemas com o uso de fprintf().
O exemplo 8.5 mostra o uso dessa funcao. Nesse programa, tres variaveis, uma float, uma de
caracteres e uma string, sao criadas e tem valores associados; entao, o arquivo e aberto e elas sao
gravadas la.
547.32
Isso e uma string constante
teste para string
T
8.6.1 Leitura
A linguagem C possui a funcao fread() que pode ser usada para ler um arquivo binario.
fread()
size t fread (void *buffer, size t num bytes, size t count, FILE *fp);
O primeiro parametro dessa funcao, buffer, e um ponteiro para uma regiao de memoria que
recebera as variaveis lidas do arquivo. O numero de bytes a serem lidos e especificado por num bytes;
e essa variavel que informa a funcao qual o tipo de variavel persistente a ser lida, pelo uso do operador
sizeof() (que retorna o tamanho do tipo passado como argumento). O parametro count indica
quantas variaveis do tamanho num bytes deverao ser lidas com essa chamada da funcao fread(). E
fp e um ponteiro para o arquivo de onde serao lidos os valores.
O tipo de retorno da funcao, size t, e definido no arquivo stdio.h, e e aproximadamente igual
a um inteiro sem sinal. Esse retorno pode ser avaliado para verificar se algum erro ocorreu, pois
fread() retornara a quantidade de itens lidos. Esse valor deve ser igual a count; se nao for, entao o
arquivo chegou ao final antes de ler a quantidade solicitada ou um erro ocorreu.
O exemplo 8.6 mostra a leitura de um inteiro, depois de um float, e entao de um caractere, de
um arquivo binario. Considere que o arquivo arq1 ja foi aberto nesse trecho de codigo.
8.6. OPERACOES SOBRE ARQUIVOS BINARIOS 245
1 int inteiro ;
2 float real ;
3 char carac ;
4 /* ... */
5 fwrite (& inteiro , sizeof ( int ) , 1 , arq1 ) ;
6 fwrite (& real , sizeof ( float ) , 1 , arq1 ) ;
7 fwrite (& carac , sizeof ( char ) , 1 , arq1 ) ;
Note que as tres variaveis devem estar gravadas em sequencia no arquivo. Note tambem que
buffer e um ponteiro para as variaveis que armazenarao valores lidos do arquivo.
8.6.2 Escrita
Ja para se realizar a escrita de variaveis persistentes num arquivo binario, a linguagem C oferece a
funcao fwrite().
fwrite()
Seu prototipo e:
size t fwrite (void *buffer, size t num bytes, size t count, FILE *fp);
Essa funcao se assemelha muito com fread(). O parametro buffer e um ponteiro para a variavel
que sera escrita no arquivo. Ja num bytes e usado com o operador sizeof() para informar a funcao
quantos bytes contem o tipo da variavel a ser escrita. count indica quantas variaveis (do tamanho
de num bytes) serao escritas. E, finalmente, fp e o ponteiro para FILE que indica em qual arquivo
devem ser escritas as variaveis.
O exemplo 8.7 mostra um programa completo que gera uma struct, a grava em um arquivo e
depois fecha-o. Entao, esse arquivo e reaberto e essa struct e lida dele (somente para exemplo, pois
os valores da struct ainda estavam corretos). Depois, esses dados lidos serao gravados em um outro
arquivo, texto.
12 TPessoa p ;
13
14 p . nome = " Marcia Maia " ;
15 p . idade = 46;
16 p . sexo = F ;
17
18 if ( !( arq = fopen ( " arquivo_bin . teste " , " wb " ) ) ) {
19 printf ( Erro na abertura de arquivo ! Abortando o programa ...) ;
20 exit (1) ;
21 }
22 fwrite (& p , sizeof ( TPessoa ) , 1 , arq ) ;
23 fclose ( arq ) ;
24
25 if ( !( arq = fopen ( " arquivo_bin . teste " , " rb " ) ) ) {
26 printf ( Erro na abertura de arquivo ! Abortando o programa ...) ;
27 exit (1) ;
28 }
29 fread (& p , sizeof ( TPessoa ) , 1 , arq ) ;
30 fclose ( arq ) ;
31
32 if ( !( arq = fopen ( " arquivo_texto . txt " , " w " ) ) ) {
33 printf ( Erro na abertura de arquivo ! Abortando o programa ...) ;
34 exit (1) ;
35 }
36 fprintf ( arq , " % s \ n % d \ n % c " , p . nome , p . idade , p . sexo ) ;
37 fclose ( arq ) ;
38 }
Pode-se ver que ao final da execucao do programa, arquivo texto.txt contera os dados da pessoa,
e poderao ser lidos assim que o arquivo for aberto por um editor de texto qualquer (fato que nao
ocorre com o arquivo arquivo bin.teste, binario).
As secoes anteriores mostraram as funcoes basicas para se trabalhar com arquivos em C. Porem,
essas funcoes nao sao suficientes para realizar todas as tarefas que arquivos permitem.
Serao apresentadas duas funcoes nessa secao: feof(), usada para verificar se o indicador de
posicao num arquivo chegou ao final dele; e fseek(), usada para posicionar o indicador de posicao
num local especfico do arquivo.
Os exemplos apresentados nessa sessao serao de programas completos e mais complexos dos que
os anteriormente mostrados.
8.7. OUTRAS FUNCOES UTEIS PARA ARQUIVOS 247
8.7.1 feof()
Uma funcao essencial usada para trabalhar com arquivos e feof(). Seu prototipo e:
Essa funcao retornara 0 se o arquivo fp passado como argumento ainda nao chegou ao final.
A verificacao se da pelo indicador de posicao no arquivo. Assim, a funcao feof() e utilizada para
verificar quando um arquivo, tanto de texto quando binario, termina.
No exemplo 8.8, existe um arquivo previamente criado, binario, que armazena muitas variaveis
struct do tipo TFuncionario. O programa abre esse arquivo, le todas as variaveis la presentes (uma
quantia inicialmente desconhecida), e vai gravando-as num arquivo texto.
Note que esse programa usa o tipo TFuncionario como um TAD (tipo abstrato de dados):
existe uma funcao para inicializar o funcionario, e funcoes que operam com um funcionario (lendo,
gravando...). A funcao main(), que usa essas funcoes, nao acessa os campos de uma struct TFun-
cionario. Alias, o programador da funcao main(), que usa o TAD funcionario, nem sequer precisa
saber como ele e implementado!
O programa funciona da seguinte forma: a funcao main() abre os arquivos, e entao, usa a funcao
le func() para ler um funcionario de cada vez do arquivo de leitura (se a leitura do funcionario nao
ocorrer corretamente, a funcao le func() usa a funcao invalida func() para indicar que a leitura
desse funcionario nao foi feita corretamente). Entao, a main() verifica se esse funcionario foi lido
corretamente, usando a funcao func valido(). Se sim, grava-o no arquivo texto, usando a funcao
grava func().
23
24 /* Bloco de codigo responsavel pela correta abertura dos arquivos */
25 if ( !( arqbin = fopen ( ARQUIVO_LEITURA , " rb " ) ) ||
26 !( arqtex = fopen ( ARQUIVO_ESCRITA , " rb " ) ) ) {
27 printf ( " Erro na abertura de arquivos ! Abortando ... " ) ;
28 exit (1) ;
29 }
30
31 /* Bloco de codigo que contem o loop while , que usa a func~ a o feof () para
32 ler o arquivo */
33 while ( ! feof ( arqbin ) ) {
34 f = le_func ( arqbin ) ;
35 if ( func_valido ( f ) ) {
36 grava_func (f , arqtex ) ;
37 } else {
38 printf ( " Erro na leitura de funcionarios . Abortando ... " ) ;
39 exit (1) ;
40 }
41 }
42 }
43
44 TFuncionario inicializa_func ( void )
45 {
46 TFuncionario f ;
47 f . matricula = 0;
48 return ( f ) ;
49 }
50
51 TFuncionario le_func ( FILE * arqbin )
52 {
53 TFuncionario f ;
54
55 if ( ( fread (& f , sizeof ( TFuncionario ) , 1 , arqbin ) ) != 1) {
56 f = invalida_func () ;
57 }
58
59 return ( f ) ;
60 }
61
62 TFuncionario invalida_func ( void )
63 {
64 TFuncionario f ;
65 f . matricula = -1;
66 return ( f ) ;
67 }
68
69
70 int func_valido ( TFuncionario f )
71 {
72 if ( f . matricula < 0) {
73 return (0) ;
8.7. OUTRAS FUNCOES UTEIS PARA ARQUIVOS 249
74 }
75 else {
76 return (1) ;
77 }
78 }
79
80 void grava_func ( TFuncionario f , FILE * arqtex )
81 {
82 fprintf ( arqtex , Nome : % s \ nSexo : % c \ nIdade : % d \ nMatricula : % d \ n
83 Salario : % f \ n \n , f . nome , f . sexo , f . idade , f . matricula , f . salario ) ;
84 }
8.7.2 fseek()
Muitas vezes, deseja-se ter controle sobre o indicador de posicao num arquivo. Ao se saber, por
exemplo, qual o ndice de uma estrutura especfica ja gravada, do total de todas as estruturas que
fazem parte de um arquivo binario, pode-se buscar essa estrutura desejada sem ter que carregar para
a memoria todas as outras. Isso pode ser feito posicionando-se o indicador de posicao no arquivo e
lendo somente a proxima estrutura (que sera a desejada).
A funcao em C que permite fazer isso e fseek(). Seu prototipo e:
A funcao retornara 0 se for bem-sucedida; caso contrario, retornara diferente de 0. Seus argu-
mentos sao: fp, um ponteiro para arquivo ja aberto por fopen(); numbytes, um numero que indica
quantos bytes a partir do parametro origin estara o indicador de posicao; e, finalmente, origin sera
uma das tres macros mostradas abaixo (que estao definidas no arquivo STDIO.H):
Posicao Parametro
Incio do arquivo SEEK TEST
Posicao atual SEEK CUR
Final do arquivo SEEK END
Assim, para se posicionar duas variavel int a frente da posicao inicial do arquivo arq (ou seja,
pronto para ler a terceira variavel int armazenada), faca:
O exemplo 8.9 mostra a funcao obtem func posicao(). Essa funcao e usada para ler structs
TFuncionario (definidas no Exemplo 8.8) contidas no arquivo binario (tambem definido no exemplo)
passando-se o ndice do funcionario que se deseja ler. Ela recebe um ponteiro para o arquivo binario
250 CAPITULO 8. ARQUIVOS
de leitura, ja aberto com fopen(), de onde tentara ler a struct de ndice i, um parametro inteiro
passado. Assim, se i=1, o retorno da funcao sera a primeira struct; se i=3, sera a terceira; etc.
Ela usara a funcao invalida func(), tambem definida nesse exemplo, para indicar que houve erro na
leitura do funcionario especificado.
aceitavel pelo tempo que isso demanda: o acesso ao disco se da na ordem de milissegundos, muito
superior aos tempos envolvidos nas operacoes da memoria e do processador.
Por esse motivo, um segundo arquivo estara tambem associado a cada variavel TDicionario: um
arquivo de ndices. Esse arquivo contera duplas (chave, ndice), em que chave corresponde as mesmas
chaves includas no arquivo de dados, e ndice indica o ndice (numerico) dessa chave no arquivo de
dados.
Segue exemplo de uma variavel TDicionario denominada meu dic, e associada a arquivos com
nome meu dicionario. Note que ambos os arquivos sao binarios, assim, o que e mostrado abaixo e
apenas o aspecto deles eles nao poderiam ser lidos diretamente de um editor de texto convencional.
Esprito Santo
estado brasileiro localizado na regi~ ao Sudeste
C
linguagem de programac~
ao de nvel medio
cachorro
mamfero considerado "o melhor amigo do homem"
Esprito Santo
0
C
1
cachorro
2
O arquivo de ndices, por ser consideravelmente menor que o de dados, podera ser carregado todo
para a memoria e entao acessado, a fim de realizar-se uma busca. Dessa forma, pode-se descobrir
que cachorro e, sim, uma entrada, e esta no ndice 2 (terceira entrada) do arquivo de dados.
As entradas do arquivo de dados serao structs com dois campos strings, e as do arquivo de ndices
structs com um campo string e um campo int. Uma variavel TDicionario sera uma struct com campos
que indicam o nome dos seus dois arquivos associados, um campo que armazena a matriz de ndices
em memoria, e um campo que guarda o total de entradas armazenadas no dicionario.
As operacoes que o TAD dicionario aceitara sao: inicializacao de um novo dicionario, abertura
de um dicionario ja existente, busca de uma palavra e acrescimo de uma palavra. Seguem abaixo
definicoes dessas funcoes:
Ao chamar essa funcao, o usuario passara o nome do dicionario como argumento (uma string).
Entao os dois arquivos serao criados; seus nome serao a string nome do dicionario acrescida de
252 CAPITULO 8. ARQUIVOS
dados, para o caso do arquivo de dados, e indice, para o arquivo de ndices. Na memoria
ficara armazenada uma tabela de ndices (ou seja, um vetor de structs), inicialmente vazia.
Essa funcao e usada para abrir um dicionario ja existente. Sua chamada acarreta na abertura do
arquivo de ndices (com nome indicado por nome do dicionario) e seu carregamento para a memoria;
apos isso, o arquivo de ndices sera fechado.
O uso dessa funcao acarreta na busca, na matriz de ndices em memoria (indicada por dic), da
string indicada por palavra. Se for encontrada, entao se sabera seu ndice; o arquivo de ndice entao
sera aberto, o indicador de posicao sera posicionado nesse ndice indicado, e a informacao desejada (o
valor correspondente a essa chave) sera lida e armazenada no argumento retorno. A funcao retornara
1 para indicar que a palavra foi encontrada e 0 senao.
Essa funcao sera usada para inserir, no dicionario dic, a palavra chave com a sua descricao valor.
Inicialmente uma busca pela chave devera ser realizada (na tabela de ndices em memoria); se ela
nao existir, a sim podera ser inserida. Nesse caso, o arquivo de dados e aberto, e a chave e o
valor sao armazenados ao fim dele; ele e fechado apos isso. A matriz de ndices em memoria devera
tambem ser modificada, com o acrescimo, ao fim dela, da chave e do seu ndice no arquivo de dados
(e possvel saber o ndice observando a quantidade de entradas ja armazenadas, indicada por dic). O
arquivo de ndices devera entao ser aberto, a matriz de ndices em memoria (ja atualizada) gravada
nele, e logo em seguida fechado.
14 } entrada_dic ;
15
16 /* duplas ( chave , indice ) */
17 typedef struct {
18 char chave [ TAM_CHAVE ];
19 int indice ;
20 } indice_dic ;
21
22 /* definicao da variavel do TAD dicionario */
23 typedef struct {
24 char nome_arq_dados [ TAM_NOME_ARQ ];
25 char nome_arq_indices [ TAM_NOME_ARQ ];
26 indice_dic matriz_indices [ MAX_ENTRADAS ];
27 int tam ;
28 } TDicionario ;
29
30 /* funcao que inicializa um novo dicionario */
31 TDicionario inicializa_dic ( char * nome_do_dicionario )
32 {
33 TDicionario dic ;
34 FILE * arq ;
35
36 /* acrescenta " _indice " ao nome_do_dicionario */
37 strcpy ( dic . nome_arq_indices , nome_do_dicionario ) ;
38 strcat ( dic . nome_arq_indices , " _indice " ) ;
39
40 /* criacao do arquivo de indices */
41 if ( ( arq = fopen ( dic . nome_arq_indices , " wb " ) ) == NULL ) {
42 printf ( " Erro na criacao de arquivos ! Abortando ... " ) ;
43 exit (1) ;
44 }
45 fclose ( arq ) ;
46
47 /* acrescenta " _dados " ao nome_do_dicionario */
48 strcpy ( dic . nome_arq_dados , nome_do_dicionario ) ;
49 strcat ( dic . nome_arq_dados , " _dados " ) ;
50
51 /* criacao do arquivo de dados */
52 if ( ( arq = fopen ( dic . nome_arq_dados , " wb " ) ) == NULL ) {
53 printf ( " Erro na criacao de arquivos ! Abortando ... " ) ;
54 exit (1) ;
55 }
56 fclose ( arq ) ;
57
58 dic . tam = 0;
59
60 return ( dic ) ;
61 }
62
63 /* funcao que carrega um dicionario */
64 void carrega_dic ( TDicionario * dic , char * nome_do_dicionario )
254 CAPITULO 8. ARQUIVOS
65 {
66 FILE * arq ;
67 int i ;
68
69 /* acrescenta " _dados " ao nome_do_dicionario */
70 strcpy ( dic - > nome_arq_dados , nome_do_dicionario ) ;
71 strcat ( dic - > nome_arq_dados , " _dados " ) ;
72
73 /* acrescenta " _indice " ao nome_do_dicionario */
74 strcpy ( dic - > nome_arq_indices , nome_do_dicionario ) ;
75 strcat ( dic - > nome_arq_indices , " _indice " ) ;
76
77 /* abertura do arquivo de indices */
78 if ( ( arq = fopen ( dic - > nome_arq_indices , " rb " ) ) == NULL ) {
79 printf ( " Erro na abertura de arquivos ! Abortando ... " ) ;
80 exit (1) ;
81 }
82
83 /* leitura e contagem do numero de entradas no arquivo de indice */
84 dic - > tam = 0;
85 for ( i =0; ! feof ( arq ) ; i ++) {
86 fread (& dic - > matriz_indices [ i ] , sizeof ( indice_dic ) , 1 , arq ) ;
87 dic - > tam ++;
88 }
89 }
90
91 /* funcao que busca uma chave no dicionario passado . Se encontrar
92 retornara 1 e retornara seu valor no argumento retorno */
93 int busca_dic ( TDicionario * dic , char * chave , char * retorno )
94 {
95 int i ;
96 FILE * arq ;
97
98 entrada_dic entrada ;
99
100 /* procura na tabela de indices em memoria a chave */
101 for ( i =0; i < dic - > tam ; i ++) {
102 if (! strcmp ( dic - > matriz_indices [ i ]. chave , chave ) ) {
103 /* se estou aqui , entao encontrei a palavra ! */
104
105 if ( ( arq = fopen ( dic - > nome_arq_dados , " rb " ) ) == NULL ) {
106 printf ( " Erro na abertura de arquivos ! Abortando ... " ) ;
107 exit (1) ;
108 }
109 /* posiciona , le e retorna o valor da chave */
110 fseek ( arq , i * sizeof ( entrada_dic ) , SEEK_SET ) ;
111 fread (& entrada , sizeof ( entrada_dic ) , 1 , arq ) ;
112 strcpy ( retorno , entrada . valor ) ;
113 fclose ( arq ) ;
114 return 1;
115 }
8.8. EXERCICIOS RESOLVIDOS 255
116 }
117 /* se cheguei ate aqui , entao nao encontrei a palavra */
118 return 0;
119 }
120
121 /* funcao que insere , no dicionario , a chave ( palavra ) passada
122 e seu valor . Retorna 1 se inserir , 0 senao */
123 int insere_dic ( TDicionario * dic , char * chave , char * valor )
124 {
125 char pega_valor [ TAM_VALOR ];
126 FILE * arq ;
127 entrada_dic entrada ;
128 indice_dic chave_ind ;
129
130 if ( ! busca_dic ( dic , chave , pega_valor ) ) {
131 /* Se a chave nao existe no dic . , anexo ao arquivo de dados */
132
133 if ( ( arq = fopen ( dic - > nome_arq_dados , " ab " ) ) == NULL ) {
134 printf ( " Erro na abertura de arquivos ! Abortando ... " ) ;
135 exit (1) ;
136 }
137
138 strcpy ( entrada . chave , chave ) ;
139 strcpy ( entrada . valor , valor ) ;
140
141 fwrite (& entrada , sizeof ( entrada_dic ) , 1 , arq ) ;
142 fclose ( arq ) ;
143
144 /* insere ao final da tabela e arquivo de indices */
145
146 strcpy ( dic - > matriz_indices [ dic - > tam ]. chave , chave ) ;
147 dic - > matriz_indices [ dic - > tam ]. indice = dic - > tam ;
148 dic - > tam ++;
149
150 if ( ( arq = fopen ( dic - > nome_arq_indices , " ab " ) ) == NULL ) {
151 printf ( " Erro na abertura de arquivos ! Abortando ... " ) ;
152 exit (1) ;
153 }
154
155 strcpy ( chave_ind . chave , chave ) ;
156 chave_ind . indice = dic - > tam ;
157 fwrite (& chave_ind , sizeof ( indice_dic ) , 1 , arq ) ;
158 fclose ( arq ) ;
159
160 return 1;
161 }
162 else printf ( " A chave passada ja existe no dicionario !\ n " ) ;
163 return 0;
164 }
165
166 int main ()
256 CAPITULO 8. ARQUIVOS
167 {
168 TDicionario dic , outro_dic ;
169 entrada_dic entrada ;
170
171 dic = inicializa_dic ( " meu_dicionario " ) ;
172
173 strcpy ( entrada . chave , " Espirito Santo " ) ;
174 strcpy ( entrada . valor , " estado brasileiro localizado na regiao Sudeste " ) ;
175 insere_dic (& dic , entrada . chave , entrada . valor ) ;
176
177 strcpy ( entrada . chave , " C " ) ;
178 strcpy ( entrada . valor , " linguagem de programacao de nivel medio " ) ;
179 insere_dic (& dic , entrada . chave , entrada . valor ) ;
180
181 carrega_dic (& outro_dic , " meu_dicionario " ) ;
182 strcpy ( entrada . chave , " C " ) ;
183 strcpy ( entrada . valor , " linguagem de programacao de nivel medio " ) ;
184 insere_dic (& outro_dic , entrada . chave , entrada . valor ) ;
185 }
de ndices em memoria, para entao ser descoberto seu ndice no arquivo de dados, e a dupla (chave,
informacao) desejada finalmente ser carregada para memoria.
8.9 Resumo
Variaveis transientes sao armazenadas na memoria principal. Tem tempo de acesso rapido, mas
duracao limitada: so existem enquanto o programa que as esta utilizando estiver na memoria.
Variaveis persistentes nao perdem suas informacoes quando o programa que as criou nao esta
mais em memoria, pois estao armazenadas em meio secundario: disco rgido, memoria flash,
disquete, CD...
Um arquivo deve ser aberto para poder ser utilizado, e apos essa utilizacao, deve ser fechado;
As variaveis persistentes podem ser escritas e lidas dos arquivos. Existem funcoes especficas
para essas tarefas, algumas designadas para arquivos binarios e outras para arquivos texto;
A linguagem C oferece uma serie de funcoes para lidar com arquivos, e esse captulo apre-
sentou algumas, como fopen(), usada para abrir arquivos, e fread(), usada para ler variaveis
persistentes de arquivos binarios.
8.10 Exerccios
1. Escreva um programa em C que abra um arquivo texto e que conte a quantidade de caracteres
armazenados nele. Imprima o numero na tela. O programa deve solicitar ao usuario que digite
o nome do arquivo.
3. Considere um arquivo texto que armazene numeros em ponto flutuante em cada uma de suas
linhas. Ou seja, o arquivo inicia com uma quantidade de um ou mais caracteres representando
numeros inteiros, entao segue um caractere de ponto (.) e mais alguns inteiros; em seguida,
uma quebra de linha; na proxima linha esse formato prossegue, ate que, no fim da ultima linha,
nao ocorre quebra de linha. Escreva um programa em C que determine o valor maximo, o valor
mnimo e a media desses valores armazenados no arquivo. Imprima esses valores na tela.
4. Considere o programa escrito para a questao anterior. Exiba na tela, agora, o valor n corres-
pondente a quantidade de numeros reais contidos no arquivo e o valor maximo, mnimo e a
258 CAPITULO 8. ARQUIVOS
media calculados anteriormente, porem agora divididos por n. Depois, gere um novo arquivo
que contenha cada um dos numeros reais do arquivo divididos por n, um por linha. Procure
reutilizar, nessa questao, o maior numero possvel das funcoes que voce tenha construdo para
a questao anterior. Modularizar seu codigo (usar funcoes) e uma forma de organizacao e de
poupar trabalho duplicado.
5. Considere um arquivo texto que armazene caracteres variados, ou seja, um texto digitado.
Escreva um programa que o leia e gere um novo arquivo que contenha somente as letras do
arquivo original, na ordem em que la aparecem (ou seja, caracteres de A-Z ou a-z).
6. Para um arquivo do mesmo tipo do da questao anterior (que armazene caracteres variados),
escreva um programa em C que determine a media dos comprimentos de todas as palavras
que se encontram nele. Entende-se por palavra um conjunto de caracteres de letras que esta
separado de outros conjuntos de caracteres no arquivo por um (ou mais) caractere de espaco
em branco ( , tabulacoes ou quebra de linha); e seu comprimento sera a quantidade de
caracteres que o formam.
7. Considere um arquivo texto como o usado na questao anterior. Faca um programa que o leia
e que permita ao usuario consultar uma das linhas do arquivo, solicitando a ele que informe
o ndice n dessa linha. O programa deve imprimir a linha especificada ou a mensagem de que
ela nao existe.
8. Escreva um programa em C que receba via teclado o nome de um arquivo texto e uma palavra.
O programa deve imprimir todas as linhas que possuem essa palavra. Dica: procure usar
algumas funcoes que voce tenha construdo para a questao anterior; provavelmente, algumas
delas poderao ser reaproveitadas.
9. Escreva um programa em C que receba via teclado o nome de um arquivo texto. O programa
deve solicitar ao usuario que digite o ndice da linha inicial e da linha final, e o programa deve
imprimi-las e todas as linhas entre elas. Se o ndice superior de linhas nao existe, esse erro deve
ser informado ao usuario (mas as linhas existentes devem, ainda, ser impressas). Novamente,
procure usar algumas funcoes que voce tenha escrito para as questoes anteriores.
10. Considere, agora, os numeros inteiros armazenados num arquivo texto como os usados nas
questoes anteriores. Entende-se por um numero inteiro nesse arquivo um conjunto de caracteres
representando numeros inteiros (ou seja, no intervalo 0-9) separados de outros conjuntos de
caracteres por um (ou mais) caractere de espaco em branco ( , tabulacoes ou quebras de
linha). Escreva um programa em C que produza dois arquivos texto: o primeiro com os numeros
pares da sequencia original e o segundo com os numeros impares. Os arquivos de sada devem
conter um numero por linha.
8.10. EXERCICIOS 259
11. Escreva um programa em C que leia um texto fornecido pelo usuario via teclado e o armazene
em um arquivo. O fim da entrada de texto e sinalizada por um ponto no incio de uma nova
linha. O ponto utilizado para marcar o fim da entrada de texto nao deve aparecer no arquivo
gerado pelo programa.
12. Considere um arquivo que apresente o nome de um aluno na primeira linha; na seguinte a
nota de sua primeira prova; e na seguinte a nota de sua segunda prova. Essas 3 linhas de
informacoes se repetem no total de alunos no arquivo de entrada. Faca um programa que
imprima os nomes de todos os alunos que tem a media das duas notas menor que 7.0.
13. Escreva um programa que leia um arquivo texto contendo linhas de dados. Em cada linha do
arquivo ha o nome de um aluno e duas notas. Esses dados estao separados por ponto e vrgula.
Existe um ponto e vrgula no final de cada linha. O programa deve ler esses dados e imprimir
os valores lidos, a media das duas notas, e se o aluno foi aprovado ou nao (com nota maior ou
igual a 5).
14. Escreva um programa em C que solicita ao usuario a digitacao de um nome de arquivo. Esse
arquivo sera criado e o usuario informara numeros em ponto flutuante, que serao gravados no
arquivo. O arquivo sera um arquivo binario. Considere que ele pode armazenar no maximo 50
valores, mas que o usuario pode encerrar a entrada de novos valores com a digitacao de um
numero negativo.
15. Escreva um programa em C que receba o nome de um arquivo binario, o qual armazena numeros
em ponto flutuante, no mesmo formato que na questao anterior. O programa deve ler todos
esses valores e grava-los na memoria; entao, ordena-los, e regrava-los num arquivo texto, com
um numero por linha.
16. Escreva um programa em C que receba via teclado o nome de um arquivo binario. Esse arquivo
armazenara nome, idade e sexo de pessoas, como no TAD TFuncionario. O programa deve
solicitar ao usuario a digitacao desses dados e armazena-los no arquivo depois. O programa
termina quando o usuario digitar enter na entrada do nome, ou seja, informar um nome vazio.
17. Escreva um programa em C que receba via teclado o nome do arquivo binario da questao
anterior. Entao, ele deve abrir o arquivo. Deve existir um menu que ofereca ao usuario a
possibilidade de poder: 1) imprimir (na tela) todos os registros la contidos; 2) acessar os
dados de uma pessoa passando o ndice dela no arquivo (1 para a primeira pessoa no arquivo,
5 para a quinta pessoa no arquivo...); 3) o programa deve informar a quantidade total de
homens e mulheres armazenadas no arquivo; 4) O programa deve permitir ao usuario digitar
novas pessoas. Esses novos dados digitados devem ser gravados no arquivo (e posteriormente
descarregados da memoria) somente quando o usuario escolher, no menu, a opcao para sair
do programa.
260 CAPITULO 8. ARQUIVOS
[1] BRIAN KERNIGHAN and DENNIS RITCHIE. The C Programming Language. second edition,
1988.
261