Sei sulla pagina 1di 123

Programao de Computadores em C

Primeira edio

Programao de Computadores em C Primeira edio

Carlos Camaro Anolan Milans

Universidade Federal de Minas Gerais Doutor em Cincia da Computao pela Universidade de Manchester, Inglaterra

Universidade Federal de Minas Gerais Doutora em Cincia da Computao pela PUC-Rio

Luclia Figueiredo

Universidade Federal de Ouro Preto Doutora em Cincia da Computao pela UFMG

Direitos exclusivos Copyright c 2011 by Carlos Camaro, Anolan Milans e Luclia Figueiredo permitida a duplicao ou reproduo deste volume, no todo ou em parte, sob quaisquer formas ou por quaisquer meios (eletrnico, mecnico, gravao, fotocpia, distribuio na Web ou outros), desde que seja para ns no comerciais.

Sumrio
Prefcio 1 Computadores e Programas
1.1 1.2 1.3 1.4 1.5 1.6 Computadores e Algoritmos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algoritmo e Programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funcionamento e Organizao de Computadores . . . . . . . . . . . . . . . . . . . Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Notas Bibliogrcas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

vii 1
1 1 2 6 10 11

O Paradigma Imperativo de Programao


2.1 2.2 2.3 2.4 2.5 2.6 Varivel e Atribuio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Expresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programando em C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Notas Bibliogrcas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13
14 15 17 19 21 22

Programando em C
3.1 3.2 Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25
28 29

Escolha condicional
4.1 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33
34

Recurso e Iterao
5.1 5.2 5.3 5.4 5.5 Repetio Fatorial 5.4.1 5.5.1 5.5.2 5.5.3 5.5.4 5.6 5.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Multiplicao e Exponenciao

35
35 36 41 42 45 45 46 48 49 49 49 63

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

Obtendo Valores com Processos Iterativos . . . . . . . . . . . . . . . . . . . . . . . No-terminao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Denies Recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Correo e Entendimento de Programas

Comandos de Repetio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Semntica Axiomtica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Valores Compostos: Parte I


6.1 Arranjos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 Declarao e Criao de Arranjos . . . . . . . . . . . . . . . . . . . . . . . . Arranjos criados dinamicamente Operaes Comuns em Arranjos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exemplo de Uso de Arranjo Criado Dinamicamente

69
69 70 70 71 72 73

Arranjos de arranjos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

vi

SUMRIO

6.1.6 6.1.7 6.2 6.3

Inicializao de Arranjos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

74 74 75 77 79 79 79 80 81 81 82 85 87 89 89 89 90 94 94

Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ponteiros 6.3.1 6.3.2 Operaes de soma e subtrao de valores inteiros a ponteiros . . . . . . . . Ponteiros e arranjos

6.4

Cadeias de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 Converso de cadeia de caracteres para valor numrico . . . . . . . . . . . . Converso para cadeia de caracteres . . . . . . . . . . . . . . . . . . . . . . Passando valores para a funo main . . . . . . . . . . . . . . . . . . . . . . Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Declaraes de tipos com Ponteiros para registros

6.5

Registros 6.5.1 6.5.2 6.5.3

typedef

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

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

Estruturas de dados encadeadas

6.6 6.7 6.8

Exerccios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Notas Bibliogrcas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Exerccios
7.1 7.2 7.3 7.4 7.5 ENCOTEL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PAPRIMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ENERGIA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CIRCUITO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . POLEPOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95
95 96 101 101 101

A Anexo: Por que a linguagem C B Tipos bsicos em C


B.1 B.2 Nmeros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.1.1 Consequncias de uma representao nita . . . . . . . . . . . . . . . . . . Caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

105 107
107 108 108

C Programas e Bibliotecas

111

Prefcio
Este livro se prope acompanhar voc no incio do longo caminho que leva ao desenvolvimento do raciocnio abstrato necesrio para construir bons algoritmos e programas. Para isto, o leitor ser introduzido nos conceitos bsicos de programao, de forma que posteriormente possa asimilar contedos mais avanados.

Contedo e Organizao do Livro


Este livro foi concebido para ser utilizado como texto didtico em cursos introdutrios de programao de computadores, de nvel universitrio. O contedo do livro no pressupe qualquer conhecimento ou experincia prvia do leitor em programao, ou na rea de computao em geral, requerendo apenas conhecimentos bsicos de matemtica, usualmente abordados nos cursos de primeiro e segundo graus. O livro adota a linguagem de programao discusso sobre essa escolha). Como estudaremos daqui a pouco, a linguagem C uma linguagem imperativa. Uma viso geral sobre os conceitos bsicos da programao imperativa apresentada inicialmente, com o objetivo de favorecer uma compreenso global sobre o signicado e o propsito dos conceitos empregados nesse estilo de programao, facilitando assim um melhor entendimento da aplicao dos mesmos na construo de programas. Cada um desses conceitos abordado mais detalhadamante em captulos subseqentes, por meio de exemplos ilustrativos e exerccios. Alm desses conceitos bsicos, o livro aborda tambm, de maneira introdutria, os seguintes tpicos adicionais: entrada e sada de dados em arquivos, e manipulao de estruturas de dados como arranjos e listas encadeadas.

(veja no anexo A uma

Recursos Adicionais
Uma pgina na Internet associada a este livro pode ser encontrada no endereo:

http://www.dcc.ufmg.br/camarao/ipcc
O texto desse livro e os cdigos da maioria dos programas apresentados no livro como exemplos encontram-se disponveis nesta pgina. Outros recursos disponveis incluem sugestes de exerccios adicionais e projetos de programao, transparncias para uso em cursos baseados neste livro e referncias para outras pginas da Internet que contm informaes sobre a linguagem ferramentas para programao nessa linguagem. H diversas pginas na Web com informaes sobre a linguagem como cursos sobre introduo a prgramao em

ou sobre

C,

disponveis na Web, assim

dentre os quais citamos: Pginas Wiki, criadas pela prpria coser encon-

http://pt.wikibooks.org/wiki/Programar_em_C:
trada em

munidade de usurios da Web, sobre programao em

C. A verso em ingls pode http://en.wikipedia.org/wiki/C_(programming_language).

http://www.ead.cpdee.ufmg.br/cursos/C: Material usado no curso de introduo gramao em C ministrado no Departamento de Engenharia Eltrica da UFMG. http://cm.bell-labs.com/cm/cs/cbook/:
& Dennis M. Ritchie, Prentice Hall, 1988.

a pro-

The C Programming Language , B. Kernighan

viii

Prefcio

http://publications.gbdirect.co.uk/c_book/:
Addison Wesley em 1991.

Pgina com a verso gratuita da segunda

edio do livro The C Book , de Mike Banahan, Declan Brady e Mark Doran, publicado pela

http://www.cyberdiem.com/vin/learn.html: http://c-faq.com/index.html:
(em ingls).

Learn C/C++ today , de V. Carpenter. Uma

coleo de referncias e tutoriais sobre as linguagens C e C++ disponveis na Internet. Perguntas frequentes sobre a linguagem

C, e suas respostas

http://cm.bell-labs.com/cm/cs/who/dmr/chist.html:
guage", Dennis M. Ritchie (Janeiro de 1993).

"The Development of the C Lan-

http://www.livinginternet.com/i/iw_unix_c.htm:
guage", Stewart, Bill (Janeiro de 2000).

"History of the C Programming Lan-

http://www.cs.ucr.edu/ nxiao/cs10/errors.htm:
in C". Livros adicionais sobre a linguagem

"10 Common Programming Mistakes

incluem:

A linguagem de programao padro ANSI C . B. Kernighan & D.C. Ritchie. Editora Campus,
1990.

C - completo e total. H. Schildt. Editora McGraw-Hill, 1990.a C: A Reference Manual , Samuel P. Harbison & Guy L. Steele, quinta edio, Prentice Hall,
2002.

C Programming: A Modern Approach , K.N. King, Norton, 2008.

E por ltimo, no perca:

http://www.youtube.com/watch?v=XHosLhPEN3k

Captulo 1

Computadores e Programas
1.1 Computadores e Algoritmos
Computadores so empregados atualmente nas mais diversas atividades, como edio e composio de textos, sons e imagens, mecanizao e automatizao de diversas tarefas, simulao dos mais variados fenmenos e transferncia de grandes volumes de informao entre locais possivelmente muito distantes. Seu uso em atividades cientcas permitiu transpor inmeras fronteiras, assim como criar novas e desaantes reas de investigao. Como pode-se explicar to grande impacto? O que diferencia um computador de uma ferramenta qualquer? Na realidade, o conjunto das operaes bsicas executadas por um computador to simples quanto o de uma pequena calculadora. A grande distino de um computador, responsvel por sua enorme versatilidade, est no fato dele ser programvel. Computadores podem realizar tarefas complexas pela combinao de suas operaes bsicas simples. Mas, como isso possvel? Como podemos descrever para o computador a tarefa que queremos que ele execute? Para que um computador possa executar qualquer tarefa, preciso que lhe seja fornecida uma descrio, em linguagem apropriada, de como essa tarefa deve ser realizada. Tal descrio chamada de programa e a linguagem usada para essa descrio, de linguagem de programao . A idia ou processo que esse programa representa chamada de algoritmo .

1.2 Algoritmo e Programa


Algoritmo e programa no so noes peculiares computao. Um algoritmo consiste simplesmente em uma descrio nita de como realizar uma tarefa ou resolver um problema. descrio deve ser composta de operaes executveis. Essa Cozinhar, montar mveis ou briquedos,

realizar clculos matemticos ou tocar um instrumento musical, so exemplos de tarefas que podem ser executadas usando um algoritmo. Receitas de cozinha, instrues de montagem, regras de clculo e partituras musicais so exemplos de programas (representaes de algoritmos) para realizar essas tarefas. A distino entre algoritmo e programa similar distino existente, por exemplo, entre nmero e numeral. Essa distino muitas vezes no se faz perceber, por se tornar dispensvel. Por exemplo, no escrevemos nmero representado por 7 ou nmero denotado por 7, mas simplesmente nmero 7. til saber, no entanto, que podem existir diversas denotaes para um mesmo algoritmo, assim como um nmero pode ser escrito de diferentes maneiras, tais como:

VII

sete

seven

|||||||

Embora seja capaz de executar apenas um pequeno nmero de operaes bsicas bastante simples, um computador pode ser usado, em princpio, para resolver qualquer problema cuja soluo possa ser obtida por meio de um algoritmo. O segredo que um computador prov tambm um conjunto de instrues para a combinao dessas operaes que, embora tambm reduzido, suciente para expressar qualquer algoritmo. Essa tese, de que o conjunto de operaes bsicas e instrues de um computador suciente para expressar qualquer algoritmo, constitui um resultado

Computadores e Programas

Figura 1.1: Organizao bsica de um computador

fundamental das primeiras pesquisas tericas em computao, as quais lanaram as bases para a cincia da computao e para a construo dos primeiros computadores.

Como construir algoritmos para a soluo de problemas e como expressar tais algoritmos de modo adequado usando uma linguagem de programao constituem os temas centrais deste livro. Entretanto, faremos agora um pequeno parntese para estudar brevemente as bases da organizao e o funcionamento de um sistema de computao. Esse conhecimento nos permitir entender como o computador executa os nossos programas.

1.3 Funcionamento e Organizao de Computadores


A atual tecnologia de construo de computadores baseada em dispositivos eletrnicos que so capazes de distinguir, com preciso, entre dois estados diferentes de um sinal eltrico, caracterizados pelos smbolos

1.

Devido a essa caracterstica, dados e operaes so representados, em um

computador, em uma linguagem que tem apenas esses dois smbolos, isto , uma linguagem binria. Cada um desses smbolos comumente chamado de bit .

Apesar do extraordinrio avano da atual tecnologia de construo de computadores, todo computador moderno mantm uma organizao bsica semelhante . Essa organizao mostrada na Figura 1.1 e seus componentes principais so descritos brevemente a seguir. O processador , tambm chamado de unidade central de processamento (do ingls CPU  Central Processing Unit), o componente do computador que executa as instrues de um programa, expresso em uma linguagem que ele pode entender. Durante a execuo de um programa, o processador l a instruo corrente, executa a operao especicada nessa instruo e determina qual a prxima instruo do programa que deve ser executada. Isso se repete at a execuo de uma instruo que indica o trmino do programa. Muitas dessas instrues precisam de um lugar de onde tirar os operandos e onde armazenar temporariamente os resultados. Esse lugar chamado de registrador, e podemos v-lo como um conjunto de caixinhas etiquetadas onde guardamos dados (sequencias de bits que representam um nmero, um caracter, ou outro valor),

um de cada vez.

Outro componente do computador a memria principal , em geral chamada simplesmente de memria , ou RAM (do ingls Random Access Memory). A memria usada para armazenar os programas a serem executados pelo computador e os dados manipulados por esses programas. Essa caracterstica de utilizar um nico dispositivo de memria para armazenar tanto programas quanto dados, peculiar a todos os computadores modernos, distintiva da chamada arquitetura

de von Neumann , assim denominada em homenagem ao pesquisador alemo que originalmente


publicou essa arquitetura, em 1946. A memria do computador consiste em uma seqncia nita de unidades de armazenamento de dados, cada qual identicada pelo seu endereo , isto , por um nmero inteiro no-negativo que

1 Veja as Notas Bibliogrcas 2 do ingls binary digit.

includas no nal deste captulo.

1.3 Funcionamento e Organizao de Computadores

corresponde sua posio nessa seqncia. Cada unidade de armazenamento de dados da memria comumente chamada de uma palavra . Uma palavra de memria usualmente composta de um pequeno nmero (em geral, 4 ou 8) de bytes  cada byte armazena uma seqncia de 8 bits. O outro grupo de componentes do computador constitudo pelos seus dispositivos de entrada

e sada , tambm chamados de dispositivos perifricos (ou apenas de perifricos ). Os perifricos


so usados para a comunicao de dados entre o computador e o mundo externo. O teclado e o

mouse so exemplos de dispositivos de entrada. A tela, ou monitor, e a impressora so exemplos de


dispositivos de sada. Alguns dispositivos, como discos e pendrives, constituem dispositivos tanto de entrada quanto de sada de dados. A linguagem constituda pelas instrues que podem ser diretamente executadas pelo computador, representadas na forma de seqncias de bits, chamada de linguagem de mquina .

Linguagem de mquina
A linguagem de mquina de um computador consiste das instrues que seu processador pode entender. Elas so instrues para comandar a execuo de operaes bsicas, tais como somar dois nmeros ou comparar se dois nmeros so iguais, instrues para transferncia de dados entre a memria e os registradores do processador, ou entre a memria e os dispositivos de entrada e sada, e instrues para controlar o uxo de execuo das instrues de um programa. J que o que o processador entende so sequencias de bits, um programa escrito em linguagem de mquina deve estar escrito em esse formato. Vejamos um exemplo. Vamos escrever um programa para calcular o fatorial de um nmero. Lembremos da matemtica que o fatorial de um nmero o resultado da multiplicao sucessiva da sequncia de inteiros de 1 at o nmero em questo. O nosso programa pode executar ento: fact fact fact

num

fact * (num - 1) fact * (num - 2) ...

fact

fact * 1

(Repare que agora em fact est o resultado nal!) Nesse exemplo, vamos calcular o fatorial de 6. Para isto precisamos conhecer as instrues da mquina na qual nosso programa ser executado (o chamado de conjunto de instrues, ou instructions set), vamos supor que nosso processador entende no mnimo as seguintes instrues:

Operao
COPIAR em registro um valor inteiro SUBSTRAIR do valor armazenado em um registro o inteiro especicado MULTIPLICAR o valor armazenado em um registro com o valor armazenado em outro e guardar o resultado no primeiro registro DESVIAR a execuo da instruo seguinte se o valor resultante for maior do que zero IMPRIMIR o valor armazenado em um registro

Cdigo
0000 0001 0010 0011 0100

Assumamos que a nossa mquina tem no mnimo dois registros R1 e R2 em cujas etiquetas aparece 00 e 01 respectivamente. O nosso programa ir:

Copiar (Carregar) no registrador Copiar (Carregar) no registrador

R1 R2

o valor numrico 1; o valor numrico 6;

Multiplicar o valor armazenado no registrador no registrador

R1;

R1

pelo valor no registrador

R2

e armazen-lo

Decrementar o valor armazenado no registrador

R2;

Desviar para a instruo de multiplicao armazenada em um certo endereo da memria se o valor do registrador

R2

for maior do que zero;

Computadores e Programas

Imprimir o resultado armazenado no registrador

R1

Ento podemos escrev-lo da seguinte programa:

Localizao
0x00e2 0x00e3 0x00e5

Operao CARREGAR #1 no registro R1 CARREGAR #6 no registro R2 MULTIPLICAR o valor armazenado em R1 com o valor armazenado R2 e guardar o resultado em R1

Seqncia de bits (op val val) 0000 000000000000 000000000001 0000 000000000001 000000000110 0010 000000000000 000000000001

0x00e6 0x00e7 0x00e8

SUBSTRAIR do valor armazenado em R2 o valor 1 DESVIAR a execuo para a linha 0x00e5 se o valor em R2 for maior do que zero IMPRIMIR o resultado armazenado em R1

0001 000000000001 000000000001 0011 000000000101 000000000000 0100 000000000000 000000000000

Leitor atento: Os autores escolheram um valor pequeno para o clculo do fatorial por um bom motivo. Repare que para valores maiores do que 6 no poderiamos efetuar este clculo nessa nossa mquina: o resultado iria ultrapassar os 12 bits, e por tanto, o valor que pode ser armazenado em um registro dessa mquina. Este problema chamado de veja o exemplo ?? para mais detalhes.

overow, e o contrrio de underow,

Essas sequencias de bits so a cara de um programa escrito usando linguagem de mquina. Como vemos, a linguagem de mquina de um computador consiste de instrues para comandar a execuo de operaes bsicas, tais como somar dois nmeros ou comparar se dois nmeros so iguais, instrues para transferncia de dados entre a memria e os registradores do processador, ou entre a memria e os dispositivos de entrada e sada, e instrues para controlar o uxo de execuo das instrues de um programa. Na poca em que foram construdos os primeiros computadores, os programadores usavam instrues como essas, cada qual formada por aproximadamente uma dezena de bits, para descrever seus algoritmos. Programar nessa linguagem era uma tarefa trabalhosa e extremamente sujeita a erros, difceis de detectar e corrigir. Por esse motivo, os programadores logo passaram a usar nomes para as operaes e dados, escrevendo, por exemplo, as instrues acima, da seguinte forma:

MOV R1, 1 MOV R2, 6 L: MUL R1, R2 SUB R2, 1 JNZ L OUT R1

Linguagem de montagem
Um programa escrito nessa forma era ento manualmente traduzido para linguagem de mquina, e depois carregado na memria do computador para ser executado. O passo seguinte foi transferir para o prprio computador essa tarefa de montar o programa em linguagem de mquina, desenvolvendo um programa para realizar essa tarefa. Esse programa chamado de montador ,

4 linguagem de montagem .

3 e a notao simblica dos programas que ele traduz chamada de

Desenvolver programas em linguagem de montagem continuava sendo, entretanto, uma tarefa difcil e sujeita a grande quantidade de erros. A razo que as instrues dessa linguagem, exatamente as mesmas da linguagem de mquina, tm pouca relao com as abstraes usualmente empregadas pelo programador na construo de algoritmos para a soluo de problemas.

3 Em 4 Em

ingls, ingls,

assembler . assembly language .

1.3 Funcionamento e Organizao de Computadores

Linguagem de alto nvel


Para facilitar a tarefa de programao, e torn-la mais produtiva, foram ento desenvolvidas novas linguagens de programao. Essas novas linguagens foram chamadas linguagens de alto nvel , por oferecer um conjunto muito mais rico de operaes e construes sintticas adequadas para expressar, de maneira mais natural, algoritmos usados na soluo de problemas. Linguagens de mquina e linguagens de montagem so chamadas, em contraposio, linguagens de baixo nvel .

Compilao
Para que um programa escrito em uma linguagem de alto nvel possa ser executado pelo computador, ele precisa ser primeiro traduzido para um programa equivalente em linguagem de mquina. Esse processo de traduo chamado de compilao e o programa que faz essa traduo, de compi-

lador . Um compilador , portanto, simplesmente um programa tradutor, de programas escritos em


uma determinada linguagem, chamada de linguagem fonte , para programas em outra linguagem, chamada de linguagem objeto . Os programas fornecidos como entrada e obtidos como sada de um compilador so tambm comumente chamados, respectivamente, de programa fonte (ou cdigo

fonte ) e programa objeto (ou cdigo objeto ).


Um compilador analisa o texto do programa fonte, para determinar se ele est sintaticamente correto, isto , em conformidade com as regras da gramtica da linguagem, e, em caso armativo, gera um cdigo objeto equivalente. Caso o programa fonte contenha algum erro, o compilador ento emite mensagens que auxiliam o programador na identicao e correo dos erros existentes.

Interpretao
Outro processo para execuo de um programa em linguagem de alto nvel, em vez da compilao desse programa seguida pela execuo do cdigo objeto correspondente, a interpretao do programa fonte diretamente. Um interpretador , como o nome indica, um programa que interpreta diretamente as frases do programa fonte, isto , simula a execuo dos comandos desse programa sobre um conjunto de dados, tambm fornecidos como entrada para o interpretador. A interpretao de programas escritos em uma determinada linguagem dene uma mquina virtual, na qual realizada a execuo de instrues dessa linguagem. A interpretao de um programa em linguagem de alto nvel pode ser centenas de vezes mais lenta do que a execuo do cdigo objeto gerado para esse programa pelo compilador. A razo disso que o processo de interpretao envolve simultaneamente a anlise e simulao da execuo de cada instruo do programa, ao passo que essa anlise feita previamente, durante a compilao, no segundo caso. Apesar de ser menos eciente, o uso de interpretadores muitas vezes til, principalmente devido ao fato de que, em geral, mais fcil desenvolver um interpretador do que um compilador para uma determinada linguagem. Voc provavelmente j ouviu falar alguma vez da linguagem Java. Um sistema (ambiente) de programao e execuo de programas em Java baseado em uma combinao dos processos de compilao e interpretao: um compilador Java gera um cdigo de mais baixo nvel, chamado de

bytecodes , que ento interpretado. Um interpretador de bytecodes interpreta instrues da chamada Mquina Virtual Java (Em ingls JVM  Java Virtual Machine) Esse esquema usado no ambiente de programao Java no apenas contribuiu para facilitar a implementao da linguagem em grande nmero de computadores diferentes, mas constitui uma caracterstica essencial no desenvolvimento de aplicaes voltadas para a Internet, pois possibilita que um programa compilado em um determinado computador possa ser transferido atravs da rede e executado em qualquer outro computador que disponha de um interpretador de bytecodes . Outras linguagens interpretadas muito conhecidas so Python e Lua. Lua uma linguagem de programao brasileira muito utilisada na programao de jogos que forma parte do Ginga, o padro brasileiro de televiso digital.

Ambientes de Programao
Alm de compiladores e interpretadores, um ambiente de programao de uma determinada linguagem de alto nvel oferece, em geral, um conjunto de bibliotecas de componentes ou mdulos de programas, usados comumente no desenvolvimento de programas para diversas aplicaes. Alm

Computadores e Programas

disso, ambientes de programao incluem outras ferramentas para uso no desenvolvimento de programas, como editores de texto e depuradores de programas. Um editor um programa usado para criar um arquivo de dados e modicar ou armazenar dados nesse arquivo. O tipo mais simples de editor um editor de texto , que permite editar Alguns editores usam caracteres especiais, chamados de (i.e. criar ou modicar) qualquer documento textual (um texto signicando uma seqncia de caracteres, separados linha por linha). caracteres de controle, para facilitar a visualizao do texto editado, por exemplo colocando em destaque (em negrito ou com uma cor diferente) palavras-chave da linguagem. Um depurador um programa que oferece funes especcas para acompanhamento da execuo de um programa, com o objetivo de auxiliar o programador na deteco e identicao da origem de erros que possam existir em um programa. Em um ambiente de programao convencional, editores, compiladores, interpretadores e depuradores so programas independentes. Em um ambiente integrado de programao, ao contrrio, as tarefas de edio, compilao, interpretao e depurao so oferecidas como opes disponveis em um mesmo programa. A execuo de programas em um computador iniciada e controlada por um programa denominado sistema operacional. O sistema operacional controla a operao em conjunto dos diversos componentes do computador  processador, memria e dispositivos de entrada e sada  assim como a execuo simultnea de diversos programas pelo computador. A execuo do ncleo do sistema operacional iniciada no momento em que o computador ligado, quando esse ncleo transferido do disco para a memria do computador, permanecendo residente na memria enquanto o computador estiver ligado. O ncleo do sistema operacional prov uma interface adequada entre a mquina e os demais programas do sistema operacional que, por sua vez, oferecem uma interface adequada entre os diversos componentes do computador e os usurios e seus programas, em um ambiente de programao.

1.4 Exerccios Resolvidos


1. Mencionamos que os nmeros so representados no computador usando a notao arbica,
no sistema de numerao de base 2, ou sistema de numerao binrio. Este exerccio aborda a representao de nmeros usando essa notao e a converso entre as representaes de nmeros nos sistemas de numerao binrio e decimal. A notao hindu-arbica , que usamos para escrever nmeros em nosso sistema de numerao decimal, teria sido originada na ndia, no terceiro sculo a.C., sendo mais tarde levada para Bagd, no oitavo sculo d.C. interessante observar que o smbolo que representa o nmero zero s apareceu em um estgio posterior do desenvolvimento dessa notao, no sculo nove d.C. O nome notao arbica , mais comumente usado, se deve ao fato de que essa notao foi divulgada pela primeira vez por um matemtico rabe, chamado al-Khuarizmi  da o nome algarismo, dado aos smbolos que usamos atualmente para a representao de nmeros no nosso sistema de numerao. A caracterstica fundamental da notao hindu-arbica, que torna mais fcil representar nmeros grandes e realizar operaes sobre nmeros, o fato de ela ser uma notao posicional . No nosso sistema de numerao, de base 10, a posio de cada algarismo determina as potncias de 10 pelas quais devem ser multiplicados os nmeros denotados por esses algarismos, para obter o nmero representado. Por exemplo:

496 = 4 102 + 9 101 + 6 100


Essa notao pode ser usada, de modo geral, para representao de nmeros em um sistema de numerao de base

qualquer, onde

um nmero inteiro positivo, com base no fato de

que qualquer nmero inteiro no-negativo

pode ser univocamente representado na forma

p=
i=0
onde cada

di bi 0
a

di ,

para

i = 0, . . . , n,

um smbolo que representa um nmero de

b 1.

1.4 Exerccios Resolvidos

Figura 1.2: Converso de representao de nmero, de decimal para binria

No sistema de numerao binrio (de base 2), o numeral 1011, por exemplo, representa o nmero decimal 11, conforme se mostra a seguir (um subscrito usado para indicar a base do sistema de numerao em cada caso):

10112 = 1 23 + 0 22 + 1 21 + 1 20 = 1110
Exerccio : Converta o nmero
Para converter um nmero notar que, se

1101012

para a sua representao no sistema decimal.

2n+1 < p

p, escrito na base 10, para a sua representao na base 2, basta n d 2i , onde di = 0 ou di = 1, para i = 0, . . . , n, e dn = 0, temos que i i=0 n 2 . Portanto, efetuando n divises sucessivas de p por 2, obtemos: p= p = =
. . .

2q0 + d0 2(2q1 + d1 ) + d0 = 22 q1 + 2d1 + d0 2(. . . 2((2qn + dn ) + dn1 ) . . . + d1 ) + d0 2n+1 qn + 2n dn + 2n1 dn1 + . . . + 2d1 + d0 2n dn + 2n1 dn1 + . . . + 2d1 + d0

= = =
uma vez que teremos

qn = 0.

O processo de converso de um nmero da sua representao decimal para a sua representao binria, pode ser feito, portanto, como mostra a Figura 1.2, onde se ilustra essa converso para o nmero decimal 13.

Exerccio : converta o nmero

29510

para a sua representao na base binria.

2. Uma das operaes bsicas que um computador capaz de realizar a operao de somar
dois nmeros inteiros. Como essa operao executada em um computador? Os componentes bsicos dos circuitos eletrnicos de um computador moderno so chamados de portas lgicas. Uma porta lgica simplesmente um circuito eletrnico que produz um sinal de sada, representado como

1 ou 0 e interpretado como verdadeiro (V) ou falso (F),

respectivamente, que o resultado de uma operao lgica sobre os seus sinais de entrada. Essas operaes lgicas  no, , ou, ou exclusivo, representadas pelos smbolos (ou conectivos lgicos)

, , , ,

respectivamente  so denidas na Tabela 1.1.

O conjunto constitudo dos valores verdadeiro e falso chamado de conjunto Booleano , em homenagem ao matemtico George Boole (1815-1864), um dos pioneiros na formalizao da lgica matemtica. Analogamente, os valores desse conjunto so chamados de valores

booleanos e as operaes lgicas denidas sobre esse conjunto so chamadas de operaes booleanas , ou operaes da Lgica Booleana (ou Lgica Proposicional).
A operao lgica  tem resultado verdadeiro se ambos os operandos so verdadeiros, e falso em caso contrrio. A operao lgica ou tem resultado falso se ambos os operando so falsos, e verdadeiro em caso contrrio. A operao lgica ou exclusivo tem resultado verdadeiro se um dos operandos, mas no ambos, verdadeiro, e falso caso contrrio.

Computadores e Programas

Tabela 1.1: As operaes lgicas no, , ou e ou exclusivo

Operao Operao
no

Resultado

Resultado

V F

F V

V V F F

op op op op

V F V F

op = V F F F

()

op = V V V F

(ou)

(ou exclusivo)

op = F V V F

Figura 1.3: Circuito do meio-somador

Para entender como portas lgicas podem ser usadas para implementar a soma de nmeros inteiros positivos em um computador, considere primeiramente a soma de dois nmeros

m,

representados na base binria, cada qual com apenas 1 bit, ilustrada a seguir:

1 1 0 0

+ + + +

1 0 1 0

= = = =

10 01 01 00

ou

n 1 1 0 0

m 1 0 1 0

vai um

1 0 0 0

r 0 1 1 0

Ao comparar a tabela acima com as operaes lgicas denidas na Tabela 1.1, fcil perceber que a operao lgica ou exclusivo fornece o bit da soma igual a

n + m:

o bit

igual a

r do numeral que representa o resultado 1 se n ou m for igual a 1, mas no ambos, e igual a 0, caso

contrrio. O bit vai um desse numeral obtido pela operao lgica : o bit vai um

quando

so iguais a

1,

e igual a

em caso contrrio.

O resultado da soma

n+m

pode ser, portanto, representado pelo par

(n m, n m),

em

que o primeiro componente o bit vai um e o segundo o bit

do resultado. Note que

m n = (m n) (m n).
Com base nessas observaes, ca fcil construir um circuito para somar dois nmeros binrios

m,

cada qual representado com apenas 1 bit. Esse circuito, chamado de meio-somador ,

apresentado na Figura 1.3. Smbolos usuais so empregados, nessa gura, para representar as portas lgicas que implementam as operaes , ou e no. O meio-somador pode ser usado para construir um circuito que implementa a soma de trs nmeros binrios

n, m

p,

cada qual representado com apenas 1 bit, usando o fato de que

a operao de adio associativa:

n + m + p = (n + m) + p. Sendo n + m = (v1 , r1 ) e r1 + p = (v2 , r), temos que n + m + p = (v1 v2 , r), uma vez que v1 e v2 no podem ser ambos iguais a 1. O circuito lgico que implementa a soma n + m + p, chamado de somador
completo , pode ser construdo como mostra a Figura 1.4.
Podemos agora facilmente construir o chamado somador paralelo , para somar nmeros inteiros, representados no sistema de numerao binrio, com qualquer nmero xo de bits.

1.4 Exerccios Resolvidos

Figura 1.4: Circuito do somador completo

Figura 1.5: Circuito do somador paralelo

A Figura 1.5 ilustra um circuito somador paralelo para somar nmeros binrios qual representado com 3 bits,

ABC

DEF,

m,

cada

respectivamente.

3. Sabemos que um computador capaz de operar com nmeros inteiros, positivos ou negativos.
Como nmeros inteiros negativos so representados no computador? Para maior facilidade de armazenamento e de operao, todo nmero representado, em um computador, por uma seqncia de bits de tamanho xo. No caso de nmeros inteiros, esse tamanho igual ao nmero de bits que podem ser armazenados na palavra do computador. Em grande parte dos computadores modernos, esse tamanho de 32 bits ou, em computadores ainda mais modernos, de 64 bits. Para representar tanto nmeros inteiros no-negativos quanto negativos, um determinado bit dessa seqncia poderia ser usado para indicar o sinal do nmero  essa abordagem chamada de sinal-magnitude . A abordagem de sinal-magnitude no muito adequada, pois existem nesse caso duas possveis representaes para o zero e as operaes de somar nmeros no to simples quanto no caso da representao em complemento de dois , usada em todos os computadores modernos. A caracterstica fundamental dessa representao a de que a operao de somar

1 ao maior

inteiro positivo fornece o menor inteiro negativo. Desse modo existe apenas uma representao para o zero e pode-se realizar operaes aritmticas de modo bastante simples. Ilustramos, na Tabela 1.2, a representao de nmeros na notao de complemento de em um computador com uma palavra de apenas 4 bits. Note que existem distintas em uma palavra de a

2n

combinaes

bits, e portanto possvel representar

2n

nmeros inteiros,

que na representao de complemento de dois compreendem os inteiros na faixa de

2(n1)

2(n1) 1.

p, 0 < p 2n1 , com relao a n bits, n denotado por c2 (p), denido como sendo a representao na base binria, com n bits, do
O complemento de 2 de um nmero inteiro positivo

10

Computadores e Programas

nmero positivo

2n p.

Na notao de complemento de 2, um nmero inteiro

representado na base binria, com

bits, da maneira usual, e um nmero inteiro

p 0 p<0

representado pelo complemento de 2 do valor absoluto de

p, cn 2 (|p|).

Tabela 1.2: Representao de inteiros em 4 bits 0 1 2 3 4 5 6 7 0000 0001 0010 0011 0100 0101 0110 0111 -8 -1 -2 -3 -4 -5 -6 -7 1000 1111 1110 1101 1100 1011 1010 1001

Dado um nmero inteiro positivo representao de

p,

uma maneira eciente para calcular

p na base binria, pode ser obtida pela observao de (2n 1) p + 1. Como a representao de 2n 1 na base binria consiste de uma seqncia n n de n 1s, fcil ver que, para obter o resultado da subtrao (2 1) p, ou seja, c2 (p) 1, tambm chamado de complemento de 1 de p, basta tomar a representao de p na base n binria e trocar, nessa representao, os 1s por 0s e vice-versa. Para obter c2 (p), precisamos ento apenas somar 1 ao resultado obtido.
Por exemplo: representao de -1 em 4 bits representao de -7 em 4

cn 2 (p), a partir da n n que c2 (p) = 2 p =

4 = c4 2 (1) = c2 (0001) = 1110 + 1 = 1111 4 4 bits = c2 (7) = c2 (0111) = 1000 + 1 = 1001

Exerccio : Como seriam representados os nmeros


de tamanho igual a 8 bits?

17 e 17, em um computador com palavra p na base binria, dada a sua represen2n (2n p) = p. Portanto, dado

Para obter a representao usual de um nmero inteiro

tao na notao de complemento de 2, basta observar que

c2 n (p), a representao de p na base binria pode ser obtida calculando o complemento de 2 n n n 4 4 de c2 (p), ou seja, c2 (c2 (p)). Por exemplo: de c2 (p) = 11112 obtemos p = c2 (11112 ) = 00012 .
Exerccio : Determine a representao na base decimal do nmero

tal que

c4 2 (p) = 10102 .

Exerccio : Determine a representao na base decimal dos nmeros representados por


e

10011111,

01100001

em um computador com palavra de tamanho igual a 8 bits.

Voc provavelmente ter observado que o bit mais esquerda da representao de um nmero inteiro na notao de complemento de 2 igual a

1,

se o nmero for negativo, e igual a

0,

caso contrrio. Esse bit pode ser, portanto, interpretado como o sinal do nmero. Usando essa representao, a adio de dois nmeros inteiros

n e m pode ser feita da maneira

usual, sendo descartado o bit vai um obtido mais esquerda. Por exemplo:

01102 + 10012 = 11112


Ou seja,

11102 + 11012 = 10112

6 + (7) = 1

(2) + (3) = (5).

A demonstrao de que esse procedimento fornece o resultado correto para a operao de adio foge um pouco do escopo deste livro.

1.5 Exerccios
1. Determine a representao, no sistema de numerao binrio, de cada um dos seguintes
nmeros, escritos na base decimal:

1.6 Notas Bibliogrcas

11

(a) 19

(b) 458

2. Determine a representao, no sistema de numerao decimal, de cada um dos seguintes


nmeros, escritos na base binria: (a)

11102

(b)

1101102

3. Realize as operaes abaixo, sobre nmeros representados no sistema de numerao binrio:


(a)

1011 + 101

(b)

10100 1101

4. Determine a representao na notao de complemento de 2, com 8 bits, de cada um dos


seguintes nmeros: (a) 23 (b) 108

5. Determine a representao na base decimal de cada um dos seguintes nmeros, representados


na notao de complemento de 2, com 8 bits: (a)

11010011

(b)

11110000

6. Indique como seria feito o clculo das seguintes operaes, em um computador que utiliza
notao de complemento de 2 para representao de nmeros e tem palavra com tamanho de 8 bits: (a)

57 + (118)

(b)

(15) + (46)

1.6 Notas Bibliogrcas


Os primeiros estudos em cincia da computao, realizados por volta de 1935, estabeleceram os fundamentos tericos da rea, lanando as bases para a construo dos primeiros computadores. Como resultado desses estudos, foi estabelecida uma caracterizao formal para a noo intuitiva de algoritmo, que determinou os limites para o conjunto dos problemas computveis, possibilitando provar a existncia de problemas que no podem ser resolvidos por meio de computao. Ao leitor interessado em saber mais sobre esse assunto, recomendamos a leitura de [?]. Uma discusso interessante sobre a inuncia dos recentes resultados da teoria da computao no desenvolvimento tecnolgico e cientco alcanados no sculo 20 apresentada em [?] e [?]. Para um bom entendimento sobre os fundamentos tericos da computao, necessrio algum conhecimento de matemtica discreta, que abrange temas como lgica matemtica, teoria de conjuntos, relaes e funes, induo e recurso. Dois livros excelentes, que abordam esses temas de maneira introdutria, so [?] e [?]. O funcionamento e organizao de computadores, descritos brevemente neste captulo, discutido detalhadamente em diversos livros especcos sobre o assunto, dentre os quais recomendamos [?,

?, ?].

12

Computadores e Programas

Captulo 2

Paradigmas de Programao: o paradigma imperativo


O nmero de linguagens de programao existentes atualmente chega a ser da ordem de alguns milhares

1 . Esse nmero impressionante tem vrias motivaes, dentre elas esto:

1. facilitar a atividade de programao, tornando-a cada vez mais produtiva; 2. favorecer a construo de programas mais ecientes, isto , que executam rapidamente e que usam relativamente pouca quantidade de memria; 3. produzir programas seguros, ou seja, menos sujeitos a erros que possam ocasionar um comportamento da execuo do programa diferente daquele que esperado. Apesar dessa grande diversidade de linguagens de programao, podemos notar que um grupo maioritrio delas possui, essencialmente, o mesmo conjunto de comandos bsicos, embora esses possam apresentar formas diferentes em diferentes linguagens como exemplo no captulo anterior. Como voc, leitor atento, deve lembrar, o programa faz o seguinte:

2 . Relembremos um programa colocado

Carregar no registrador Carregar no registrador

R1 R2

o valor numrico 1; o valor numrico 6;

Multiplicar os valores armazenados nos registradores Decrementar o valor em

R1 e R2 e armazenar o resultado em R1;

R2;

Desviar para a instruo de multiplicao armazenada em um certo endereo da memria enquanto o valor de R2 for maior do que zero. Imprimir na tela o valor em R1;

Voc concorda que o seguinte trecho de cdigo tem uma funcionalidade equivalente?

11 12 13 14 15 16 17

R1 R2 faz


R1 R2

1 6

R1 * R2 R2 - 1

enquanto R2 > 0 imprime (R1)

1 No Tiobe index (http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html) voc encontra uma lista


das linguagens mais populares.

2O

termo instruo em geral usado para linguagens de mais baixo nvel, enquanto o termo comando, em

princpio equivalente, mais usado para linguagens de alto nvel

14

O Paradigma Imperativo de Programao

Este programa, apesar de pequeno, ilustra os tipos de comando bsicos presentes em um grande grupo de linguagens de programao. Esse conjunto de comandos est enumerado na gura 2.1.

I.- um comando de atribuio  usado para armazenar um valor em uma determinada posio de memria (linhas 11, 12, 14 e 15); II.- comandos para leitura de dados, de dispositivos de entrada, e para escrita de dados, em dispositivos de sada (linha 17); III.- trs formas distintas de combinao de comandos:

composio seqencial  execuo de um comando aps outro, seleo (ou escolha condicional )  escolha de um comando para ser executado, de
acordo com o resultado da avaliao de uma condio,

repetio  execuo de um comando repetidas vezes, at que uma condio seja satisfeita (linhas 1316).

Figura 2.1: Comandos bsicos das linguagens imperativas Em uma linguagem com essas caractersticas, um programa consiste em uma seqncia de comandos que descreve, em essncia, como devem ser modicados os valores armazenados na memria do computador, de maneira que uma determinada tarefa seja realizada. Esse paradigma de programao denominado paradigma imperativo e linguagens baseadas nesse paradigma so chamadas de linguagens imperativas . imperativa. Existem, em contraposio, outros paradigmas de programao, chamados declarativos , nos quais um programa se assemelha mais a uma descrio de o que constitui uma soluo de um determinado problema, em lugar de como proceder para obter essa soluo. O foco deste livro est em apresentar uma viso geral dos principais conceitos da programao imperativa. Para isto, dedicaremos os seguintes captulos ao estudo dos comandos que caracterizam as linguagens que suportam este paradigma, enumerados na gura entrada/saida e combinaes deles. 2.1, ou seja, atribuio, A linguagem

uma linguagem que prov suporte ao

paradigma imperativo de programao; usual dizer, abreviadamente, que

uma linguagem

O leitor atento diz: Os autores armam que linguagens imperativas tem somente comandos de atribuio, entrada/saida e combinaes deles. Ento me digam, como eu posso executar operaes matemticas usando essas linguagens? Os autores respondem: Na realidade, para descrever operaes utilizamos

expresses, e no

comandos. Uma expresso uma combinao de variveis, constantes e operadores que, quando avaliada, resulta num valor. Antes de aprofundar no conceito de expresses precisamos falar um pouco desses conceitos.

2.1 Varivel e Atribuio


Em um programa escrito usando uma linguagem imperativa, uma varivel representa um lugar que contm um certo valor. Esse lugar uma determinada rea contigua da memria do computador. Podemos imaginar que uma varivel uma etiqueta que serve para identicar esse lugar na memria. varivel. O tipo da varivel denota o conjunto de valores que podem ser armazenados na Exemplos de tipos so o tipo caractere (admite valores como 'c', 'a') e o tipo nmero

inteiro (admite valores como 3, -400). Veja o anexo B para uma descrio dos tipos bsicos em C. O estado da memria aps a execuo do seguinte trecho de programa:

1 2

media aluno

10 'P'

2.2 Operadores

15

poderia ser representado como: 10 media P aluno

A operao executada nas linhas 1 e 2 do programa chama-se de atribuio. O efeito da operao de atribuio o armazenamento do valor atribudo a varivel no espao na memria identicado por esta. comando: Uma nova atribuio ir sobreescrever o valor da varivel, assim, aps a execuo do

1 2

media aluno

177 'P'

o estado da memria ser: 177 media P aluno

Esse conceito de varivel difere daquele a que estamos acostumados em matemtica. Em uma expresso matemtica, toda ocorrncia de uma determinada varivel denota um mesmo valor. Em um programa em linguagem imperativa, ao contrrio, ocorrncias distintas de uma mesma varivel podem representar valores diferentes, uma vez que tais linguagens so baseadas em comandos que tm o efeito de modicar o valor armazenado em uma varivel, durante a execuo do programa. Alm disso, uma determinada ocorrncia de uma varivel no texto de um programa pode tambm representar valores diferentes, em momentos distintos da execuo desse programa, uma vez que um comando pode ser executado repetidas vezes. Entretanto, uma varivel sempre ir representar

um valor de cada vez. Por exemplo, no programa anterior o valor de media muda aps a nova
atribuio.

2.2 Operadores
Operadores permitem descrever operaes a serem executadas, como soma, multiplicao e comparao. Alm do operador de atribuio '=', a linguagem

oferece operadores aritmticos,

relacionais e lgicos e de converso de tipo. Os operadores podem ser binrios ou unrios, dependendo de se atuam sobre dois operandos ou um, respectivamente. Operadores binrios se escrevem entre os dois operandos. Operadores aritmticos, como seu nome indica, permitem descrever operaes aritmticas, eles so mostrados na Tabela 2.2. Note que a diviso de um valor inteiro (de tipo

int,

com ou sem

qualicadores) por outro valor inteiro, chamada em computao de diviso inteira , retorna em

Co

quociente da diviso (a parte fracionria, se existir, descartada). Veja o exemplo da Tabela 2.2:

5/2

igual a

2.

Para obter um resultado com parte fracionria, necessrio que pelo menos um

dos argumentos seja um valor de ponto utuante. Por sua parte, operadores relacionais e lgicos se utilizam para a tomada de decises. Em C no existem os valores booleanos verdadeiro e falso. Se considera que o valor 0 (zero) falso, enquanto valores diferentes de 0 so considerados verdadeiros. relacionais so 0 ou 1. Os resultados produzidos por operaes

Operadores relacionais ou de comparao, como em A = (b < = c), so usados para comparar


dois valores. Como vimos anteriormente, o smbolo

==

usado para comparar a igualdade de

dois valores. A avaliao de uma expresso da forma  e1 resultados da avaliao de representada por

== e2 

tem resultado verdadeiro se os A operao de desigualdade

!=.

e1

e2

so iguais, e falso, caso contrrio.

Outras operaes de comparao so apresentadas na Tabela 2.1.

Leitor atento: Um erro que os principiantes cometem com frequncia tentar comparar mais de dois valores em operaes relacionais. O compilador gcc retorna uma mensagem de erro curiosa quando tentamos compilar comparaes no estilo da matemtica:

Warning:comparisons like X<=Y<=Z do not have their mathematical meaning, -3 < = -2 < = -1 is F

16

O Paradigma Imperativo de Programao

Figura 2.2: Operadores aritmticos em

C 3 3.0 3.0 1 1.0 1.0 -1 -1.0 6 6.0 6.0 2 2.5 2.5 1 1.0

Operador

+ * / %

Signicado
Adio

Subtrao

Negao Multiplicao

Diviso

Resto

2 + 1 2 + 1.0 2.0 + 1.0 2 - 1 2.0 - 1 2.0 - 1.0 -1 -1.0 2 * 3 2.0 * 3 2.0 * 3.0 5 / 2 5 / 2.0 5.0 / 2.0 5 % 2 5.0 % 2.0

Exemplo

Resultado

Tabela 2.1: Operadores de comparao

Operador

== != < > <= >=

Signicado
Igual a Diferente de Menor que Maior que Menor ou igual a Maior ou igual a

Exemplo

1 == 1 1 != 1 1 < 1 1 > 1 1 <= 1 1 >= 1

Resultado
verdadeiro falso falso falso verdadeiro verdadeiro

Tabela 2.2: Operadores lgicos em

Operador

! && ||

Signicado
Negao Conjuno (no-estrita) () Disjuno (no-estrita) (ou)

Por outro lado, operadores lgicos ou booleanos, relacionados na tabela 2.2 servem para combinar expresses booleanas (operam com valores falso ou verdadeiro), ou seja: o e, ou e no das seguintes operaes:

A = (b < c) e (c < 6) A = (c > 3) ou (b > 5) A = no A


A operao de negao (ou complemento) representada por verdadeiro: a negao de um valor verdadeiro em

!;

ela realiza a negao do argu-

mento (de modo que um valor falso (zero) que verdadeiro (diferente de zero), e o inverso tambm

(diferente de zero) falso (zero em C). && (l-se ): e1 && e2 verdadeiro (diferente de zero) se somente se a avaliao de cada uma das expresses, e1 e e2 tem como resultado o valor A operao de conjuno lgica representada por verdadeiro. de O operador

e1 && e2

&& um operador no-estrito, no segundo argumento;

isso signica que a avaliao

pode retornar um resultado verdadeiro mesmo que a avaliao de

e2

no retorne

2.3 Expresses

17

nenhum valor vlido; por exemplo:

0 && (0/0 == 0)
retorna falso (0). Note que o operador de conjuno bit-a-bit, ou seja, a avaliao de retornam. Por exemplo,

e1 & e2

&,

um operador estrito (nos dois argumentos),

retorna um resultado se e somente se a avaliao de

e1

e de

e2

0 & (0/0 == 0)
provoca a ocorrncia de um erro (pois de

0/0

provoca a ocorrncia de um erro).

A operao de conjuno no-estrita denida como a seguir: o valor de

e1

se esse for falso (0); caso contrrio, igual ao valor de

e2 .

Dessa forma,

e1 && e2 igual ao e2 s avaliado se a

avaliao de

e1

fornecer valor verdadeiro (diferente de zero).

Ao contrrio, a avaliao de

e1 & e2

sempre envolve a avaliao tanto de

Analogamente, a operao de disjuno lgica representada por igual a falso somente se a avaliao de cada uma das expresses, valor falso. conjuno lgica no-estrita lgica no-estrita e igual a

e1 quanto de e2 . || (l-se ou): e1 || e2 e1 e e2 , tem como resultado

Observaes anlogas s feitas anteriormente, para os operadores de conjuno bit-a-bit

||.

& e &&, so vlidas para os operadores de disjuno bit-a-bit | e disjuno

Nesse caso, temos que

e1 || e2 igual a verdadeiro (diferente de zero) se e1 for igual a verdadeiro,

e2

em caso contrrio.

Existe ainda predenido em nos seguintes casos:

o operador ou-exclusivo bit-a-bit

Por ltimo, est o operador de Converso de Tipo. Converses de tipo podem ocorrer, em

C,

implicitamente: em atribuies e passagem de argumentos a funes, dependendo dos tipos envolvidos (isto , dependendo de se, respectivamente, o tipo da expresso de atribuio

v = e

e em um comando

diferente do tipo da varivel

v,

ou se o tipo do argumento diferente

do tipo do parmetro correspondente em uma chamada de funo);

explicitamente: em converses de tipo (em ingls, type casts ).

Em geral h converso automtica (implcita) quando um valor de um tipo numrico tem um conjunto de valores contido em outro tipo numrico quando

t0

que

t.

Neste caso, o valor de tipo

t0

convertido automaticamente, sem alterao, para um valor de tipo

t0

char

ou

short

ou

byte

igual a

int.

t.

Isso ocorre, por exemplo,

Uma converso de tipo explcita tem a seguinte sintaxe:

(t) e
onde da expresso

t um tipo e e uma expresso. e deve ser convertido

A converso de tipo indica que o valor resultante da avaliao para o tipo

t.

Em geral, a converso de tipo pode resultar

em alterao de um valor pelo truncamento de bits mais signicativos (apenas os bits menos signicativos so mantidos), no caso de converso para um tipo por menos bits do que valores do tipo da expresso

cujos valores so representados

e.

No caso de uma converso para um tipo

cujos valores so representados por mais bits do que valores do tipo da expresso

e,

h uma

extenso do bit mais esquerda do valor de como um valor do tipo

para completar os bits da representao desse valor

t.

2.3 Expresses
Agora que falamos em variveis, atribuies e operadores, j estamos prontos para entender as expresses. Uma expresso de uma linguagem de programao formada a partir de variveis e constantes, usando operadores ou funes. Funes na computao, como na matemtica, so relaes que associam univocamente membros de um conjunto com membros do outro. Elas so

18

O Paradigma Imperativo de Programao

Tabela 2.3: Precedncia de operadores

Precedncia maior Operadores

*, /, % +, >, <, >=, <= ==,!= & | && || ? :

* j / k i + j * k i < j + k b == i < j b & b1 == b2 i j & k i < j | b1 & b2 i + j != k && b1 i1 < i2 || b && j < k i < j || b ? b1 : b2
i

Exemplos

(i * j ) / k i + (j *k ) i < (j +k ) b == (i < j ) b & (b1 == b2 ) i (j & k ) (i < j ) | (b1 & b2 ) ((i + j) != k) && b1 (i1 <i2 ) || (b && (j <k )) ((i <j ) || b ) ? b1 : b2

Precedncia menor

um pilar fundamental de linguagens de programao, voltaremos a estud-las mais detalhadamente nos prximos captulos. expresso x +y, essa ltima formada pela aplicao do operador caso, variveis). predenidos, que podem ser usados em expresses. O valor de uma expresso (ou valor retornado pela expresso, como comum dizer, em computao) aquele obtido pela avaliao dessa expresso durante a execuo do programa. Por exemplo, y +1 uma expresso cujo valor obtido somando Uma expresso em Por exemplo, f

(x +y )

uma expresso formada pela aplicao de uma funo, de nome f ,

s expresses x e y (nesse

Toda linguagem de programao oferece um conjunto de funes e operadores

ao valor contido na varivel y . A

sempre avaliada da esquerda para a direita, respeitando-se contudo a

precedncia predenida para operadores e a precedncia especicada pelo uso de parnteses. mente, apresentada na Tabela 2.3.

precedncia desses operadores, assim como de outros operadores predenidos usados mais comuO estabelecimento de uma determinada precedncia entre operadores tem como propsito reduzir o nmero de parnteses usados em expresses. O uso de parnteses em uma expresso , em alguns casos, opcional, apenas contribuindo para tornar o programa mais legvel. Por exemplo, a expresso

que o operador de multiplicao (*) tem maior precedncia do que o de adio (+) (o que resulta na avaliao da multiplicao antes da adio). Outro exemplo

3+(5*4) equivalente a 3+5*4, uma vez

3<5*4, que equivalente a 3<(5*4). Em outros casos, o uso de parnteses necessrio, tal como, por exemplo, na expresso 3*(5+4)  a ausncia dos parnteses, nesse caso, mudaria o resultado da expresso (pois 3*5+4 fornece o mesmo resultado que (3*5)+4). Note tambm a distino existente entre o signicado de uma varivel v como varivel alvo
de um comando de atribuio, isto , em uma ocorrncia do lado esquerdo de um comando de atribuio, e o signicado de um uso dessa varivel em uma expresso: o uso de

como varivel

alvo de uma atribuio representa um lugar (o endereo da rea de memria alocada para varivel ocorre em uma expresso.

v,

durante a execuo do programa), e no o valor armazenado nessa rea, como no caso em que a

10, aps a execuo + 1;, o valor contido em x passa a ser 11: o uso de x no lado direito desse comando retorna o valor 10, ao passo que o uso de x do lado esquerdo desse comando representa uma posio de memria, onde o valor 11 armazenado. Um programa em linguagem como C pode incluir denies de novas funes, que podem ento
Por exemplo, supondo que o valor contido em uma varivel inteira x seja do comando de atribuio  x

ser usadas em expresses desse programa. Em linguagens imperativas, a possibilidade de uso de comandos em denies de funes torna possvel que a avaliao de uma expresso no apenas retorne um resultado, mas tambm modique valores de variveis. Quando uma expresso tem tal efeito, diz-se que tem um efeito colateral . Em

C,

o prprio comando de atribuio uma expresso (com efeito colateral). Por exemplo,

supondo que a e b so duas variveis inteiras, podemos escrever:

2.4 Programando em C

19

+ 1; + 1
calculado, e ento atribudo a b e,

Na execuo desse comando, o valor da expresso b ento a expresso b

em seguida, atribudo a a. Se b contm, por exemplo, o valor

em b (que passa a ser

b + 1 no s retorna o valor b 4).

3, antes da execuo desse comando, + 1, igual a 4, como modica o valor contido

O comando de atribuio no o nico comando que pode modicar o valor de uma varivel. Isso ocorre tambm no caso de comandos de entrada de dados, tambm chamados de comandos

de leitura , que transferem valores de dispositivos externos para variveis. Um comando de leitura
funciona basicamente como um comando de atribuio no qual o valor a ser armazenado na varivel obtido a partir de um dispositivo de entrada de dados. Comandos de entrada e sada de dados so abordados no Captulo atribuio tem a forma:

??.
Em

Um comando de atribuio armazena um valor em uma varivel.

C,

um comando de

= e;
v,
ao valor que estava

A execuo desse comando tem o efeito de atribuir o valor resultante da avaliao da expresso

varivel

v.

Aps essa atribuio, no se tem mais acesso, atravs de

armazenado anteriormente nessa varivel  o comando de atribuio modica o valor da varivel.

O programa a seguir ilustra que no uma boa prtica de programao escrever programas que dependam de forma crtica da ordem de avaliao de expresses, pois isso torna o programa mais difcil de ser entendido e pode originar resultados inesperados, quando esse programa modicado. A execuo do programa abaixo imprime como uma expresso. varivel i.

em vez de

20.

Note como o resultado depende

criticamente da ordem de avaliao das expresses, devido ao uso de um comando de atribuio Nesse caso, o comando de atribuio (que retorna, nesse caso, o valor

(i=2) usado como uma expresso 2), tendo como efeito colateral modicar o valor armazenado na

int int

i j

= 10; = (i =2) * i ;

Leitor atento: Note o uso do smbolo

no comando de atribuio da linguagem

seu uso mais comum, como smbolo de igualdade. Em

==.

Usar

em vez de

==

C,

C,

diferente do

o operador usado para teste de igualdade

um erro cometido com freqncia por programadores iniciantes.

2.4 Programando em C
Em

C,

e em muitas outras das linguagens de programao imperativas, toda varivel usada em

um programa deve ser declarada antes de ser usada. Uma declarao de varivel especica o nome e o tipo da varivel, e tem o efeito de criar uma nova varivel com o nome especicado. A declarao reserva um espao na memria do tipo especicado e associa o nome a esse espao, mas no atribui valor as variveis. At a primeira atribuio (inicializao) o valor da varivel estar indenido, pois no espao reservado estar ainda o valor armazenado previamente na memria, chamado de lixo de memria. Estamos inicializando uma varivel quando, junto a declarao da varivel, especicamos qual o valor a ser armazenado nela. Assim, a declarao:

char x; int
3

= 10; int

z;

comum usar, indistintamente, os termos valor armazenado em uma varivel, valor contido em uma varivel

ou, simplesmente, valor de uma varivel.

20

O Paradigma Imperativo de Programao

char, ou seja, que pode armazenar um caractere, e que y e z int (variveis inteiras, ou de tipo inteiro). A declarao da varivel y especica que a varivel deve ser inicializada com o valor 10, quando ela criada. No so especicados valores iniciais para as variveis x e z ; em C, isso signica que um valor inicial indenido (determinado
especica que x uma varivel de tipo so variveis de tipo de acordo com a congurao da memria no instante da execuo do comando de declarao) armazenado em cada uma dessas variveis. A inexistncia de inicializao implcita em

C, e muitas C

outras caractersticas da linguagem, tm como principal motivao procurar proporcionar maior

ecincia na execuo de programas (ou seja, procuram fazer com que programas escritos em
levem menos tempo para serem executados). Na linguagem

C,

cada expresso tem um tipo, conhecido estaticamente  de acordo com Estaticamente signica durante a

a estrutura da expresso e os tipos dos seus componentes.

compilao (ou seja, antes da execuo) ou, como comum dizer em computao, em tempo
de compilao (uma forma de dizer que tem inuncia da lngua inglesa). Isso permite que um compilador em que o tipo no apropriado.

C possa detectar erros de tipo, que so erros devidos ao uso de expresses em contextos Por exemplo, supondo que + um operador binrio (deve ser int
y

chamado com dois argumentos para fornecer um resultado, seria detectado um erro na expresso

x +, usada por exemplo

e outro depois do operador

+)

x +;  uma vez que no foram usados dois argumentos (um antes

nessa expresso. Um dos objetivos do uso de tipos em linguagens

de programao permitir que erros sejam detectados, sendo uma mensagam de erro emitida pelo compilador, para que o programador possa corrigi-los (evitando assim que esses erros possam ocorrer durante a execuo de programas). Para impedir que o valor da varivel possa ser modicado usamos o atributo Ela ento, de fato, uma constante. Um nome de uma varivel ou funo em caractere sublinha programador

const na declarao

de varivel. Essa varivel dever ser inicializada, e seu valor permanecer sempre igual no programa.

'_'),

C pode ser qualquer sequncia de letras ou dgitos ou o C


 que inicia um comando

e deve comear com uma letra ou com o caractere sublinha. No entanto,

existem nomes reservados, que no podem ser usados como nomes de variveis ou funes pelo

C.

Por exemplo,

return

uma palavra reservada em

usado para especicar o resultado de uma funo e fazer com que a execuo da funo seja interrompida, retornando o resultado especicado  e portanto no pode ser usada como nome de varivel ou funo. O limite no tamanho do nome depende do standard, o ANSI C Standard de 1989 admite at 31 caracteres.

Dica de estilo: O nome de uma varivel ou funo pode ser escolhido livremente pelo programador, mas importante que sejam escolhidos nomes signicativos  ou seja, mnemnicos, que lembrem o propsito ou signicado da entidade (varivel ou funo) representada. Por exemplo, procure usar nomes como soma , media e pi para armazenar, respectivamente, a soma e a mdia de determinados valores, e uma aproximao do nmero

em vez de simplesmente, digamos, s, m , p (outras letras

que no s, m e p so ainda menos mnemnicas, por no ter relao com soma , media e pi ). Isto muito importante quando o nome ser usado em grande parte do programa. No entanto, til procurar usar nomes pequenos, a m de tornar o programa mais conciso, quando em fragmentos pequenos do programa. Por isso, muitas vezes comum usar nomes como, por exemplo, i e j como contadores de comandos de repetio, e n como um nmero natural qualquer, arbitrrio  de modo semelhante ao uso de letras gregas em matemtica. Valores constantes so comumente nomeados em letras maisculas.
A linguagem

C prov os tipos bsicos int, float, double, char e void. double),

O tipo inteiro representa

valores inteiros (sem parte fracionria). entre

double usa um espao (nmero de bits) pelo menos igual, mas em geral maior do que um valor de tipo float. Um valor de tipo char usado para armanezar um caractere. Em C um valor de tipo char
um valor de tipo um inteiro, sem sinal (com um tamanho menor ou igual ao de um inteiro).??? No existe valor de tipo

Nmeros de ponto utuante (float ou

float e double de preciso:

contm uma parte fracionria. A diferena

void;

esse tipo usado basicamente para indicar que uma funo no

retorna nenhum resultado, ou no tem nenhum parmetro. Em C existe um tipo especial de varivel que permite armazenar e manipular valores de en-

2.5 Exerccios

21

dereos de memria, so os chamados ponteiros. variveis do tipo inteiro:

Para cada tipo h um tipo ponteiro capaz de

armazenar endereos de valores do tipo correspondente. Assim, da mesma forma que declaramos int a = 5; //reserva espao para armazenar um inteiro na memoria podemos declarar variveis que armazenem valores de endereos onde existem valores inteiros armazenados: int *p; que declara uma varivel p que pode armazenar valores de memria em que existe um inteiro armazenado. A sintaxe da declarao de um ponteiro em C : tipo * nome; Usamos a atribuio p=NULL; para indicar que o ponteiro p no esta apontando para lugar algum.

2.5 Exerccios
1. Algumas respostas ERRADAS 'a lista do laboratorio:
- Quando o dividendo inteiro,o programa realiza uma diviso inteira. Com o (oat) o programa faz uma diviso normal.Quando o dividendo real a operao feita normalmente. - a instruo (oat) transforma o resultado da operao 'b / a' em um nmero inteiro para de seja impresso como inteiro atravs da varivel 'c'. Responda: O que elas tem de errado?

2. Qual o efeito das declaraes de variveis durante a execuo do trecho de programa abaixo?

int int

x; y

= 10;

3. Quais so os valores armazenados nas variveis x e y , ao nal da execuo do seguinte trecho


de programa?

int x ; int y = 10; x = y * 3; while (x > y ) { x = x - 5; y = y + 1; }


4. Qual o valor da varivel s ao nal da execuo do seguinte trecho de programa, nos dois
casos seguintes: (a) as variveis a e b tm, inicialmente, valores (b) as variveis a e b tm, inicialmente, valores

5 8

e e

10, respectivamente; 2, respectivamente.

= 0; if (a > b ) s = (a +b )/2; while (a <= b ) { s = s + a; a = a + 1; b = b - 2; }


s

22

O Paradigma Imperativo de Programao

2.6 Notas Bibliogrcas


Alm dos paradigmas de programao imperativo e orientado por objetos, existem, como mencionamos na introduo deste captulo, outros paradigmas de programao, mais declarativos : o paradigma funcional e o paradigma lgico. Embora esses paradigmas sejam ainda relativamente pouco utilizados, o interesse por eles tem crescido de maneira signicativa. No paradigma funcional , um programa consiste, essencialmente, em uma coleo de denies de funes, cada qual na forma de uma srie de equaes. Por exemplo, a funo que determina o fatorial de um nmero inteiro no-negativo n, poderia ser denida pelas equaes:

fatorial fatorial

0 = 1 n = n *

fatorial (n -1)

A execuo de um programa em linguagem funcional consiste na avaliao de uma determinada expresso desse programa, que usa as funes nele denidas. dessas linguagens. Uma caracterstica importante de linguagens funcionais o fato de que possibilitam denir O fato de que essas denies de funes podem tambm ser vistas como regras de computao estabelece o carter operacional

funes de ordem superior , isto , funes que podem ter funes como parmetros, ou retornar uma
funo como resultado. Essa caracterstica facilita grandemente a decomposio de programas em componentes (funcionais) e a combinao desses componentes na construo de novos programas. O maior interesse pela programao funcional apareceu a partir do desenvolvimento da linguagem ML e, mais recentemente, da linguagem Haskell (veja Notas Bibliogrcas). No paradigma lgico , um programa tem a forma de uma srie de asseres (ou regras), que denem relaes entre variveis. A denominao dada a esse paradigma advm do fato de que a linguagem usada para especicar essas asseres um subconjunto da Lgica de Primeira Ordem (tambm chamada de Lgica de Predicados ). Esse subconjunto da lgica de primeira ordem usado em linguagens de programao em lgica usa asseres simples (formada por termos, ou expresses, que tm valor verdadeiro ou falso), da forma:

P
somente se todos os termos depois

se

P1 , P2 , . . . , Pn P
verdadeiro se e forem verdadeiros. Alm dessa interpretao

A interpretao declarativa dessa assero , informalmente, a de que

P1 , . . . , Pn (n 0)

declarativa, existe uma interpretao operacional:

para executar (ou resolver)

P,

execute

P1 ,

P2 ,

etc., at

das avaliaes de

Pn , fornecendo o resultado verdadeiro se e P1 , . . . , Pn fornecer resultado verdadeiro.

somente se o resultado de cada uma

A linguagem Prolog a linguagem de programao mais conhecida e representativa do paradigma de programao em lgica. Existe tambm, atualmente, um interesse expressivo em pesquisas com novas linguagens que exploram o paradigma de programao em lgica com restries, e com linguagens que combinam a programao em lgica, a programao funcional e programao orientada por objetos (veja Notas Bibliogrcas). Existem vrios livros introdutrios sobre programao de computadores, a maioria deles em lngua inglesa, abordando aspectos diversos da computao. Grande nmero desses livros adota a linguagem de programao PASCAL [?], que foi originalmente projetada, na dcada de 1970, especialmente para o ensino de programao. Dentre esses livros, citamos [?,

?].

A dcada de 1970 marcou o perodo da chamada programao estruturada [?], que demonstrou os mritos de programas estruturados, em contraposio programao baseada em linguagens de mais baixo nvel, mais semelhantes a linguagens de montagem ou linguagens de mquina. A linguagem Pascal, assim como outras linguagens tambm baseadas no paradigma de programao imperativo, como

C [?], so ainda certamente as mais usadas no ensino introdutrio de programao

de computadores. Livros de introduo programao de computadores baseados no uso de Pascal ou de linguagens similares, escritos em lngua portuguesa, incluem, por exemplo, [?]. A partir do incio da dcada de 1980, o desenvolvimento de software, em geral, e as linguagens de programao, em particular, passaram a explorar a idia de decomposio de um sistema em partes e o conceito de mdulo , originando o estilo de programao modular . As novas linguagens de programao desenvolvidas, tais como Modula-2 [?], Ada [?] e, mais tarde, Modula-3 [?], passaram a oferecer recursos para que partes de um programa pudessem ser desenvolvidas e compiladas separadamente, e combinadas de forma segura.

2.6 Notas Bibliogrcas

23

A programao orientada por objetos, que teve sua origem bastante cedo, com a linguagem Simula [?], s comeou a despertar maior interesse a partir da segunda metade da dcada de 1980, aps a denio da linguagem e do ambiente de programao Smalltalk [?]. A linguagem Smalltalk teve grande inuncia sobre as demais linguagens orientadas por objeto subseqentes, tais como C++ [?], Eiel [?] e Java [?]. O grande trunfo da programao orientada por objetos, em relao programao modular, o fato de explorar mais o conceito de tipo , no sentido de que uma parte de um programa, desenvolvida separadamente, constitui tambm um tipo, que pode ser usado como tipo de variveis e expresses de um programa. A inuncia da linguagem Java se deve, em grande parte, ao enorme crescimento do interesse por aplicaes voltadas para a Internet, aliado s caractersticas do sistema de tipos da linguagem, que favorecem a construo de programas mais seguros, assim como ao grande nmero de classes e ferramentas existentes para suporte ao desenvolvimento de programas nessa linguagem. O recente aumento do interesse por linguagens funcionais devido, em grande parte, aos sistemas de tipos dessas linguagens. Os sistemas de tipos de linguagens funcionais modernas, como ML [?] e Haskell [?,

?], possibilitam a denio e uso de tipos e funes polimrcas, assim como

a inferncia automtica de tipos. Uma funo polimrca uma funo que opera sobre valores de tipos diferentes  todos eles instncias de um tipo polimrco mais geral. No caso de polimorsmo paramtrico, essa funo apresenta um comportamento uniforme para valores de qualquer desses tipos, isto , comportamento independente do tipo especco do valor ao qual a funo aplicada. De maneira semelhante, tipos polimrcos so tipos parametrizados por variveis de tipo, de maneira que essas variveis (e os tipos polimrcos correspondentes) podem ser instanciadas, fornecendo tipos especcos. Em pouco tempo de contacto com a programao funcional, o programador capaz de perceber que os tipos das estruturas de dados e operaes usadas repetidamente na tarefa de programao so, em sua grande maioria, polimrcos. Por exemplo, a funo que calcula o tamanho (ou comprimento) de uma lista polimrca, pois seu comportamento independe do tipo dos elementos da lista. Essas operaes, usadas com freqncia em programas, so tambm, comumente, funes de ordem superior, isto , funes que recebem funes como parmetros ou retornam funes como resultado. Como um exemplo bastante simples, a operao de realizar uma determinada operao sobre os elementos de uma estrutura de dados pode ser implementada como uma funo polimrca de ordem superior, que recebe como argumento a funo a ser aplicada a cada um dos elementos dessa estrutura. A inferncia de tipos, introduzida pioneiramente na linguagem ML, que tambm introduziu o sistema de tipos polimrcos, possibilita combinar duas caractersticas convenientes: a segurana e maior ecincia proporcionadas por sistemas com vericao de erros de tipo em tempo de compilao e a exibilidade de no requerer que tipos de variveis e expresses de um programa sejam especicados explicitamente pelo programador (essa facilidade era anteriormente encontrada apenas em linguagens que realizam vericao de tipos em tempo de execuo, as quais so, por isso, menos seguras e menos ecientes). Estes e vrios outros temas interessantes, como o uso de estratgias de avaliao de expresses at ento pouco exploradas e o modelamento de mudanas de estado em programao funcional, so ainda objeto de pesquisas na rea de projeto de linguagens de programao. O leitor interessado nesses temas certamente encontrar material motivante nos livros sobre programao em linguagem funcional mencionados acima. Ao leitor interessado em aprender mais sobre o paradigma de programao em lgica e a linguagem Prolog recomendamos a leitura de [?,

?].

Um bom estilo de programao no imprescindivel para fazer com que os nossos programas funcionem. No entanto, ele facilita a compreenso de um programa e por tanto, a sua depurao e manuteno. O estilo inclui tanto regras de noemeao de variveis e funes quanto a indentao e como comentar adequadamente seu cdigo. Existem vrios bons livros de estilo de programao. Uma boa recomendao o captulo 1 do livro The Practice of Programming (A prtica da programao) de Brian Kernighan e Rob Pike.

24

O Paradigma Imperativo de Programao

Captulo 3

Programando em C
Nessa seo voc vai escrever seu primeiro programa preferido e digite o seguinte texto:

C!

Para isto abra o seu editor de texto

/** Vamos executar o nosso primeiro programa C */ #include <stdio.h> int main(void) { printf("Meu primeiro programa C\n"); return 0; }
Agora salve-o com algum nome e a extenso

.c .

Como voc, leitor atento, deve lembrar, o prximo

passo para traduzir o nosso programa de forma que o computador possa entend-lo a compilao. Para isto, voc pode digitar no shell do Linux

gcc nome_do_seu_arquivo.c -o nome_do_executavel


Pronto, agora s tem que executar o programa digitando no seu shell

./nome_do_executavel.
(Seno preste

Se na tela apareceu a frase Meu primeiro programa C voc est de parabns! ateno ao cdigo digitado e tente de novo, no sinta vergonha de perguntar!) Agora que seu programa funcionou vamos olh-lo de perto. comentrio.

O programa comea com um

Comentrios so adicionados a um programa para tornar mais fcil a sua leitura.

Eles no tm nenhum efeito sobre o comportamento do programa, quando esse executado. Nos exemplos apresentados neste livro, comentrios so muitas vezes omitidos, uma vez que a funo e o signicado dos programas so explicados ao longo do texto. Existem dois tipos de comentrios em com

C. O primeiro comea com os caracteres /* e termina */  qualquer sequncia de caracteres entre /* e */ faz parte do comentrio. O comentrio //
em uma dada linha e termina no nal dessa linha.

no incio do nosso programa um exemplo desse tipo de comentrio. O segundo tipo de comentrio comea com os caracteres chamada stdio. Em Na primeira linha tem uma ordem para o preprocessador incluir cdigo de uma biblioteca C

C, assim como em grande parte das linguagens de programao, os mecanismos

de E/S no fazem parte da linguagem propriamente dita, mas de uma biblioteca padro, que deve ser implementada por todos os ambientes para desenvolvimento de programas na linguagem. Essa biblioteca chamada de stdio . Essa biblioteca necessria porque ela contm o cdigo da funo

printf que usaremos para imprimir o texto que voc viu sair na tela.
A seguir, declarada a funo main , que a funo que inicia a execuo do programa. denies de funes forme um programa A denio de uma funo de nome main deve sempre estar presente, para que uma sequncia de

C.

Logo aps a denio da funo aparece uma chave e uma sequncia de comandos. seqncia de comandos entre chaves chamada de bloco . Note que, em lugar de um comando o caractere  ; no deve ser usado aps o seja um bloco.

C, se um bloco for usado no mesmo: o caractere  ; usado

Uma

como um terminador de comandos, devendo ocorrer aps cada comando do programa, que no

26

Programando em C

O corpo de essa funo est composto pela chamada a funo printf, que quando executada ir imprimir o nosso texto, e o return que indica o m da execuo da funo e o retorno do controle ao shell. Voc deve ter notado que os comandos nesse programa esto organizados seguindo uma com-

posio seqencial. A composio seqencial a forma mais simples de combinao de comandos.


A composio seqencial de dois comandos

c1

c2

escrita na forma:

c1 ; c2 ;
e consiste na execuo de Os comandos seqencial. A composio seqencial de comandos naturalmente associativa (no sendo permitido o uso de parnteses). Por exemplo, a seqncia de comandos:

c1

e, em seguida, do comando

c2 .

c1

c2

podem, por sua vez, tambm ser formados por meio de composio

int
tem o efeito de:

a;

int b ; int c ;

= 10;

= 20;

+ b;

1. Declarar uma varivel, de nome a , como tendo tipo declarado. 2. Analogamente, declarar variveis b e c , de tipo 3. Em seguida, atribuir o valor 4. Em seguida, atribuir o valor 5. Em seguida, atribuir o valor

int.

Declarar uma varivel signica

alocar uma rea de memria e associar um nome a essa rea, que pode conter valores do tipo

int.

10 20 30,

varivel a. varivel b. resultante da avaliao de a

b, varivel c.

(GOSTEI DA IDEIA MAS NAO DO TEXTO A SEGUIR. PORFA REESCREVER) Voc deve concordar com que o exemplo mostra um programa bastante simples. Imagina o que seria escrever e/ou entender um programa complexo em que cada um dos seus comandos e expresses est escrito de forma sequencial, um tras outro? Na realidade, bons programas en uma sequncia de uma ou mais chamadas a funes. Linguagens de programao de alto nvel oferecem construes para denio de novas funes , que podem ento ser usadas em expresses desse programa, aplicadas a argumentos apropriados. Dizemos que uma funo constitui uma abstrao sobre uma expresso, uma vez que representa uma expresso, possivelmente parametrizada sobre valores que ocorrem nessa expresso. O uso de uma funo em uma expresso tambm chamado, em computao, de uma chamada a essa funo. De maneira anloga, um programa pode tambm incluir denies de procedimentos , que constituem abstraes sobre comandos  ou seja, um procedimento consiste em uma seqncia de comandos, possivelmente parametrizada sobre valores usados nesses comandos, podendo ser chamado em qualquer ponto do programa em que um comando pode ser usado. A possibilidade de denio e uso de funes e procedimentos constitui um recurso fundamental para decomposio de programas, evitando duplicao de cdigo e contribuindo para construo de programas mais concisos e legveis, assim como mais fceis de corrigir e modicar. O termo funo costuma tambm ser usado no lugar de procedimento , uma vez que em geral uma funo pode usar efeitos colaterais (comandos de atribuio e outros comandos que alteram o valor de variveis) em expresses. A Figura 3.1 apresenta denies de funes para soluo de cada um dos problemas relacionados acima. Cada uma das funes opera apenas sobre valores inteiros e tem denio bastante simples, baseada apenas no uso de outras funes, predenidas na linguagem, ou denidas no prprio programa. A denio de cada funo obedece seguinte estrutura padro de declaraes de funes:

consistem em

27

/******************************************************** * * * Primeiros exemplos * * * * Definies de funes * *-*/ int


quadrado

(int

x)

{ return

x *x ;

int somaDosQuadrados (int x, int y) { return (quadrado (x ) + quadrado (y)); } int tresIguais (int a, int b, int c ) { return ((a ==b ) && (b ==c )); } int eTriang (int a, int b, int c ) { // a, b e c positivos e // cada um menor do que a soma dos outros dois return (a >0) && (b >0) && (c >0) && (a<b+c) && (b<a+c) && (c<a+b); }

Figura 3.1: Denies de funes: primeiros exemplos em

1. Inicialmente especicado o tipo do valor fornecido como resultado, em uma chamada (aplicao) da funo. O tipo do valor retornado por cada funo declarada acima

int

(os nomes das funes so

quadrado , somaDosQuadrados , tresIguais , max e max3 ).


2. Em seguida vem o nome da funo, e depois, entre parnteses, a lista dos seus parmetros (que pode ser vazia). A especicao de um parmetro consiste em um tipo, seguido do nome do parmetro. A especicao de cada parmetro separada da seguinte por uma vrgula. Por exemplo, a declarao de somaDosQuadrados especica que essa funo tem dois parmetros: o primeiro tem tipo

int

e nome x , o segundo tambm tem tipo

int

e nome y.

3. Finalmente, denido o corpo do mtodo, que consiste em um bloco, ou seja, uma sequncia de comandos, delimitada por  { (abre-chaves) e  } (fecha-chaves).

A execuo do corpo do mtodo quadrado 

{ return

 retorna o quadrado do

valor (x) passado como argumento em uma chamada a esse mtodo. O valor retornado pela execuo de uma chamada a um mtodo determinado pela expresso que ocorre como argumento do comando da execuo de um comando da forma:

return, que deve ocorrer no corpo desse mtodo. return e;

O efeito

o de avaliar a expresso retornando esse valor.

e,

obtendo um determinado valor, e nalizar a execuo do mtodo,

Uma funo denida em um programa pode ser usada do mesmo modo que funes predenidas na linguagem. Por exemplo, a funo quadrado usada na denio da funo somaDosQuadrados, assim como o operador predenido

+, que representa a operao (ou funo) de adio de inteiros.

28

Programando em C

avaliao da expresso quadrado (3)

o que signica executar o comando

+ quadrado (4) consiste em avaliar a expresso quadrado (3), return 3 * 3, que retorna 9 como resultado da chamada quadrado (3); em seguida, avaliar a expresso quadrado (4), de maneira anloga, retornando 16; e nalmente avaliar a expresso 9 + 16, o que fornece o resultado 25.
O nmero e o tipo dos argumentos em uma chamada de mtodo devem corresponder ao nmero e tipo especicados na sua denio. Por exemplo, em uma chamada a tresIguais , devem existir trs expresses

e1 , e2

e3 ,

como a seguir:

tresIguais

(e1 ,e2 ,e3 ) int int ou, em outras


pode ocorrer.

Essa chamada pode ser usada em qualquer contexto que requer um valor de tipo palavras, em qualquer lugar onde uma expresso de tipo

3.1 Exerccios Resolvidos


1. Simplique a denio da funo tipoTriang de modo a utilizar um menor nmero de operaes (de igualdade e de disjuno), supondo que, em toda chamada a essa funo, os argumentos correspondentes aos parmetros a , b e c so passados em ordem no-decrescente. Escreve um programa que leia trs valores inteiros e imprima uma mensagem que indique se eles podem ou no ser lados de um tringulo e, em caso positivo, se o tringulo formado equiltero, issceles ou escaleno.

Soluo :

enum TipoTriang { NaoETriang, Equilatero, Isosceles, Escaleno }; enum TipoTriang tipoTriang (int a, int b, int c ) { return (eTriang(a ,b ,c )) ? NaoETriang : (a == b && b == c ) ? Equilatero : (a == b || b == c || a == c ) ? Isosceles : Escaleno ); } char* show (enum TipoTriang t) { return (t ==Equilatero ? "equilatero": t ==Isosceles ? "isosceles": "escaleno"); } int main () { int a, b, c ; printf ("Digite 3 valores inteiros: "); scanf ("%d %d %d", &a, &b, &c ); enum Tipotriang t = tipoTriang (a, b, c ); printf ("Os valores digitados "); if (t == NaoETriang ) printf ("nao podem formar um triangulo\n"); else printf ("formam um triangulo %s\n", show (t )); }
O valor retornado pela funo show um string . Em valor de tipo mais detalhadamente na seo 6.4.

C, um string um ponteiro para um char (ou, equivalentemente em C, um arranjo de caracteres). Isso ser explicado 0 && 0/0 == 0
tem resultado diferente

2. Como vimos na Seo 2.2, a avaliao da expresso


da avaliao da expresso

|,

0 & 0/0 == 0.

Construa duas expresses anlogas, usando

||

para as quais a avaliao fornece resultados diferentes.

Soluo:

3.2 Exerccios

29

1 || 1 |

0/0 == 0 0/0 == 0

A avaliao da primeira expresso fornece resultado verdadeiro, e a avaliao da segunda provoca a ocorrncia de um erro.

3. Dena os operadores lgicos


mesmo comportamento que:

&&

||

usando uma expresso condicional.

Soluo: Para quaisquer expresses booleanas a e b , a operao de conjuno a

&&

b tem o

: 0

Note que o valor da expresso falso quando o valor de a falso, independentemente do valor de b (ou mesmo de a avaliao de b terminar ou no, ou ocasionar ou no um erro). Antes de ver a resposta abaixo, procure pensar e escrever uma expresso condicional anloga usada acima, mas que tenha o mesmo comportamento que a A expresso desejada (que tem o mesmo signicado que a

||

b.

||

b ) :

1 :

3.2 Exerccios
1. Sabendo que a ordem de avaliao de expresses em

da esquerda para a direita, respei-

tando contudo a precedncia de operadores e o uso de parnteses, indique qual o resultado da avaliao das seguintes expresses (consulte a Tabela 2.3, se necessrio): (a) (b) (c) (d)

2 + 4 - 3 4 - 3 * 5 (4 - 1) * 4 - 2 2 >= 1 && 2 != 1 true false

2. A funo eQuadrado , denida a seguir, recebe como argumentos quatro valores inteiros e
retorna se esses valores podem formar os lados de um quadrado, e em caso contrrio.

int eQuadrado (int a ,int b ,int c ,int d ) { // todos os valores so positivos, e iguais entre si return (a >0) && (b >0) && (c >0) && (d >0) && (a ==b && b ==c && c ==d); }

Escreva uma denio de funo que, dados quatro nmeros inteiros, retorna verdadeiro se eles podem representar os lados de um retngulo, e falso em caso contrrio. Escreva um programa que leia quatro valores inteiros e imprima uma mensagem indicando se eles podem ou no os lados de um retngulo, usando a funo acima.

3. A seguinte denio de funo determina se um dado valor inteiro positivo representa um


ano bissexto ou no. No calendrio gregoriano, usado atualmente, um ano bissexto se for divisvel por 4 e no for divisvel por 100, ou se for divisvel por 400.

30

Programando em C

int bissexto (int ano ) { return ((ano % 4 == 0 && ano % 100 != 0) || ano % 400 == 0 ); }
Reescreva a denio de bissexto de maneira a usar uma expresso condicional em vez de usar, como acima, os operadores lgicos

&&

||.

Escreva um programa que leia um valor inteiro e responda se ele ou no um ano bissexto, usando a funo denida acima com a expresso condicional.

4. Dena uma funo somaD3 que, dado um nmero inteiro representado com at trs algarismos, fornece como resultado a soma dos nmeros representados por esses algarismos. Exemplo: somaD3 (123) deve fornecer resultado

6.

Escreva um programa que leia um valor inteiro, e imprima o resultado da soma desses 3 algarismos, usando a funo somaD3. Por simplicidade, voc pode supor que o inteiro lido contm apenas 3 algarismos (o seu programa deve funcionar corretamente apenas nesse caso).

5. Dena uma funo inverteD3 que, dado um nmero representado com at trs algarismos,
fornece como resultado o nmero cuja representao obtida invertendo a ordem desses algarismos. Por exemplo: o resultado de inverteD3 (123) deve ser

321.

Escreva um programa que leia um valor inteiro, e imprima o resultado de inverter esses algarismos, usando a funo inverteD3. Por simplicidade, voc pode supor que o inteiro lido contm apenas 3 algarismos (o seu programa deve funcionar corretamente apenas nesse caso).

6. Considere a seguinte denio, que associa a pi o valor

3.1415:

final float
ximado de uma circunferncia, dado o raio.

pi

= 3.1415f;

Use essa denio do valor de pi para denir uma funo que retorna o comprimento apro-

Escreva um programa que leia um nmero de ponto utuante, e imprima o comprimento de uma circunferncia que tem raio igual ao valor lido, usando a funo denida acima. (o seu programa deve funcionar corretamente apenas nesse caso). Por simplicidade, voc pode supor que o valor lido um nmero de ponto utuante algarismos

7. Dena uma funo que, dados cinco nmeros inteiros, retorna verdadeiro (inteiro diferente
de zero) se o conjunto formado pelos 2 ltimos nmeros um subconjunto daquele formado pelos 3 primeiros, e falso em caso contrrio. Escreva um programa que leia 5 valores inteiros, e imprima o resultado de determinar se o conjunto formado pelos 2 ltimos um subconjunto daquele formado pelos trs primeiros, usando a funo denida acima. Por simplicidade, voc pode supor que os valores lidos so todos inteiros (o seu programa deve funcionar corretamente apenas nesse caso).

8. Dena uma funo que, dado um valor inteiro no-negativo que representa a nota de um
aluno em uma disciplina, retorna o caractere que representa o conceito obtido por esse aluno nessa disciplina, de acordo com a tabela:

Nota
0 a 59 60 a 74 75 a 89 90 a 100

Conceito
R C B A

Escreva um programa que leia um valor inteiro, e imprima a nota correspondente, usando a funo denida acima. Por simplicidade, voc pode supor que o valor lido um valor inteiro (o seu programa deve funcionar corretamente apenas nesse caso).

3.2 Exerccios

31

9. Dena uma funo que, dados dois caracteres, cada um deles um algarismo, retorna o maior
nmero inteiro que pode ser escrito com esses dois algarismos. Voc no precisa considerar o caso em que os caracteres dados no so algarismos. Escreva um programa que leia 2 caracteres, cada um deles um algarismo, e imprima o maior nmero inteiro que pode ser escrito com esses dois algarismos. Por simplicidade, voc pode supor que os valores lidos so de fato um caractere que um algarismo (o seu programa deve funcionar corretamente apenas nesse caso).

10. Escreva uma funo que, dados um nmero inteiro e um caractere  representando respectivamente a altura e o sexo de uma pessoa, sendo o sexo masculino representado por

'm'

e o sexo feminino representado por

'F'

ou

'f'

'M'

ou

, retorna o peso supostamente ideal

para essa pessoa, de acordo com a tabela: homens mulheres

(72, 7 altura) 58

(62, 1 altura) 44, 7

Escreva um programa que leia um nmero inteiro e um caractere, que voc pode supor que sejam respectivamente o peso de uma pessoa e um dos caracteres dentre e imprima o peso ideal para uma pessoa, usando a funo denida acima. Voc pode supor

'M', 'm', 'F', 'f',

que os dados de entrada esto corretos (o seu programa deve funcionar corretamente apenas nesse caso).

32

Programando em C

Captulo 4

Escolha condicional
Frequentemente precisamos codicar decises do tipo: se o aluno no entregou a lista a sua

nota 0. A seleo, ou controle de uxo, outra forma de combinao de comandos que possibilita
selecionar um ou outro grupo de instrues para execuo de forma que

Se (condio) ento executa_bloco_de_comandos Seno executa_outro_bloco_de_comandos Fim Se


segundo o valor da condio seja verdadeiro ou falso. Em

C,

a seleo feita com o comando

if,

que tem a seguinte forma:

if (
Nesse comando,

) c1 ; else c2 ; C
no tem um tipo para representar

uma expresso lgica. J que linguagem

diretamente os valores verdadeiro ou falso, se usa para isso o tipo um contexto como o da expresso valor retornado pela avaliao de o valor retornado for

int.

A conveno usada que, em

b,

em que um valor verdadeiro ou falso esperado,

representa

falso e qualquer valor diferente de zero representa verdadeiro. Na execuo do comando

0,

for qualquer valor diferente de

0, o comando c1

if,

se o

executado; se

o comando

c2

executado.

Por exemplo, como resultado da execuo o seguinte comando:

if (aluno_nao_submeteu_lista) { nota_aluno = 0; }
atribudo a varivel nota_aluno o valor 0 caso a condio aluno_nao_submeteu_lista for verdadeira. Por outro lado, no seguinte exemplo:

if (nota_aluno >= 60){ printf("Aprovado"); } else { printf("Reprovado"); }


queremos que um de dois blocos seja executado dependendo da condio. contrrio. A parte  else Em Assim, na tela ser impresso Aprovado (caso o valor de nota_aluno seja maior ou igual a 60) ou Reprovado caso

c2 ;

(chamada de clusula

else)

opcional. Se no for especicada, simples-

mente nenhum comando executado no caso em que a avaliao da expresso

b retorna falso. C, para se usar uma seqncia com mais de um comando no lugar de c1 , ou no lugar de c2 ,

devem ser usadas chaves para indicar o incio e o m da seqncia de comandos. Por exemplo:

34

Escolha condicional

if (a > 10) { a = a + 10; b = b + 1; } else { b = 0; if (c > 1) a = a + 5; }


Existe tambm outra forma de comando de seleo, que seleciona um comando para ser executado, de acordo com o valor de uma expresso, dentre uma srie de possibilidades, e no apenas duas, como no caso do comando

if.

Esse comando, tratado no Exerccio Resolvido 6 do Captulo

5, tem o mesmo efeito que uma seqncia de comandos condio especicada nesse comando.

if,

com testes sucessivos sobre o valor da

Neste captulo, introduzimos um primeiro conjunto de problemas, e exploramos um raciocnio simples de construo de algoritmos. A soluo de cada um desses problemas expressa na forma de uma denio de funo, na linguagem

C.

O nosso primeiro problemas pode ser especicado como Dados dois nmeros inteiros, retornar o mximo entre eles. Para resolv-lo devemos primeiramente entender o problema. A entrada da nossa funo so dois nmeros inteiros irrestritos. A sada um valor inteiro que se corresponde com o maior deles. Pense bem: os nmeros podem ser iguais? J que o problema no restringe esse caso, podemos denir como pre-condio do nosso problema (a condio que deve cumprir a entrada para podermos garantir a sada desejada pelo usurio) que ambos os nmeros so diferentes. Nesse caso, para retornar o maior deles devemos compar-los e retornar o primeiro se ele for o maior ou o segundo caso contrrio. Uma possvel codicao para esta soluo mostrada a seguir:

/* Funo max * Pre-condio: Recebe dois nmeros inteiros diferentes * Post-condio: O valor retornado o maior desses nmeros */ int max (int a, int b ) { if (a > b ) return a; else return b; }

O nosso segundo problema Dados trs nmeros inteiros, retornar o mximo entre eles resolve o problema anterior para o caso de trs nmeros. J que o problema de encontrar o mximo de dois nmeros pode ser resolvido usando a funo max, podemos us-la para resolver este problema tambm: repare que o maior de trs nmeros o maior entre qualquer um deles e o maior dos outros dois:

int max3 (int a, int b, int c) { return (max (max (a ,b ),c ));

4.1 Exerccios

Captulo 5

Recurso e Iterao
5.1 Repetio
Em um comando de repetio, um determinado comando, chamado de corpo do comando de repetio, executado repetidas vezes, at que uma condio de terminao do comando de repetio se torne verdadeira, o que provoca o trmino da execuo desse comando. Cada avaliao da condio de terminao, seguida da execuo do corpo desse comando, denominada uma iterao , sendo o comando tambm chamado comando iterativo . Para que a condio de terminao de um comando de repetio possa tornar-se verdadeira, depois de um certo nmero de iteraes, preciso que essa condio inclua alguma varivel que tenha o seu valor modicado pela execuo do corpo desse comando (mais especicamente exista um comando de atribuio no corpo do comando de repetio que modique o valor de uma varivel usada na condio de terminao). O comando

while

um comando de repetio, que tem, em

C,

a seguinte forma, onde

condio de terminao, e

c,

o corpo do comando:

while ( b ) c;

c, a b avaliada; se o resultado for verdadeiro (isto , diferente de 0), o comando c executado, e esse processo se repete; seno (i.e., se o resultado da avaliao de b for falso (igual a 0), ento a 1 execuo do comando while termina.
A execuo desse comando consiste nos seguintes passos: antes de ser executado o corpo condio

loop

1 Devido

a esse lao envolvendo

e depois

repetidamente, um comando de repetio tambm chamado de

(fala-se lup), que signica lao, em ingls.

36

Recurso e Iterao

Como exemplo de uso do comando

varivel soma a soma dos valores inteiros de

while, considere o seguinte trecho de programa, que atribui 1 a n:

soma i

= 0; = 1; while ( i <= n ) { soma = soma + i; i = i + 1; } 0


armazenado em soma,

Essa seqncia de comandos determina que, primeiramente, o valor depois o valor

armazenado em i, e em seguida o comando

0), a execuo do comando termina. Caso contrrio, o while executado, seguindo-se novo teste etc. A execuo do corpo do comando while adiciona i ao valor da varivel soma , e adiciona 1 ao valor armazenado na varivel i, nessa ordem. Ao nal da n-sima iterao, o valor da varivel i ser, portanto, n+1. A avaliao da condio de terminao, no incio da iterao seguinte, retorna ento o valor falso (0, e a execuo do comando while termina. A linguagem C possui dois outros comandos de repetio, alm do comando while: os comandos do-while e for. Esses comandos tm comportamento semelhante ao do comando while, e so
Se o resultado desse teste for falso (igual a corpo do comando abordados no Captulo 5. Os conceitos de recurso e iterao constituem, juntamente com as noes de composio seqencial e seleo, as ferramentas fundamentais para construo de algoritmos e programas, a partir de um conjunto apropriado de operaes ou comandos bsicos. programas para clculo de operaes aritmticas simples, tais como: Esses dois conceitos so introduzidos neste captulo, por meio de uma srie de exemplos ilustrativos, de construo de

Na execuo do comando

while,

while

executado.

primeiramente testado se o valor de i menor ou igual a n.

clculo da multiplicao de dois nmeros inteiros, expressa em termos da operao de adio; clculo do resultado da operao de exponenciao, com um expoente inteiro, expressa em termos da operao de multiplicao; clculo do fatorial de um nmero inteiro; clculo de somas de sries numricas.

A soluo de qualquer problema que envolva a realizao de uma ou mais operaes repetidas vezes pode ser expressa, no paradigma de programao imperativo, por meio de um comando de repetio (tambm chamado de comando iterativo, ou comando de iterao), ou usando funes com denies recursivas. Denies recursivas de funes so baseadas na mesma idia subjacente a um princpio de prova fundamental em matemtica  o princpio da induo . A idia a de que a soluo de um problema pode ser expressa da seguinte forma: primeiramente, denimos a soluo desse problema para casos bsicos; em seguida, denimos como resolver o problema para os demais casos, em termos da soluo para casos mais simples que o original.

5.2 Multiplicao e Exponenciao


Considere por exemplo o problema de denir a multiplicao de dois nmeros inteiros sendo qual

m e n, n > 0, em termos da operao de adio. O caso mais simples dessa operao aquele no n = 0: nesse caso, o resultado da operao igual a 0, independentemente do valor de m. De n > 0,
supondo que sabemos determinar sua soluo para casos Neste caso, podemos expressar o resultado de ou seja, podemos denir

acordo com a idia exposta acima, precisamos agora pensar em como podemos expressar a soluo desse problema no caso em que mais simples, que se aproximam do caso bsico.

m n em termos do resultado da operao, mais simples m (n 1); m n como sendo igual a m + (m (n 1)), para n > 0.

5.2 Multiplicao e Exponenciao

37

/*************************************************** int mult (int m, int n) { int r =0, i ; for (i =1; i <=n ; i ++) r += m; return r; } int multr (int m, int n) { if (n ==0) return 0; else return (m + multr (m , } int exp (int m, int n ) { int r =1, i ; for (i =1; i <=n ; i ++) r *=m; return r; } int expr (int m, int n ) { if (n ==0) return 1; else return (m * expr (m, n -1)); } ***************************************************/

n -1));

Figura 5.1: Primeiros exemplos de denies recursivas e baseadas em comandos de repetio

Ou seja, a operao de multiplicao pode ento ser denida indutivamente pelas seguintes equaes:

m0=0 m n = m + (m (n 1))
plicao

se

n>0 m,
isto :

Uma maneira alternativa de pensar na soluo desse problema seria pensar na operao de multi-

m*n

como a repetio,

vezes, da operao de adicionar

m n = m + ... + m
n vezes
Raciocnio anlogo pode ser usado para expressar a operao de exponenciao, com expoente inteiro no-negativo, em termos da operao de multiplicao. A Figura 5.1 apresenta duas denies alternativas, na linguagem

C, para computar o resultado

da multiplicao e da exponenciao de dois nmeros, dados como argumentos dessas operaes. As funes mult e exp so denidas usando-se um comando de repetio. As funes multr e expr so denidas usando-se chamadas prpria funo que est sendo denida, razo pela qual essas denies so chamadas de recursivas . Usamos a letra  r  adicionada como suxo ao nome de uma funo para indicar que se trata de uma verso recursiva da denio de uma funo. Como vimos na Seo 5.1, a execuo de um comando de repetio consiste na execuo do corpo desse comando repetidas vezes, at que a condio associada a esse comando se torne falsa (caso isso nunca ocorra, a repetio prossegue indenidamente). Para entender melhor o signicado desse comando e, portanto, o comportamento das funes mult e exp , consideramos seguir a execuo de uma chamada funo mult  por exemplo, mult (3,2). denies recursivas e comandos de repetio. Note que essas denies so usadas apenas com ns didticos, para explicar inicialmente existe, no caso da multiplicao, o operador predenido (*) e, no caso da exponenciao, a funo Essas denies no tm utilidade prtica pois

math.pow (cf. seo ??).

38

Recurso e Iterao

A execuo de uma chamada de funo iniciada, como vimos na Seo 3, com a avaliao dos argumentos a serem passados funo: no exemplo acima, essa avaliao fornece os valores representados pelos literais

3 e 2,

do tipo

int.

Esses valores so atribudos aos parmetros m e n ,

respectivamente, e o corpo da funo mult ento executado. A execuo do corpo da funo mult iniciada com a criao da varivel r , qual atribudo inicialmente o valor O comando

for

0.

Em seguida, o comando

for

executado.

um comando de repetio (assim como o comando

Seo 5.1). A execuo do comando

for usado no corpo da funo mult

while,

introduzido na

consiste simplesmente em

adicionar (o valor contido em) m ao valor de r , a cada iterao, e armazenar o resultado dessa adio em it r. O nmero de iteraes executadas igual a n. Por ltimo, o valor armazenado em

r aps a execuo do comando

for

Consideramos, a seguir, o comando O cabealho de um comando

i ++)  constitudo de trs partes (ou clusulas), descritas a seguir:

for mais detalhadamente. for  no exemplo, a parte

retornado, por meio da execuo do comando  return r ;. entre parnteses

(int i =1; i <=n;

a primeira parte, de inicializao , atribui valores iniciais a variveis, antes do incio das iteraes. Essa parte de inicializao s executada uma vez, antes do incio das iteraes. a segunda parte, chamada de teste de terminao , especica uma expresso booleana que avaliada antes do incio de cada iterao (inclusive antes da execuo do corpo do comando

for pela primeira vez). Se o valor obtido pela avaliao dessa expresso for verdadeiro (em C, diferente de zero), ento o corpo do comando for executado, seguido pela avaliao da
terceira clusula do cabealho desse comando, e depois uma nova iterao iniciada; caso contrrio, ou seja, se o valor for falso (em

C,

zero), a execuo do comando

for

termina.

a terceira parte, de atualizao , contm expresses que tm o objetivo de modicar o valor de variveis, depois da execuo do corpo do comando  i ++, que representa o mesmo que  i

= i +1,

for,

a cada iterao. No nosso exemplo,

incrementa o valor de i a cada iterao.

Note a ordem na execuo de cada iterao em um comando atualizao.

for:

teste de terminao, corpo,

cuo da chamada mult (3,2). Em outras palavras, acompanhar, em cada passo da execuo, o

instrutivo acompanhar a modicao do valor armazenado em cada varivel durante a exe-

estado da computao: o estado da computao uma funo que associa um valor a cada varivel.

A Tabela 5.1 ilustra os passos da execuo da chamada mult (3,2), registrando, a cada passo,

o estado da computao. O resultado fornecido pela avaliao de expresses tambm mostrado nessa tabela. Note que o resultado fornecido pela clusula de atualizao de um comando ou mais variveis (ou seja, uma expresso com efeito colateral). A varivel i usada no comando

for

sempre descartado. Essa expresso avaliada apenas com o propsito de atualizar o valor de uma

for

como um contador de iteraes . A denio de multr espelha diretamente a

Considere agora a funo multr (Figura 5.1).

denio indutiva da operao de multiplicao, em termos da adio, apresentada anteriormente. Ou seja, multiplicar m por n (onde n um inteiro no-negativo) fornece:

0,
m

no caso base (isto , se n ==0);

mult (m,n -1), no caso indutivo (isto , se n !=0).

Vejamos agora, mais detalhadamente, a execuo de uma chamada multr (3,2). Cada chamada da funo multr cria novas variveis, de mesmo nome m e n . Existem, portanto, vrias variveis com nomes (m e n ), devido s chamadas recursivas. Nesse caso, o uso do nome refere-se varivel local ao corpo da funo que est sendo executado. As execues das chamadas de funes so feitas, dessa forma, em uma estrutura de pilha. Chamamos, genericamente, de estrutura de pilha uma estrutura na qual a insero (ou alocao) e a retirada (ou liberao) de elementos feita de maneira que o ltimo elemento inserido o primeiro a ser retirado. Como a execuo de chamadas de funes feita na forma de uma estrutura de pilha, em cada instante da execuo de um programa, o ltimo conjunto de variveis alocados na pilha corresponde s variveis e parmetros da ltima funo chamada. No penltimo espao so alocadas as variveis

5.2 Multiplicao e Exponenciao

39

Tabela 5.1: Passos na execuo de mult (3,2)

Comando/ Expresso

Resultado
(expresso)

Estado
(aps execuo/avaliao)

int

mult (3,2) r

...

m m m

= 0 i = 1 i <= n r += m i ++ i <= n r += m i ++ i <= n for ... return r mult (3,2)

verdadeiro

3 2
verdadeiro

m m m m m m m m

6 3
falso

3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,

n n n n n n n n n n n

2 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

r r r r r r r r r r

0 0, 0, 3, 3, 3, 6, 6, 6, 6,

i i i i i i i i i

1 1 1 2 2 2 3 3 3

6
Cada espao de variveis e

e parmetros da penltima funo chamada, e assim por diante.

parmetros alocado para uma funo chamado de registro de ativao dessa funo. O registro de ativao de cada funo desalocado (isto , a rea de memria correspondente na pilha liberada para uso por outra chamada de funo) no instante em que a execuo da funo termina. Como o trmino da execuo da ltima funo chamada precede o trmino da execuo da penltima, e assim por diante, o processo de alocao e liberao de registros de ativao de chamadas de funes se d como em uma estrutura de pilha. As variveis que podem ser usadas no corpo de uma funo so apenas os parmetros dessa funo e as variveis internas a declaradas (chamadas de variveis locais da funo), alm das variveis externas, declaradas no mesmo nvel da denio da funo, chamadas de varivveis

globais.
Variveis declaradas internamente a uma funo so criadas cada vez que uma chamada a essa funo executada, enquanto variveis globais so criadas apenas uma vez, quando a execuo do programa iniciada ou anteriormente, durante a carga do cdigo do programa na memria. No momento da criao de uma varivel local a uma funo, se no foi especicado explicitamente um valor inicial para essa varivel, o valor nela armazenado ser indeterminado, isto , ser o valor j existente nos bytes alocados para essa varivel na pilha de chamadas de funes. Na Figura 5.2, representamos a estrutura de pilha criada pelas chamadas funo multr. Os nomes m e n referem-se, durante a execuo, s variveis mais ao topo dessa estrutura. resultado da expresso, ainda no conhecido, indicado por  .... Uma chamada recursiva que vai comear a ser executada circundada por uma caixa. Nesse caso, o As denies de exp e expr seguem o mesmo padro de denio das funes mult e multr , respectivamente. A denio de expr baseada na seguinte denio indutiva da exponenciao:

m0 = 1 mn = m mn1

se

n>0

Ecincia
Uma caracterstica importante da implementao de funes, e de algoritmos em geral, a sua

ecincia . A denio alternativa da funo de exponenciao apresentada na Figura 5.2 ilustra


bem como implementaes de uma mesma funo, baseadas em algoritmos distintos, podem ter ecincia diferente. Essa nova denio uma implementao de indutiva:

mn

baseada na seguinte denio

m0 = 1 mn = (mn/2 )2 mn = m mn1

se se

n n

par mpar

40

Recurso e Iterao

Tabela 5.2: Passos na execuo de multr (3,2)

Comando/ Expresso
multr n

Resultado
(expresso)

Estado
(aps execuo/avaliao)

(3,2) . . . == 0
falso

m n m n m n m n m n m n m n m n

return m + multr (m,n -1)


n

...
falso

== 0

return m + multr (m,n -1)


n

...
verdadeiro

== 0

return 0 return return


multr m m

+ 0 6

+ 3 (3,2)

3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 2

m n m n m n m n m n

3 1 3 1 3 1 3 1 3 1

m n m n

3 0 3 0

expr (m ,n ), mas de maneira mais eciente  em termos do nmero de operaes necessrias,


e portanto em termos do tempo de execuo.

A avaliao de exp2 (m ,n ) produz o mesmo resultado que a avaliao de exp (m ,n ) e de

a avaliao de exp (m,n ), por outro lado, diminui o valor de

exp2 (m ,n ) baseada em chamadas recursivas que dividem o valor de

Para perceber isso, observe que a avaliao de

por

a cada chamada;

expr (m,n ) diminui o valor de

de 1 a cada iterao (assim como

Por exemplo, as chamadas recursivas que ocorrem durante a avaliao da expresso exp2 (2,20)

de 1 a cada chamada recursiva).

so, sucessivamente, as seguintes:

exp2 (2,20) exp2 (2,10) exp2 (2,5) exp2 (2,4) exp2 (2,2) exp2 (2,1) exp2 (2,0)
A diferena em ecincia se torna mais signicativa quando o expoente da ordem de

log2 (n)

chamadas recursivas durante a execuo de exp2 (m,n )  uma vez que n

n maior.

So realizadas

int exp2 (int m , int n ) { if (n == 0) return 1; else if (n % 2 == 0) { // n par int x = exp2 (m , n /2); return x *x ; } else return m * exp2 (m,n -1); }

Figura 5.2: Implementao mais eciente da operao de exponenciao

5.3 Fatorial

41

em mdia dividido por 2 em chamadas recursivas , ao passo que a execuo de exp (m,n ) requer

n iteraes.
Um exerccio interessante aplicar a mesma tcnica usada na denio de exp2 (ou seja, usar diviso por 2 caso o segundo operando seja par) para obter uma denio mais eciente para a multiplicao (procure resolver esse exerccio antes de ver sua soluo, no Exerccio Resolvido 2). Questes sobre ecincia de algoritmos so importantes em computao, envolvendo aspectos relativos ao tempo de execuo e ao consumo de memria (referidos abreviadamente como aspectos de tempo e espao). A anlise da ecincia de algoritmos abordada supercialmente ao longo deste texto, sendo um estudo mais detalhado desse tema deixado para cursos posteriores.

5.3 Fatorial
Nesta seo, exploramos um pouco mais sobre recurso e iterao, por meio do exemplo clssico do clculo do fatorial de um nmero inteiro no-negativo

n,

usualmente denido como:

n! = n (n 1) (n 2) . . . 3 2 1
Duas formas comuns para implementao dessa funo so mostradas a seguir:

int fat (int n) { int f =1, i ; for (i =1; i <=n ; i ++) return f; }

*= i;

int fatr (int n) { if (n == 0) return 1; else return n * fatr (n -1); }

signicado dos trs pontos (...) na denio de

A funo fatr espelha diretamente a seguinte denio indutiva, que especica precisamente o

n!
se

dada acima:

n! = 1 n! = n (n 1)!

n=0

em caso contrrio

varia de

n usando um contador (i), que 1 a n, e uma varivel (f ), que contm o produto 1 2 . . . i, obtido multiplicando o valor de i por f a cada iterao. Mudando o valor do contador e o valor do produto a cada iterao (para valores de i que variam de 1 a n), obtemos o resultado desejado na varivel f (ou seja, n!), ao nal da repetio. Verique que, no caso da execuo de uma chamada fat (0), nenhuma iterao do comando for executada, e o resultado retornado pela funo , corretamente, igual a 1 (valor
A verso iterativa, fat , adota a regra de calcular o fatorial de inicialmente atribudo varivel f ). O exemplo a seguir mostra como pode ser implementada uma verso recursiva do processo iterativo para clculo do fatorial. contm o produto Nesse caso, o contador de repeties (i) e a varivel (f ), que

1 2 . . . i,

so passados como argumentos da funo denida recursivamente,

sendo atualizados a cada chamada recursiva.

42

Recurso e Iterao

int fatIter (int n, int i , int f ) { if (i > n ) return f ; else return fatIter (n,i +1,f *i ); } int fatr1 (int n ) { return fatIter (n,1,1); }

5.4 Obtendo Valores com Processos Iterativos


Outros exemplos de problemas cuja soluo requer o uso de recurso ou iterao so abordados a seguir. De modo geral, na soluo de tais problemas, o valor calculado em uma iterao ou chamada recursiva de uma funo obtido pela aplicao de funes a valores obtidos na iterao ou chamada recursiva anterior. O exemplo mais simples o clculo do somatrio de termos de uma srie aritmtica. exemplo, considere o clculo do somatrio dos termos da srie aritmtica de passo 1, um dado Por n i=1 i, para

n,

implementada a seguir pela funo pa1 :

int pa1 (int n ) { int s = 0; for (int i =1; i <=n; i ++) return s ; }

+= i;

Assim como para o clculo do fatorial, duas verses recursivas, que usam processos de clculo diferentes, podem ser implementadas. Essas denies so mostradas a seguir. A primeira (pa1r ) espelha diretamente a denio indutiva de

n i=1 i, e a segunda (pa1rIter ) espelha o processo iterativo, de maneira anloga usada na denio de fatIter.

int pa1r (int n) } if (n ==0) return 0; else return n + pa1r (n -1); } int pa1Iter (int n, int i, int s ) { if (i > n ) return s; else return pa1Iter (n, i +1, s +i ); } int pa1rIter (int n) { return pa1Iter (n ,1,0); }

simples modicar essas denies para o clculo de somatrios dos termos de sries aritmticas com passo diferente de 1. Essas modicaes so deixadas como exerccio para o leitor. Nenhuma dessas implementaes constitui a maneira mais eciente para calcular a soma de termos de uma srie aritmtica, pois tal valor pode ser calculado diretamente, usando a frmula:

2O

leitor com interesse em matemtica pode procurar deduzir essas frmulas. Conta a histria que a frmula

5.4 Obtendo Valores com Processos Iterativos

43

i=
i=1

n(n + 1) 2

Essas denies servem, no entanto, para a ilustrar a implementao de somatrios semelhantes, como nos exemplos a seguir. Considere a implementao de uma funo para clculo de:

xi
i=0
Tambm nesse caso, podemos usar um comando de repetio, como na implementao de pa1 , sendo adequado agora usar uma varivel adicional para guardar a parcela obtida em cada iterao. Essa varivel chamada parc na implementao apresentada a seguir, que calcula o somatrio das parcelas da srie geomtrica,

n i=0

xi ,

para um dado x e um dado n :

int pg (int n , int x ) { int s = 1, parc = x ; for (int i =1; i <=n; i ++) { s += parc ; parc *= x ; } return s ; }
O uso de parc em pg evita que seja necessrio calcular (contida em parc ), igual a

xi

(ou seja, evita que uma operao de

exponenciao tenha que ser efetuada) a cada iterao. Em vez disso, a parcela da i-sima iterao

xi1 , multiplicada por x

para obteno da parcela da iterao seguinte.

Para a implementao de um somatrio, devemos, portanto, decidir se cada parcela a ser somada vai ser obtida a partir da parcela anterior (e nesse caso devemos declarar uma varivel, como a varivel parc , usada para armazenar o valor calculado para a parcela em cada iterao), ou se cada parcela vai ser obtida por meio do prprio contador de iteraes. Como exemplo desse segundo caso, considere a implementao do seguinte somatrio:

i=1

1 i

Nesse caso, a parcela a ser somada a cada iterao, anterior,

1/(i -1):

1/i , obtida a partir de i, e no da parcela

float somaSerieHarmonica (int n ) { float s = 0.0f; for (int i =1; i <=n; i ++) s += 1/(float)i ; return s ; }
n i=1

i=

matemtica pediu-lhe, como punio, que calculasse a soma dos 1000 primeiros nmeros naturais. frmula em pouco tempo, Gauss respondeu a questo prontamente.

n(n+1) foi deduzida e usada por Gauss quando ainda garoto, em uma ocasio em que um professor de 2

Deduzindo a

De fato, possvel deduzir a frmula rapidamente, mesmo sem uso de papel e lpis, depois de perceber que a soma

+
A deduo da frmula para o leitor.

1 n n xi =

+ +

2 (n 1)

+ +

... ...

+ +

(n 1) 2

+ +

n 1

fornece o mesmo resultado que somar

termos iguais a

n + 1.

n i=0

xn+1 1 pode ser feita com artifcio semelhante, e deixada como exerccio x1 1 , . . . denominada n

3A

seqncia de nmeros

1 1 1, 2 , 3,...,

srie harmnica .

44

Recurso e Iterao

Na maioria das vezes, uma parcela pode ser calculada de maneira mais fcil e eciente a partir da parcela calculada anteriormente. Em vrios casos, esse clculo no usa a prpria parcela anterior, mas valores usados no clculo dessa parcela. Por exemplo, considere o seguinte somatrio, para clculo aproximado do valor de

: = 4 (1 1 1 1 + + . . .) 3 5 7

Nesse caso, devemos guardar no o valor mas o sinal da parcela anterior (para invert-lo) e o denominador usado para clculo dessa parcela (ao qual devemos somar 2 a cada iterao):

float piAprox (int n ) { float s = 0.0f, denom = 1.0f; int for (int i =1; i <=n; i ++) { s += sinal /denom ; sinal = -sinal ; denom += 2; } return 4 * s ; }

sinal

= 1;

Em nosso ltimo exemplo, vamos denir uma funo para calcular um valor aproximado para

ex ,

para um dado

x,

usando a frmula:

ex = 1 + (x1 /1!) + (x2 /2!) + . . .


Para calcular a parcela a ser somada em cada iterao tao usa o denominador um comando

i do

comando de repetio, a implemen-

i!

while

e o numerador

(em vez do

for),

xi ,

calculado na parcela anterior. Optamos pelo uso de

para ilustrao:

float eExp (float x , int n ) { float s = 1.0f; int i =1; float numer = x ; int denom = 1; while (i <=n) { s += numer /denom ; i ++; numer *= x ; denom *= i ; } return s ; } for while C

A deciso entre usar um comando internamente a um comando em

ou um comando

em

, na maioria das vezes,

uma questo de esttica ou de gosto. Existe uma diferena quando um comando

for

ou um comando

while

continue usado

(veja Exerccio Resolvido 10).

Note que os dois comandos a seguir so equivalentes  se nenhum comando

continue usado

c1 :

for (c1 ; b; c2 ) c
c1 ; while (b) { c; c2 ; }
Outro comando de repetio disponvel em forma:

o comando

do-while.

Esse comando tem a

do c; while (b);

5.5 Correo e Entendimento de Programas

45

onde

um comando e

ao comando

while.

uma expresso de tipo boolean. Esse comando bastante semelhante

No caso do comando

vez, antes do teste de terminao imprime os inteiros de

10,

(b).

do-while,

no entanto, o comando

executado uma

Por exemplo, considere o seguinte trecho de programa, que

no dispositivo de sada padro:

int i = 1; do { printf ("%d ",i ); i ++; } while (i <= 10);

5.4.1 No-terminao
Pode ocorrer, em princpio, que a avaliao de certas expresses que contm smbolos denidos recursivamente, assim como a execuo de certos comandos de repetio, no termine. Considere, como um exemplo extremamente simples, a seguinte denio:

int

innito ()

{ return

innito ()

+ 1; } int.
Qual seria esse

Essa declarao especica que a funo innito retorna um valor do tipo

valor? A avaliao de uma chamada funo innito nunca termina, pois envolve, sempre, uma nova chamada a essa funo. O valor representado por uma chamada a essa funo no , portanto, nenhum valor inteiro. Em computao, qualquer tipo, predenido ou declarado em um programa, em geral inclui um valor especial, que constitui um valor indenido do tipo em questo e que representa expresses desse tipo cuja avaliao nunca termina ou provoca a ocorrncia de um erro (como, por exemplo, uma diviso por zero). Muitas vezes, um programa no termina devido aplicao de argumentos a funes cujo domnio no engloba todos os valores do tipo da funo. Essas funes so comumente chamadas, em computao, de parciais . Por exemplo, a denio de fat , dada anteriormente, especica que essa funo recebe como argumento um nmero inteiro e retorna tambm um nmero inteiro. Mas note que, para qualquer n

< 0,

a avaliao de fat (n ) no termina. A denio de fat poderia,

claro, ser modicada de modo a indicar a ocorrncia de um erro quando o argumento negativo. Considere agora a seguinte denio de uma funo  const1  que retorna sempre qualquer argumento:

1,

para

int
A avaliao da expresso:

const1

(int

x)

{ return 1; }

const1 (innito ())


nunca termina, apesar de const1 retornar sempre o mesmo valor (1), para qualquer argumento (o qual, nesse caso, no usado no corpo da funo). Isso ocorre porque, em do incio da execuo do corpo da funo.

C, assim como na grande

maioria das linguagens de programao, a avaliao dos argumentos de uma funo feita antes

5.5 Correo e Entendimento de Programas


Como mencionado no incio do captulo, os conceitos de recurso e iterao constituem, juntamente com as noes de composio seqencial e seleo, as ferramentas fundamentais para construo de algoritmos e programas, a partir de um conjunto apropriado de operaes ou comandos bsicos.

46

Recurso e Iterao

A soluo de qualquer problema que envolva a realizao de uma ou mais operaes repetidas vezes pode ser expressa, no paradigma de programao imperativo, por meio de um comando de repetio ou usando funes com denies recursivas. A escolha de um ou outro mtodo, recursivo ou iterativo, muitas vezes uma questo de estilo, mas diversos aspectos podem inuir na deciso de escolher um ou outro mtodo, principalmente a clareza ou facilidade de entendimento ou de convencimento ou demonstrao da correo, e a ecincia do cdigo gerado. Vamos dar nesta seo uma introduo a aspectos relativos a um entendimento mais preciso e detalhado sobre o comportamento de programas e sobre como convencer (a si prprio e a outros) e demonstrar a correo de programas. Esse assunto importante particularmente para cursos Na prtica, a demonstrao de correo e o de cincia da computao e cursos relacionados.

convencimento de que um programa se comporta como esperado (em particular no caso de programas imperativos, que envolvem mudanas de estado) so obtidos informalmente. Estes devem ser baseados no entanto em argumentao mais precisa e detalhada, como as apresentadas a seguir. Comeamos com denies recursivas (seo 5.5.1) e depois abordamos programas que envolvem mudanas de estado, com o uso de comandos de repetio e de atribuio (seo 5.5.2). Devemos notar que o material desta seo constitui apenas um material bsico sobre o assunto e, como tal:

desconsidera aspectos que envolvem uso de ponteiros e valores de tipo unio (union em que tornam possvel a alterao de uma varivel sem uso do nome desta varivel;

C),

considera que expresses no tm efeito colateral. Trechos de programas com expresses que tm efeito colateral podem ser transformados sem grande diculdade em trechos que primeiro usam comandos para provocar o efeito colateral e modicar o valor armazenado em variveis e em seguida usar as variveis (com os valores modicados).

5.5.1 Denies Recursivas


Denies recursivas de funes so baseadas no princpio de induo, e o entendimento, a correo e demonstraes de propriedades de tais denies recursivas podem ser obtidas por meio de provas por induo. Como um exemplo simples, vamos mostrar, por induo, que as duas denies recursivas fatr e fatr1 denidas na seo 5.3 (copiadas abaixo) retornam o mesmo resultado (n!) quando aplicadas ao mesmo argumento (n).

int fatr (int n) { if (n == 0) return 1; else return n * fatr (n -1); } int fatIter (int n, int i , int f ) { if (i > n ) return f ; else return fatIter (n,i +1,f *i ); } int fatr1 (int n ) { return fatIter (n,1,1); }

Mais precisamente, vamos mostrar o seguinte.

Lema 1. Para todo

inteiro maior ou igual a zero, temos que fatr (n)

fatr1 (n)

= n!. P1
e

Prova : O lema envolve consiste de fato em duas propriedades, que vamos chamar de

P2 :

P1 (n) = (n I n 0) fatr (n) = n! P2 (n) = (n I n 0) fatr1 (n) = n!

5.5 Correo e Entendimento de Programas

47

onde supomos que

representa o conjunto dos valores de tipo

int

n!

denido, para todo

n I, n 0,

como:

n! =
A prova de (!). A prova de

1 n (n 1)! n,

se

n=0

caso contrrio

P1

direta, por induo sobre

uma vez que a denio de fatr consiste simples-

mente em no uso de uma notao diferente (a linguagem

C) usada para se escrever a funo fatorial

Caso base

P1 includa (n = 0): Nesse

a seguir como ilustrao. caso, temos:

fatr (0)

=1 = 0!
para

(def. de fatr ) (def. de

0!)

Caso indutivo (P1 (n) implica fatr (n

P1 (n + 1),

n 0, n + 1 I):
(def. de fatr , (hip.:

+ 1)

= (n + 1) * fatr (n) = (n + 1) * (n!) = (n + 1) (n!) = (n+1)!

n + 1 > 0) )

(1) (2) (3) (4)

(hip. de induo)

implementa

(def. de !)

Para obter

(3),

precisamos supor que no ocorre overow, isto , que a operao de mul-

tiplicao usada em

C ( *)

se comporta como a operao matemtica de multiplicao ().

Em

implementaes que usam 32 bits para armazenamento de nmeros inteiros, essa suposio no vlida (ocorre overow ) para e menor ou igual a 16. Para provar

n 17.

Portanto, de fato, o lema s vlido, em implementaes

que usam 32 bits para armazenamento de valores de tipo

int, para n inteiro maior ou igual a zero


n, i
e

P2 ,

observemos que, em qualquer chamada a fatIter com parmetros

f:

1. A condio

(n 0) (i n + 1) f = (i 1)!
deve ser verdadeira, no incio da execuo de uma chamada funo. Uma condio que deve ser verdadeira no incio da execuo de um comando chamada de

pr-condio .
Analogamente, uma condio que verdadeira no nal da execuo de um comando, se a pr-condio for verdadeira, chamada de ps-condio . O programa deve ser feito de modo a garantir que a pr-condio seja verdadeira na primeira chamada funo. No caso de fatIter , por exemplo, deve ser garantido sempre que caso contrrio a execuo de fatIter no terminar. Sendo a pr-condio verdadeira, podemos vericar facilmente que ela continuar a ser verdadeira, no incio da execuo da chamada recursiva. sendo temos Isto porque, na chamada recursiva,

n > 0,

f os parmetros de nomes i e f em uma chamada que faz esta chamada recursiva, que i = i + 1 e f = f i; como f = (i 1)!, temos que f = i! = (i 1)!.
e

A pr-condio , assim, uma condio invariante em chamadas recursivas a fatIter , desde que seja verdadeira na primeira chamada funo. 2. A expresso

ni+1
sempre no-negativa, se o for na primeira chamada funo, e decresce em cada chamada recursiva funo. Tal expresso, que decrescente mas permanece no-negativa, se for no-negativa na primeira iterao ou chamada recursiva de uma funo, chamada de uma expresso variante . Entenda-se expresso no-negativa decrescente quando se falar de uma expresso variante. A existncia de uma expresso variante garante que uma funo recursiva terminar, caso tal expresso seja no-negativa na primeira chamada funo. As duas condies acima garantem que, quando

i > n,

temos que

i = n + 1 e,

portanto,

f = n!.

48

Recurso e Iterao

5.5.2 Comandos de Repetio


Para expressar o comportamento de comandos, podemos usar o que se chama de semntica

axiomtica , que usa frmulas de correo de comandos , escritas comumente na forma:

{p} c {q }
onde

um comando e

chamada pr-condio e

q so asseres (ou condies, ou frmulas q ps-condio associada ao comando c.

proposicionais). A assero

A pr-condio especica o que deve ser vlido no estado anterior, e a ps-condio o que deve ser vlido no estado posterior, execuo do comando. Mais precisamente, dizemos que:

{p} c {q } parcialmente correta se toda execuo de c que for iniciada em p verdadeiro e terminar, termina em um estado em que q verdadeiro. {p} c {q } totalmente correta se toda execuo de c que for iniciada p verdadeiro terminar em um estado em que q verdadeiro.
parcial.

um estado em que

em um estado em que

Ou seja, correo total inclui a condio de que o comando termina, ao contrrio da correo Vamos mostrar a seguir que a denio iterativa fat dada na seo 5.3 (copiada abaixo) computa o fatorial do argumento fornecido.

int fat (int n) { int f =1, i ; for (i =1; i <=n ; i ++) return f; }

*= i;

Mais precisamente, vamos demonstrar o seguinte (onde valores de tipo

int).

I,

introduzido acima, o conjunto dos

Lema 2. Em um programa sintaticamente correto, o resultado da execuo de fat (n) igual a


para todo

n!,

n I 0.

Para provar este lema, vamos transformar o comando valente. Vamos tambm usar a semntica do comando Todo comando

while

for acima em um comando while while explicada a seguir.

equi-

pode ser descrito como tendo a seguinte semntica:

onde

uma expresso

{p b} c {p} {p b = e} c {e < } p (e 0) {p} while b do c {p (b)} inteira e uma varivel nova, que

no ocorre em

p, b, e

c.

Podemos descrever a regra acima como a seguir:

a premissa invariante

palavras, um invariante durante a execuo do comando

{p b} c {p} estabelece que p uma condio invariante da iterao (ou, em outras while, ou, de forma abreviada, um do comando while).

e ocorre) garantem a terminao da iterao. guardar o valor de e antes da execuo de c (uma vez que no ocorre em c, o valor de o mesmo antes e depois da execuo de c). A premissa {p b = e} c {e < } estabelece que o valor de e decresce a cada iterao. Juntamente com a premissa p (e 0), nenhuma computaco innita de c pode ser realizada (uma vez que e decresce e maior ou igual a zero sempre que o invariante p for verdadeiro).
as outras duas premissas (as premissas nas quais O propsito de

Prova : . . .

5.6 Exerccios Resolvidos

49

5.5.3 Semntica Axiomtica


Podemos especicar a semntica de comandos de modo a facilitar o entendimento e a prova de propriedades que usem tais comandos. baseada em frmulas de correo do tipo ... A maneira de especicar a semntica de comandos chamada de semntica axiomtica . A seguir

{p} c {q }

apresentamos a semntica axiomtica de comandos bsicos comumente usados.

5.5.4 Exemplos
...

5.6 Exerccios Resolvidos


1. Dena recursivamente o signicado do comando

while.

Soluo : Esse comando pode ser denido recursivamente pela seguinte equao:

while (
Obs.: O smbolo

= if (

) { c ; while (

usado acima para denir uma equao matemtica, e no como um

comando de atribuio.

2. Dena uma funo mult2 que use a mesma tcnica empregada na denio de exp2 . Ou
seja, use diviso por 2 caso o segundo operando seja par, para obter uma implementao da operao de multiplicao mais eciente do que a apresentada em multr .

Soluo :

static int mult2 (int m, int n ) { if (n == 0) return 0; else if (n % 2 == 0) { // n par int x = mult2 (m,n /2); return x + x ; } else return m + mult2 (m,n -1); }

3. D uma denio recursiva para uma funo pgr que espelhe o processo iterativo de pg . Em
outras palavras, dena recursivamente uma funo que, dados um nmero inteiro nmero inteiro no-negativo

e um

n,

retorne o valor do somatrio:

xi
i=0
obtendo cada termo do somatrio a partir do termo anterior (pela multiplicao desse termo anterior por x ) e passando como argumento, em cada chamada recursiva, o termo e o somatrio obtidos anteriormente. Escreva um programa que leia repetidamente pares de valores inteiros de

seja zero ou negativo, e imprima o valor do somatrio

n i=0

n,

at que o valor

xi ,

usando a funo pgr .

Soluo :

4 Exemplos

do uso desse algoritmo so encontrados em um dos documentos matemticos mais antigos que se

conhece, escrito por um escrivo egpcio (A'h-mose), por volta de 1700 a.C.

50

Recurso e Iterao

int pgIter (int x , int n, int i, int s , int t ) { if (i > n ) return t ; else { int sx = s *x ; return pgIter (x ,n,i +1,sx ,t +sx ); } } int }
pgr

(int x, int n ) { return pgIter (x ,n ,1,1,1);

int main () { int x,n; while (1) { scanf ("%d %d",&x , &n ); if (n <= 0) break; printf ("%d",pgr (x,n )); } }
4. D uma denio para funo exp2 , denida na Figura 5.2, usando um comando de repetio,
em vez de recurso.

Soluo :

int exp2 (int m , int n ) { int r = 1; while (n != 0) if (n % 2 == 0) { n = n / 2; m = m * m; } else { r = r * m ; n = n - 1; } return r ; }


A denio recursiva apresentada na Figura 5.2 mais simples, pois espelha diretamente a denio indutiva de exp2 , dada anteriormente. no corpo do comando de repetio. A denio acima usa um esquema notrivial (no diretamente ligado denio da funo) para atualizao do valor de variveis

5. Dena uma funo para calcular o mximo divisor comum de dois nmeros inteiros positivos.
Soluo :
O algoritmo de Euclides um algoritmo clssico e engenhoso para clculo do mximo divisor comum de dois nmeros inteiros positivos:

mdc (a, b)

a
mdc (b, a%b)

se

b=0

caso contrrio

O operador de

por

livro

% usado acima como em Java, ou seja, a%b representa o resto da diviso inteira b (a e b so nmeros inteiros). O algoritmo original de Euclides (escrito no famoso Elementos , por volta do ano 3 a.C.) usava mdc (b, a b) em vez de mdc (b, a%b), na
a b ou em caso contrrio. Se a < b, a chamada a por b (por exemplo, mdc (20, 30) o mesmo que mdc (30, 20)).

denio acima, mas o uso da diviso torna o algoritmo mais eciente. Note que esse algoritmo funciona se recursiva simplesmente troca

Em C, podemos escrever ento:

5.6 Exerccios Resolvidos

51

int mdc (int a , int b ) { if (b == 0) return a ; else return mdc (b , a %b ); }


Podemos tambm escrever a funo

mdc

usando um comando de repetio, como a seguir:

int mdc (int a, int b) { int t; if (a <b ) return mdc (b,a ); while (b >0) { t = a; a = b; return a; }

% b; };

6. Dena uma funo calc que receba como argumentos um caractere, que indica uma operao
aritmtica ('+', retornar

'-', '*'

'/'),

e dois valores de ponto utuante, e retorne o resultado da deve retornar

aplicao da operao sobre os dois argumentos. Por exemplo: calc

2.0

e calc

('*', 2.0, 3.0)

Soluo : Vamos ilustrar aqui o uso do comando

6.0. switch,

('+', 1.0, 1.0) switch

deve

existente em Java, que permite tem a forma:

escolher um dentre vrios comandos para ser executado. O comando

switch case case ... case }


O comando

( e) { e1 : c1 ; e2 : c2 ;
en : cn ;

switch pode ser visto como uma seqncia de comandos de seleo if, cada um switch. O signicado de um comando switch o seguinte: a expresso e avaliada e executado o primeiro comando ci , na ordem c1 , . . . , cn , para o qual o valor fornecido por e igual ao valor de ei (caso tal comando exista), sendo tambm executados todos os comandos seguintes (ci+1 , . . . , cn ), se existirem, nessa ordem.
realizando um teste correspondente a um caso do comando A execuo de qualquer desses comandos pode ser nalizada, e geralmente deve ser, por meio de um comando da funo. Supe-se tambm, na funo op denida abaixo, que o caractere passado como argumento para op sempre igual a

break.

No entanto, na soluo desse exerccio o uso de um comando

no necessrio, pois toda alternativa contm um comando

break return, que termima a execuo

'+', '*', '-'

ou

'/'.

double op (char switch (c ) { case '+' : case '*' : case '-' : case '/' : } }

c,

double
a a a a

a,

double b ) {
b; b;

{ { { {

return return return return

+ * /

} } b; } b ; }-

52

Recurso e Iterao

Se o valor de para qualquer Se comando

no for igual a nenhum dos valores O caso para algum

ser usado, tal como nesse exemplo.

ei , para i n, um default pode ser usado no

caso default pode lugar de

case ei ,

default no for especicado switch pode terminar


ei ,
para

ei ,

i = 1, . . . , n,

sendo em geral usado depois do ltimo caso.

( comum dizer se no existir nenhum caso sem que nenhum dos comandos

ci ,

para

default), o i = 1, . . . , n, seja

executado (isso ocorre se o resultado da avaliao de expresses

e no for igual ao valor de nenhuma das

i = 1, . . . , n).

A necessidade do uso de um comando um dos comandos do comando herdado da linguagem C). A expresso

break, sempre que se deseja que seja executado apenas switch, correspondente a um determinado caso (expresso), int, short, byte ou char, devendo o seu . . . , en . As expresses e1 , . . . , en tm que

hoje em geral reconhecida como um ponto fraco do projeto da linguagem Java (tendo sido

e,

no comando

switch,

deve ter tipo

tipo ser compatvel com o tipo das expresses ser valores constantes (e distintos). Um comando caractere  :. Nesse caso, um comando ao ser executado, o comando

e1 ,

switch

pode ser tambm precedido de um rtulo  um nome seguido do

break

break

pode ser seguido desse nome: isso indica que,

causa a terminao da execuo do comando

switch

precedido pelo rtulo especicado.

7. Escreva uma denio para a funo raizq , que calcula a raiz quadrada de um dado valor
com erro de aproximao menor que

0.0001.

x,

Soluo : A raiz quadrada de

um nmero

tal que

y 2 = x.

Para certos nmeros, como,

por exemplo, 2, a sua raiz quadrada um nmero que teria que ser representado com innitos algarismos na sua parte decimal, no sendo possvel obter essa representao em um tempo nito. Desse modo, o clculo desses valores feito a seguir, com um erro de aproximao inferior a um valor preestabelecido  nesse caso, Vamos chamar de

retornado por raizq (x ) deve ser tal que:

o valor

0.0001.

Denotando

0.0001. por |x| o valor

absoluto de

x,

o valor

a ser

y0

|y 2 x| < e

Para denir a funo raizq, vamos usar o mtodo de aproximaes sucessivas de Newton. Para a raiz quadrada, o mtodo de Newton especica que, se ento uma aproximao melhor dada por:

yi

uma aproximao para

x,

yi+1 = (yi + x/yi )/2


Por exemplo, sejam

x=2

y0 = 2.

Ento:

y1 = (2 + 2/2)/2 = 1.5 y2 = (1.5 + 2/1.5)/2 = 1.4167 y3 = (1.4167 + 2/1.4167)/2 = 1.4142157 . . .


Repetindo esse processo, podemos obter aproximaes para a raiz quadrada de 2 com qualquer preciso desejada (sujeitas, claro, s limitaes da representao de nmeros do computador). A implementao da funo raizq pode ser feita usando um comando de repetio, como mostrado na Figura 5.3.

8. Em 1883, o matemtico francs douard Lucas inventou a seguinte pequena estria :


Um templo, na cidade de Hani, contm trs torres de diamante, em uma das quais Deus colocou, quando criou o mundo, 64 discos de ouro, empilhados uns sobre os outros, de maneira que os discos diminuem de tamanho da base para o topo, como mostrado na Figura 5.4 a seguir. Os monges do templo trabalham sem cessar para transferir, um a um, os 64 discos da torre em que foram inicialmente colocados para uma das duas outras torres, mas de forma que um disco nunca pode ser colocado em cima de outro menor. Quando os monges terminarem de transferir todos os discos para uma outra torre, tudo virar p, e o mundo acabar.

5.6 Exerccios Resolvidos

53

const double

= 0.0001;
x) y

double raizq (double double y = x ; while (!m (y ,x )) return y ; }

{
melhore (y ,x );

int m (double y , double x ) { return math.abs (y * y - x ) < }

e;

double melhore (double y , double return (y + x /y ) / 2; }

x)

Figura 5.3: Clculo da raiz quadrada, usando o mtodo de Newton

Figura 5.4: Torres de Hani (com 3 discos)

A questo que se coloca :

supondo que os monges trabalhem to ecientemente quanto

possvel, e consigam transferir 1 disco de uma torre para outra em 1 segundo, quanto tempo decorreria (em segundos) desde a criao at o m do mundo?

Soluo : Uma soluo recursiva para o problema baseada na idia ilustrada na Figura 5.5.
Nessa soluo, supe-se, como hiptese indutiva, que se sabe como transferir

n1

discos de

uma torre para outra (sem colocar um disco em cima de outro menor); o caso base consiste em transferir um disco de uma torre para outra vazia. O nmero total de movimentaes de

discos denido indutivamente por:

f (1) = 1 f (n) = 2 f (n 1) + 1

Cada valor da seqncia de valores denida por de movimentaes requeridas para mover

f (n)

representa, de fato, o menor nmero

discos de uma torre para outra, satisfazendo o

requerimento de que um disco nunca pode ser colocado sobre outro de dimetro menor. Para perceber isso, basta notar que, para mover um nico disco torre

d,

digamos, da torre

para a

B,

preciso antes mover todos os discos menores que

para a torre

C.
Um resultado

Portanto, a resposta questo proposta anteriormente dada por aproximado

f (64).

1, 844674 1019

segundos, aproximadamente 584,5 bilhes de anos.

A denio de para de

f estabelece uma relao de recorrncia para uma seqncia de valores f (i ), i = 1, . . . , n (uma relao de recorrncia para uma seqncia tal que cada termo

denido, por essa relao, em termos de seus predecessores). A primeira equao na denio

chamada condio inicial da relao de recorrncia.

Podemos procurar obter uma frmula que dene diretamente, de forma no-recursiva, o valor

54

Recurso e Iterao

Figura 5.5: Soluo do problema das torres de Hani (com 3 discos)

de

f (n),

buscando estabelecer um padro que ocorre no clculo de

f (n),

para cada

n:

f (1) = 1 f (2) = 2 f (1) + 1 = 2 1 + 1 = 2 + 1 f (3) = 2 f (2) + 1 = 2(2 + 1) + 1 = 22 + 2 + 1 f (4) = 2 f (3) + 1 = 2(22 + 2 + 1) + 1 = 23 + 22 + 2 + 1 ... f (n) = 2 f (n 1) + 1 = 2n1 + 2n2 + . . . + 2 + 1
n1 i n i=0 2 = 2 1). O leitor com interesse em matemtica pode provar (usando induo sobre n) que, para todo n, a denio recursiva de f de fato satisfaz a equao f (n) = 2n 1.
Podemos observar que

f (n) = 2n 1

(pois

9. O problema descrito a seguir foi introduzido em 1202 por Fibonacci  tambm conhecido
como Leonardo de Pisa, e considerado como o maior matemtico europeu da Idade Mdia:

Supondo que um par de coelhos  um macho e uma fmea  tenha nascido no incio de um determinado ano, que coelhos no se reproduzam no primeiro ms de vida, que depois do primeiro ms um par de coelhos d origem, a cada ms, a um novo par  macho e fmea  e que nenhuma morte ocorra durante um ano, quantos coelhos vo existir no nal do ano?

Escreva um programa para solucionar esse problema, imprimindo o nmero de coelhos existentes no nal do ano.

Soluo : Note que: 1) o nmero de (pares de) coelhos vivos no nal de um ms


ao nmero de (pares de) coelhos vivos no nal do ms coelhos que nasceram no ms

igual

k1

mais o nmero de (pares de)

k;

e 2) o nmero de (pares de) coelhos que nasceram no ms

igual ao nmero de (pares de) coelhos vivos no ms de coelhos que geraram lhotes no ms

k k 2 (pois esse exatamente o nmero

k ).

5.6 Exerccios Resolvidos

55

Portanto, a quantidade de coelhos vivos ao m de cada ms

dada pelo nmero

b (n)

da

seqncia de nmeros denida a seguir, conhecida como seqncia de Fibonacci:

b (0) = 0 b (1) = 1 b (n) = b (n 1) + b (n 2)

se

n>1

O problema pode ento ser resolvido pelo programa a seguir:

int }

printf ("%d",b (12 ));

main

() {

int b (int n ) { if (n ==0) return 0; else if (n ==1) return 1; else return b (n -1) + b (n -2); }
O nmero de pares de coelhos no nal do dcimo segundo ms, b (12), igual a 144 (ou seja, o nmero de coelhos igual a 288). A funo b denido no programa acima bastante ineciente, pois repete muitos clculos, envolve calcular b (2) duas vezes. O mtodo a seguir claramente mais eciente, podendo ser usado para o mesmo efeito, no programa acima, por meio da chamada b (12,0,1): devido s chamadas recursivas a b (n -1) e b (n -2). Por exemplo, o clculo de b (4)

int b (int n , int r1 , int r ) { if (n ==1) return r ; else return b (n -1, r , r1 +r ); }

Por questo de ecincia, o teste de igualdade com zero foi retirado, supondo-se ento que o argumento de b deve ser um inteiro positivo. As chamadas a b usam r1 e r como acumuladores, comportando-se como mostrado a seguir:
a 1 chamada a 2 chamada

b (n ,

b (n -1,

0, 1) 1, 1)

b (0) b (1)

= 0 = 1 =
r1

b (1) b (2) b (n )

= 1 = 1 =
r

...
ltima chamada

b (1, r1 , r )

b (n -1)

A denio recursiva anterior de b simula o mesmo processo iterativo do comando de repetio mostrado a seguir:

int b (int n ) { int r1 = 0, r = 1, t , i ; for (i =2; i <=n ; i ++) { t = r1 ; r1 = r ; r = r + t ; } return r ; }

56

Recurso e Iterao

int main () { int n , m , col = 1; printf ("Impressao de valores de 1 a n em notacao unaria, sem multiplos de m\n"); printf ("Digite n e m: "); scanf ("%d %d",&n, &m); for (int i =1; i <=n ; i ++) { printf ("\n"); if (i % m == 0) continue; for (col =1; col <=i ; col ++) printf ("|"); } return EXIT_SUCCESS ;

Figura 5.6: Exemplo de uso do comando

continue

10. Os comandos

break e continue podem ser usados no corpo de um comando de repetio. A break causa o trmino da repetio e a execuo de um comando continue causa o incio imediato de uma prxima iterao. O comando continue prov
execuo de um comando uma forma de saltar determinados casos em um comando de repetio, fazendo com que o controle passe para a iterao seguinte. O exemplo da Figura 5.6 imprime a representao unria (usando o smbolo  |) de todos os nmeros inteiros de

at n que no so mltiplos de um determinado valor inteiro

O resultado da execuo de Os comandos

Exemplo_continue 20 5

m > 1.

mostrado na Figura 5.7.

break

continue

podem especicar um rtulo, de mesmo nome do rtulo

colocado em um comando de repetio. No caso de um comando colocado tambm em um bloco ou em um comando Como ilustra esse exemplo, um comando

switch

break,

o rtulo pode ser

(veja o Exerccio Resolvido 6).

continue

sem um rtulo indica o incio da execu-

o da prxima iterao do comando de repetio mais interno em relao a esse comando

continue.

Analogamente, um comando

do comando de repetio ou comando O comando

break sem um rtulo indica o trmino da execuo switch mais interno em relao a esse comando break.

break break,

prov uma forma de sair de um comando de repetio, que pode ser, em

algumas situaes, mais fcil (ou mais conveniente do que outras alternativas). Em alguns casos, pode ser mais fcil testar uma condio internamente ao comando de repetio e usar o comando repetio. O uso do comando em vez de incluir essa condio no teste de terminao do comando de

break

ilustrado pelo exemplo a seguir. Considere o problema de ler,

repetidamente, um valor inteiro positivo e imprimir, para cada inteiro lido, seu fatorial. Se for lido um valor negativo, a execuo do programa deve terminar.

11. Esse exerccio ilustra o uso de entrada de dados at que uma condio ocorra ou at que se
chegue ao nal dos dados de entrada. O caractere no sistema operacional Windows,

Control-d

usado para especicar m

dos dados de entrada, em uma entrada de dados interativa no sistema operacional Linux e,

Return).

Control-z

no incio da linha seguido da tecla

Enter

(ou

A funo scanf retorna o nmero de variveis lidas, e pode ser usado para detectar m dos dados de entrada a serem lidos (isto , se no existe mais nenhum valor na entrada de dados a ser lido). O exemplo a seguir l vrios inteiros do dispositivo de entrada padro e imprima a soma de todos os inteiros lidos. O programa termina quando no h mais valores a serem lidos.

5.6 Exerccios Resolvidos

57

Impressao de valores de 1 a n em notacao unaria, sem multiplos de m Digite n e m: 20 5 | || ||| |||| |||||| ||||||| |||||||| ||||||||| ||||||||||| |||||||||||| ||||||||||||| |||||||||||||| |||||||||||||||| ||||||||||||||||| |||||||||||||||||| ||||||||||||||||||
Figura 5.7: Resultado do programa de exemplo de uso do comando

continue

int fat (int x) { int fatx = 1, i; for (i=1; i<=x; i++) return fatx ; }

fatx

*= i;

int main () { int v ; while (1) { printf ("Digite um valor inteiro: "); scanf ("if (v < 0) break; printf ("Fatorial de %d = } return SYSTEM_SUCCESS ; }

Figura 5.8: Exemplo de uso do comando

break

58

Recurso e Iterao

#include <stdio.h > #include <stdlib.h > int main () { int n, soma = 0, testeFim ; while (1) { testeFim = scanf ("%d",&n); if (testeFim != 1) break; soma = soma + n; } printf ("Soma = %d\n", soma ); return EXIT_SUCCESS ; }

O programa a seguir funciona de modo semelhante, mas l vrios inteiros positivos do dispositivo de entrada padro e termina a execuo quando quando no h mais valores a serem lidos ou quando um valor negativo ou zero for lido.

#include <stdio.h > #include <stdlib.h > int main () { int n, soma = 0, testeFim ; while (1)) { testeFim = scanf ("%d",&n); if (testeFim != 1 || n<=0) break; else soma = soma + n; } printf ("Soma = %d\n", soma ); return EXIT_SUCCESS ; }

12. Escreva um programa que leia, do dispositivo de entrada padro, vrios valores inteiros,
positivos ou no, e imprima, no dispositivo de sada padro, os dois maiores valores lidos.

A entrada termina com indicao de m dos dados de entrada (em entrada interativa,

Control-z

seguido de

Enter

no Windows, ou

Control-d

no Linux).

Soluo : So usadas duas variveis inteiras max1 e max2 para armazenar os dois maiores
valores lidos, e elas so atualizadas adequadamente, se necessrio, aps a leitura de cada inteiro. O valor inicial atribudo a essas variveis o valor INT_MIN , menor inteiro armaesse valor e ser, caso for maior, atribudo varivel. INT_MIN denido em limits.h. zenvel em uma varivel de tipo

int.

Qualquer valor inteiro digitado ser maior ou igual a

O valor retornado por scanf usado para vericar m dos dados de entrada (scanf retorna

-1

como indicao de m dos dados de entrada).

O programa mostrado a seguir.

5.6 Exerccios Resolvidos

59

#include <stdio.h > #include <limits.h > int main () { int max1 = INT_MIN, max2 = INT_MIN, valor , m ; while (1) { m = scanf ("%d",&valor ); if (m == -1) break; if (valor > max1 ) { max2 = max1 ; max1 = valor ; } else if (valor > max2 ) max2 = valor ; } printf ("Dois maiores = %d, %d\n", max1,max2 ); }

13. Escreva um programa que leia, do dispositivo de entrada padro, um texto qualquer, caractere
a caractere e imprima, no dispositivo de sada padro, i) o nmero de caracteres, ii) o nmero de palavras, e iii) o nmero de linhas do texto.

Considere que uma palavra uma sequncia de um ou mais caracteres que comea com qualquer caractere que no um delimitador de palavras. Um delimitador de palavras um caractere espao (branco, i.e.

' '), m-de-linha ('\) ou tab (caractere de tabulao, i.e. \t).

A entrada termina com indicao de m dos dados de entrada (em entrada interativa,

Control-z

seguido de

Enter

no Windows, ou

Control-d

no Linux).

Soluo : A soluo mostrada a seguir usa scanf com formato


indicar m dos dados).

%c

para ler um caractere, e

o valor retornado por scanf para vericar m dos dados de entrada (scanf retorna

-1

para

A funo isletter denida retorna verdadeiro (em caractere est entre

C,

valor inteiro diferente de zero) se e Para isso, testado se tal

somente se o caractere passado como argumento uma letra.

'a'

'z'

ou entre

'A'

'Z'.

Para contar palavras, usado uma varivel (fora ) que indica se o caractere corrente, que est sendo lido, est fora ou dentro de uma palavra. O nmero de palavras (armazanado na varivel palavras ) incrementado quando se est fora de uma palavra e um caractere no delimitador de palavras lido (como especicado no enunciado, um delimitador de palavras considerado como sendo um dos caracteres espao, m-de-linha ou tab).

O programa mostrado a seguir.

60

Recurso e Iterao

#include <stdio.h > int delim (char c) { return (c == ' ') || (c == '\t') || (c == '\n'); } int main () { char c; int caracs = 0, palavras = 0, linhas = 0, isDelim, m, while (1) { m = scanf ("%c",&c); if (m == -1) break; caracs ++; isDelim = delim (c); if (isDelim ) { if (c=='\n') linhas ++; fora =1; } else if (fora ) { palavras ++; fora = 0; } } printf ("Numero de caracteres,palavras,linhas = %d,%d,%d\n", caracs,palavras,linhas ); }
fora =1;

14. Esse um exerccio baseado no problema PAR (Par ou mpar) , obtido de:

http://br.spoj.pl/problems/PAR
H uma modicao motivada pelo fato de que no vamos usar ainda leitura de cadeias de caracteres, e por isso os nomes dos jogadores correspondentes a escolha "par"e "mpar"so substitudos respectivamente por "Par"e "Impar". O problema descrito a seguir. O problema consiste em determinar, para cada jogada de partidas do jogo Par ou mpar , o vencedor da jogada. A entrada representa uma sequncia de dados referentes a partidas de Par ou mpar . primeira linha de cada partida contm um inteiro partida. por As A

n,

que indica o nmero de jogadas da

linhas seguintes contm cada uma dois inteiros

que representam o

nmero escolhido por cada jogador (0

a5

0 B 5).

O nal da entrada indicado

n = 0. i o nmero da

A sada deve conter para cada partida, uma linha no formato Partida i, onde

partida: partidas so numeradas sequencialmente a partir de 1. A sada deve conter tambm uma linha para cada jogada de cada partida, contendo Par ou Impar conforme o vencedor da partida seja o jogador que escolheu par ou mpar, respectivamente. uma linha em branco entre uma partida e outra. Por exemplo, para a entrada: Deve ser impressa

3 2 3 1 2 1 2 0

4 5 0 5 3

A sada deve ser:

5.6 Exerccios Resolvidos

61

Partida 1 Par Par Impar Partida 2 Par Impar


Soluo : O programa abaixo dene e usa a funo processaPartida para separar o processamento (clculo e impresso) de valores de cada partida, e a funo par , que determina se um dado valor par ou no. O nmero de cada partida armazenado em uma varivel, que tem valor inicial igual a 1 e incrementada aps o processamento de cada partida. O programa usa tambm o recurso de denir a assinatura (ou interface, ou cabealho) de cada funo denida, para usar (chamar) a funo antes de deni-la. Na denio da interface o nome dos parmetros opcional, e omitido no programa abaixo.

#include <stdio.h > #include <stdlib.h > void processaPartida (int,int); int par (int); int main () { int numPartida = 1, numJogadas ; while (1) { scanf ("%d", &numJogadas ); if (numJogadas == 0) break; processaPartida (numPartida,numJogadas ); numPartida ++; } } void processaPartida (int numPartida , int numJogadas ) { int mao1, mao2 ; printf ("Partida %d\n", numPartida ); for ( ; numJogadas >0; numJogadas ) { scanf ("%d%d", &mao1, &mao2 ); printf ("%s\n",par (mao1 +mao2 ) ? "Par" : "Impar"); } printf ("\n"); } int par (int valor ) { return valor % 2 == 0; }

15. Nesse exerccio vamos apresentar uma soluo para o problema RUMO9S (Rumo aos 9s) ,
obtido de

http://br.spoj.pl/problems/RUMO9s/
A soluo apresentada no usa arranjo nem cadeia de caracteres (abordados no prximo captulo), para ler, armazenar e imprimir os valores de entrada. Em vez disso, caracteres so lidos um a um (nmeros no podem ser lidos e armazenados como valores inteiros porque

62

Recurso e Iterao

podem ter at 1000 dgitos decimais, e portanto no poder ser armazenadas como valores de tipo

int

ou

long int).

O problema descrito a seguir.

Um nmero inteiro mltiplo de nove se e somente se a soma dos seus dgitos mltiplo de 9. Chama-se grau-9 de um nmero inteiro

n0

o valor igual a:

1 1

se

n = 9, 0

se

n < 9; n > 9.

mais o grau-9 da soma de seus dgitos, se

Escreva um programa que, dado uma sequncia de inteiros positivos, imprime se cada um deles mltiplo de nove e, em caso armativo, seu grau-9. com o valor A entrada, no dispositivo de entrada padro, contm uma sequncia de inteiros positivos, um em cada linha, e termina

0.

Exemplo:

Entrada:

999 27 9 998 0

Sada:

999 e' multiplo de 9 e seu grau-9 e' 3. 27 e' multiplo de 9 e seu grau-9 e' 2. 9 e' multiplo de 9 e seu grau-9 e' 1. 998 nao e' multiplo de 9.

C,

Soluo : A soluo usa a funo isdigit para testar se um dado caractere um dgito (i.e. em
um inteiro sem sinal, entre

'0'

'9').

A funo somaEImprimeCaracs l e imprime os dgitos contidos em uma linha da entrada padro, e retorna o inteiro correspondente. ordenados, a partir de como valores inteiros). Para isso, ela subtrai converte cada caractere lido no inteiro correspondente, subtraindo o caractere

'0',

no cdigo ASCII, usado em

'0' (uma vez que os caracteres so C para representao de caracteres,

5.7 Exerccios

63

#include <stdio.h > #include <stdlib.h > int somaEImprimeCaracs () { char c; scanf ("%c",&c); if (c=='0') return 0; int soma =0; while (isdigit (c)) { soma += c - '0'; printf ("%c",c); scanf ("%c",&c); } return soma ; } int somaDigs (int n) { if (n<10) return n; else return (n%10 + }

somaDigs (n/10));

int grau9 (int n) { if (n<10) return (n==9 ? 1 : 0); else { int grau = grau9 (somaDigs (n)); return (grau == 0? 0 : 1 + grau ); } } int main () { int v , grau9v ; while (1) { int n = somaEImprimeCaracs (); if (n==0) break; int grau9n = grau9 (n); if (grau9n ==0) printf ("is not a multiple of 9.\n", n); else printf ("is a multiple of 9 and has 9-degree %d.\n", } system ("PAUSE"); return 0; }

grau9n );

5.7 Exerccios
1. Escreva trs denies de funo, chamadas somaIter , somaRec e soma , tais que, dados dois
nmeros inteiros positivos

b,

retorne o valor

a + b.

As duas primeiras denies devem

usar apenas as operaes mais simples de incrementar 1 e decrementar 1 (devem supor que as operaes de adicionar e de subtrair mais de uma unidade no so disponveis). A primeira denio deve usar um comando de repetio, e a segunda denio deve ser recursiva. A terceira denio deve usar o operador

de adio.

Inclua essas trs denies em um programa de teste, e dena um programa de teste que leia vrios pares de valores cada par de valores

de um arquivo, e no imprima nada se e somente se, para

lidos, os resultados retornados pelas execues das trs denies

(somaIter , somaRec e soma ) forem iguais. Se existir um par de valor

a e b lido para o qual o

resultado retornado pela execuo das trs denies no igual, o programa deve terminar

64

Recurso e Iterao

e uma mensagem deve ser impressa, informando o par de valores nome da funo que retornou cada resultado.

a,b

para o qual o resultado

da execuo foi diferente, assim como o resultado retornado por cada denio, junto com o

2. Escreva uma denio da funo numdiv que, dados dois nmeros inteiros positivos, retorna
o nmero de vezes que o primeiro pode ser dividido exatamente pelo segundo.

numdiv (8,2) deve retornar

e numdiv (9,2) deve retornar

0.

Exemplos:

Escreva um programa que leia vrios pares de nmeros inteiros positivos

a,b

e imprima, para

cada par lido, o nmero de vezes que o primeiro pode ser dividido pelo segundo, usando a funo denida acima. A leitura deve terminar quando um dos valores lidos for menor ou igual a zero.

3. O nmero de combinaes de
por

objetos

 ou seja, o nmero de maneiras diferentes de

escolher, de um conjunto com

elementos, um subconjunto com

elementos  denotado

n p , dado pela frmula:

n(n 1) . . . (n p + 1) p!
Por exemplo, o nmero de combinaes de 4 objetos, 2 a 2, igual a os objetos por nmeros, as combinaes so Dena uma funo que, dados

= 6 (se representamos {1,2}, {1,3}, {1,4}, {2,3}, {2,4} e {3,4}). p, calcule o nmero de combinaes de n objetos p a p. n! p! (n p)!

43 2!

Observao : A frmula acima pode ser escrita tambm na forma:

No entanto, note que uma implementao baseada diretamente nessa ltima seria menos eciente do que uma implementao baseada diretamente na primeira, uma vez que o nmero de operaes de multiplicao necessrias para o clculo seria maior nesse ltimo caso. Escreva um programa que leia vrios pares de nmeros inteiros positivos para cada par lido, o nmero de combinaes existentes de

objetos

n, p e imprima, p. A leitura deve

terminar quando um dos valores lidos for menor ou igual a zero.

4. Escreva uma funo para calcular qual seria o saldo de sua conta de poupana depois de 5
anos, se voc depositou

1000

reais no incio desse perodo e a taxa de juros de

6%

ao ano.

5. Generalize a questo anterior, de maneira que se possa especicar quaisquer valores inteiros
como capital inicial, taxa de juros e prazo desejados. Escreva um programa que leia, repetidamente, trs valores inteiros positivos

c, j , t

que

representam, respectivamente, o capital inicial, a taxa de juros anual e o nmero de anos de depsito, e imprima, para cada trs valores lidos, o saldo nal da conta, calculado usando a funo acima. A leitura deve terminar quando um dos trs valores lidos for menor ou igual a zero.

6. Dena funes que, dado o nmero de termos


(a) (b)

n,

calcule:

1 1 1 (c) 1

n 2 i=1 i +3 2 + 2 4 + n i i=0 ( i!

5 3 3 9

7 4 + ... 4 5 16 + 25
2

...

(d) (e)

i (i+1)! )

244668... 335577...

Cada somatrio deve ser implementado usando i) um comando de repetio e ii) uma funo recursiva. Escreva um programa que leia repetidamente pares de valores inteiros cada par lido, o resultado de chamar a argumento ou quando

n, k

e imprima, para

k -sima

funo acima (k variando de 1 a 5) com o

n (que representa o nmero de k no for um valor entre 1 e 5.

termos). A leitura deve terminar quando

n0

5.7 Exerccios

65

7. Escreva funes para calcular um valor aproximado do seno e cosseno de um ngulo dado em
radianos, usando as seguintes frmulas: sen (x) cos (x)

=x =1

x3 3! x2 2!

+ +

x5 5! x4 4!

... ...

Cada funo deve receber o valor de no somatrio.

em radianos e o nmero de parcelas a serem usadas

A denio no deve ser feita calculando o fatorial e a exponencial a cada parcela, mas sim de modo que o valor do numerador e do denominador de cada parcela sejam obtidos a partir dos valores respectivos da parcela anterior. Escreva um programa que leia, do dispositivo de entrada padro, vrios nmeros de ponto utuante que representam valores de ngulos em graus, e, para cada valor, imprima o seno e o cosseno desse valor, usando as funes denidas acima e funes para converter graus em radianos. A entrada deve terminar quando um valor negativo ou nulo for lido.

8. Faa um programa que leia uma sequncia de valores inteiros diferentes de zero, separados
por espaos ou linhas, e imprima os valores pares dessa sequncia. Um valor par se o resto da diviso desse valor por 2 igual a zero. A entrada termina quando um valor igual a zero for lido.

9. Faa um programa para imprimir a seguinte tabela:


1 2 ... 10 20 30 40 50 60 70 80 90 100 2 4 3 6 4 8 5 10 6 12 7 14 8 16 9 18 10 20

10. Escreva um programa que leia um nmero inteiro positivo

e imprima um tringulo como

o mostrado abaixo, considerando que o nmero de linhas igual a

n.

* *** ***** ******* ********* *********** *************

11. Escreva um programa que leia um nmero inteiro positivo

e imprima um losango como o

mostrado abaixo, considerando que o nmero de linhas igual a

n.

* *** ***** ******* ********* *********** ************* *********** ********* ******* ***** *** *

66

Recurso e Iterao

12. Dena uma funo que converta o valor de uma temperatura dada em graus Fahrenheit para
o valor correspondente em graus centgrados (ou Celsius). A converso dada pela frmula:

TC =

5 (TF 32) 9 p
(valor de

Use a funo denida acima como parte de um programa que receba como argumentos o valor de uma temperatura inicial, o valor de uma temperatura nal e um passo incremento), e imprima uma tabela de converso de graus Fahrenheit em graus centgrados, desde a temperatura inicial at o maior valor que no ultrapasse a temperatura nal, de em

graus Fahrenheit.

13. Os nmeros mostrados na tabela a seguir formam a parte inicial do chamado tringulo de
Pascal (nome dado em homenagem a Blaise Pascal (16231662), que escreveu um inuente
tratado sobre esses nmeros). A tabela contm os valores das combinaes de

elementos,

p,

para valores crescentes de

p.
n 4 n 5 n 6 n 7 n 8 n 9 n 10

n
0 1 2 3 4 5 6 7 8 9 10

n 0 1
1 1 1 1 1 1 1 1 1 1

n 1
1 2 3 4 5 6 7 8 9 10

n 2

n 3

1 3 6 10 15 21 28 36 45 1 4 10 20 35 56 84 120 1 5 15 35 70 126 210 1 6 21 56 126 252 1 7 28 84 210 1 8 36 120 1 9 45 1 10 1

As entradas em branco nessa tabela tm, de fato, valor igual a zero, tendo sido deixadas em branco para evidenciar o tringulo formado pelas demais entradas da tabela. Escreva um programa para imprimir o tringulo de Pascal, usando o fato de que

n p+1

n p

(n p ) p+1

Observao : A frmula acima pode ser deduzida facilmente de

n p

n! p ! (n p)!

14. Escreva um programa que leia quatro valores inteiros positivos nA, nB , tA e tB  representando respectivamente as populaes atuais de dois pases A e B e as taxas de crescimento anual dessas populaes  e determine o nmero de anos necessrios para que a populao do pas A ultrapasse a de B , supondo que as taxas de crescimento dessas populaes no variam e que nA

<

nB e tA

>

tB .

15. Escreva um programa que leia, do dispositivo de entrada padro, um texto qualquer, caractere
a caractere e imprima, no dispositivo de sada padro, i) o nmero de vogais, ii) o nmero de consoantes e iii) o nmero de outros caracteres diferentes de vogais e consoantes presentes em palavras: considere que estes so todos os demais caracteres que no sejam espao, mde-linha ('\n') ou tab (caractere de tabulao, i.e.

'\t').

A entrada termina com indicao de m dos dados de entrada (em entrada interativa,

Control-z

seguido de

Enter

no Windows, ou

Control-d

no Linux).

Dicas: Use scanf com formato

%c

para ler um caractere, e use o valor retornado por scanf

para vericar m dos dados de entrada: scanf retorna

-1

para indicar m dos dados.

Dena e use funo que retorna verdadeiro se e somente se o caractere passado como argumento uma letra; para deni-la, teste se tal caractere est entre

'Z'.

'a'

'z'

ou entre

'A'

5.7 Exerccios

67

16. Modique o programa da questo anterior de modo a eliminar a suposio de que nA

<

nB

e calcular, nesse caso, se a menor populao vai ou no ultrapassar a maior e, em caso armativo, o nmero de anos necessrio para que isso ocorra (em caso negativo, o programa deve dar como resultado o valor

0).

17. Resolva o problema

BIT

disponvel em:

http://br.spoj.pl/problems/BIT/
O enunciado apresentado, de forma resumida, a seguir. O problema consiste em escrever um programa para calcular e imprimir, para cada valor inteiro positivo lido, quantas notas de 50, 10, 5 e 1 reais so necessias para para totalizar esse valor, de modo a minimizar a quantidade de notas. A entrada composta de vrios conjuntos de teste. considerado. O nal da entrada indicado por Cada conjunto de teste composto

por uma nica linha, que contm um nmero inteiro positivo

v,

que indica o valor a ser

v = 0.

Para cada conjunto de teste da entrada seu programa deve produzir trs linhas na sada. A primeira linha deve conter um identicador do conjunto de teste, no formato onde

"Teste n",
Na

o nmero do teste; os testes so numerados sequencialmente a partir de 1.

segunda linha devem aparecer quatro inteiros, que representam o resultado encontrado pelo seu programa: o primeiro inteiro indica o nmero de notas de 50 reais, o segundo o nmero de notas de 10 reais, o terceiro o nmero de notas de 5 reais e o quarto o nmero de notas de 1 real. A terceira linha deve ser deixada em branco. Por exemplo, para a entrada:

1 72 0
A sada deve ser:

Teste 1 0 0 0 1 Teste 2 1 2 0 2
18. Resolva o problema

ALADES

disponvel em:

http://br.spoj.pl/problems/ALADES/
O enunciado apresentado, de forma resumida, a seguir. O problema consiste em escrever um programa que, dados valores de hora e minutos corrente e hora e minutos de um alarme, determinar o nmero de minutos entre os dois valores. A entrada contm vrios casos de teste. Cada caso de teste descrito em uma linha, contendo quatro nmeros inteiros e

h1 , m1 , h2 e m2 , sendo que h1 : m1 representa hora e minuto atuais, h2 :m2 representa hora e minuto para os quais um alarme foi programado (0 h1 < 24, 0 m1 < 60, 0 h2 < 24, 0 m2 60). O nal da entrada indicado por uma linha que

contm apenas quatro zeros, separados por espaos em branco. Os dados devem ser lidos da entrada padro. Para cada caso de teste da entrada, deve ser impressa uma linha, no dispositivo de sada padro, contendo um nmero inteiro que indica o nmero de minutos entre os dois horrios. Por exemplo, para a entrada:

68

Recurso e Iterao

1 5 3 23 59 21 33 0 0 0

5 0 34 21 10 0

A sada deve ser:

120 35 1417

Captulo 6

Valores Compostos: Parte I


Este captulo aborda a denio e o uso de arranjos (seo 6.1), e registros (ou, como so chamados em

C,

estruturas , seo 6.5). Uma introduo denio e uso de estruturas de dados

encadeadas, formadas com o uso de registros com ponteiros, so abordadas na seo 6.5. Arranjos e cadeias so estruturas de dados homogneas, devido ao fato de que os componentes tm que ser todos de um mesmo tipo, enquanto estruturas de dados encadeadas e registros em geral so estruturas de dados heterogneas, que podem envolver componentes de vrios tipos, diferentes entre si.

6.1 Arranjos
Arranjos so estruturas de dados muito usadas em programas. Um arranjo uma forma de representar uma funo nita (funo de domnio nito  que em geral vista como uma tabela ou uma seqncia nita de valores  com a caracterstica de que o acesso aos seus componentes podem ser feitos de modo eciente. Esta seo aborda a denio e uso de arranjos na linguagem

C.

Um arranjo uma estrutura de dados formada por um certo nmero nito de componentes (tambm chamados de posies do arranjo) de um mesmo tipo, sendo cada componente identicado por um ndice . Um arranjo tem um tamanho , que o nmero de componentes do arranjo. A uma varivel de tipo arranjo de um tipo Em Se do arranjo.

T e tamanho n correspondem n variveis de tipo T . C, os ndices de um arranjo so sempre inteiros que variam de 0 a n -1, onde n o tamanho
uma expresso que representa um arranjo de tamanho

int

com valor entre

e n -1, ento v [i ] representa um componente do arranjo

n,

uma expresso de tipo

v.

Nota sobre uso de ndices fora do limite em

C:

A linguagem

no especica que, em uma

operao de indexao (uso de um valor como ndice) de um arranjo, deva existir uma vericao de que esse ndice um ndice vlido. A linguagem simplesmente deixa a responsabilidade para o programador. Se o ndice estiver fora dos limites vlidos em uma indexao, uma rea de memria distinta da rea alocada para o arranjo ser usada, ou o programa interrompido, com uma mensagem de que um erro ocorreu devido a um acesso ilegal a uma rea de memria que no pode ser usada pelo processo corrente. Se o ndice for invlido mas estiver dentro da rea reservada O erro devido a um ao processo corrente, nenhum erro em tempo de execuo ser detectado.

acesso ilegal a uma rea de memria no reservada ao processo corrente provoca a emisso da mensagem segmentation fault , e a interrupo da execuo do processo corrente. O motivo de no existir vericao de que um ndice de um arranjo est ou no entre os limites desse arranjo , obviamente, ecincia (i.e. evitar gasto de tempo de execuo). O programador deve estar ciente disso e atento de modo a evitar erros (i.e. evitar o uso de ndices fora dos limites vlidos em indexaes de arranjos). A ecincia que existe no acesso a componentes de um arranjo se deve ao fato de que arranjos so geralmente armazenados em posies contguas da memria de um computador, e o acesso

i-sima

posio feito diretamente, sem necessidade de acesso a outras posies. Isso espelha o

70

Valores Compostos: Parte I

funcionamento da memria de computadores, para a qual o tempo de acesso a qualquer endereo de memria o mesmo, ou seja, independe do valor desse endereo. Um arranjo , por isso, chamado de uma estrutura de dados de acesso direto (ou acesso indexado ). Ao contrrio, em uma estrutura de dados de acesso sequencial , o acesso ao de ndice inferior a

i-simo

componente requer o acesso aos componentes

i.

6.1.1 Declarao e Criao de Arranjos


de tipo arranjo em

C, arranjos so criados no instante da declarao de variveis do tipo arranjo. Uma varivel C armazena na verdade no um valor de tipo arranjo mas o endereo do primeiro componente do arranjo que ela de fato representa. A verso C-99 da linguagem permite
Em

a declarao de arranjos dentro de funes com tamanho que conhecido apenas dinamicamente, mas em geral uma varivel de tipo arranjo tem um valor conhecido estaticamente (em tempo de compilao) ou um parmetro de uma funo, sendo o tamanho nesse caso igual ao tamanho do argumento, especicado no instante da chamada funo. Usaremos, para criao de arranjos com tamanho conhecido apenas dinamicamente, a declarao de um ponteiro. Isso ser explicado mais detalhadamente na seo 6.3.2. O tamanho de um arranjo no faz parte do seu tipo (em geral, esse tamanho no conhecido estaticamente), mas no podendo ser modicado. Considere os seguintes exemplos: progbint ai [3]; char ac [4]; As declaraes acima criam as variveis ai e ac . segunda um arranjo de 4 caracteres. A declarao dessas variveis de tipo arranjo envolvem tambm a criao de um valor de tipo arranjo. Na declarao de ai , criada uma rea de memria com tamanho igual ao de 3 variveis de tipo A primeira um arranjo de 3 inteiros e a

int.

Similarmente, na declarao de ac , criada uma rea de memria com tamanho igual

ao de 4 variveis de tipo

char.

Os valores contidos nessas reas de memria no so conhecidos:

so usados os valores que esto j armazenados nessas reas de memria.

6.1.2 Arranjos criados dinamicamente


A funo malloc , denida na biblioteca stdlib , aloca dinamicamente uma poro de memria de um certo tamanho, passado como argumento da funo, e retorna o endereo da rea de memria alocada. Esse endereo retornado com o valor de um ponteiro para o primeiro componente do arranjo. Ponteiros so abordados mais detalhadamente na seo 6.3. Por enquanto, considere apenas que malloc aloca uma rea de memria  na rea de memria dinmica do processo corrente, chamada em ingls de heap  que ser usada tipicamente por meio da operao de indexao do arranjo. Considere os seguintes comandos:

int *pi ; char *pc ; pi = malloc (3 * sizeof(int)); pc = malloc (4 * sizeof(char));


A chamada malloc (3*sizeof(int)) alloca uma rea de memria de tamanho milarmente, a chamada malloc (4 sendo

sizeof(int)

o tamanho de uma rea de memria ocupada por um valor de tipo sendo

4*(sizeof(char)), valor de tipo char.

* sizeof(char)) sizeof(char) o tamanho int.

3*(sizeof(int)), int. Si-

alloca uma rea de memria de tamanho de uma rea de memria ocupada por um

Aps a atribuio varivel pi acima, pi contm o endereo da primeira posio de um arranjo com 3 componentes de tipo Analogamente, aps a atribuio varivel pc acima, ac contm o endereo da primeira posio de um arranjo com 4 componentes de tipo

char.

Note que, no caso da declarao de variveis com um tipo que explicitamente indicado como sendo um tipo arranjo, para o qual o tamanho indicado explicitamente (como no exemplo anterior das variveis ai e ac ), o arranjo no alocado na rea dinmica mas na rea de pilha da funo

6.1 Arranjos

71

/************************************************************ * L n, depois n inteiros do dispositivo de entrada padro * * e imprime os n inteiros lidos em ordem inversa. * ***********************************************************/ #include
stdio .h ;

int main () { int *arr , i=0, n; scanf ("%d", &n); arr = malloc (n * sizeof(int)); for (i=0; i<n; i++) scanf ("%d", &arr [i]); for (i; i>=0; i) printf ("%d ", arr [i]); }

Figura 6.1: Exemplo de uso de comando

for

para percorrer arranjo

na qual a declarao ocorre (no caso, na rea de memria alocada quando a execuo da funo

main iniciada).

sizeof.

O tamanho de uma rea de memria pode ser obtido em A palavra reservada

sizeof

C por meio do uso da funo predenida sizeof


o tamanho

pode ser seguida de um nome de tipo (como nos exemplo

acima) ou por uma expresso.

O resultado retornado pela avaliao de

em bytes do tipo ou expresso usada como argumento (tamanho do tipo ou tamanho do tipo da expresso, respectivamente). No caso de uso de um tipo, ele deve ser colocado entre parnteses, mas quando uma expresso usada, ela pode seguir Por exemplo, depois da atribuio acima, A linguagem a declarao:

sizeof sem necessidade de parnteses (respeitandopi retorna o mesmo que

se a precedncia de operadores e chamadas de funes).

sizeof

sizeof(3 * sizeof(int)).

usa colchetes em declarao de arranjos aps o nome da varivel (por exemplo,

int a[5];
declara uma varivel um arranjo de componentes de tipo que cria uma varivel b de tipo

a de tipo arranjo, mas o tipoa int ocorre antes e a indicao de que esse tipo int ocorre aps o nome da varivel.

Isso feito com o mero intuito de permitir declaraes um pouco mais sucintas, como a seguinte,

int

e um arranjo a com componentes de tipo

int:

int b,

a [5];

6.1.3 Exemplo de Uso de Arranjo Criado Dinamicamente


Como exemplo do uso de arranjo criados dinamicamente, vamos considerar o problema de ler um inteiro no-negativo que foram lidos. Por exemplo, se forem lidos o inteiro imprimir

n,

em seguida

valores inteiros e imprimir os

inteiros na ordem inversa e

3,

em seguida trs inteiros

i1 , i2

i3 ,

o programa deve

i3 , i2 , i1 ,

nesta ordem. Uma soluo mostrada na Figura 6.1.

O programa da Figura 6.1 ilustra a operao bsica de percorrer um arranjo para realizao de alguma operao sobre os valores armazenados no arranjo. O programa declara o tipo da varivel

arr no como um arranjo de componentes de tipo


tipo

int.

int,

mas como um ponteiro para variveis do

Isso ocorre porque o tamanho do arranjo s conhecido dinamicamente, sendo sua

alocaco feita na rea de memria dinmica pela funo malloc , que retorna um endereo para a rea de memria alocada. No entanto, a operao de indexao de arranjos funciona normalmente tambm no caso de variveis ou valores de tipo ponteiro. arranjos e ponteiros em Mais detalhes sobre a relao entre

esto na seo 6.3.

72

Valores Compostos: Parte I

void preenche (int arr [], int tam , int valor ) { // Preenche todas as posicoes de arr com valor int i; for (i=0; i<tam ; i++) arr [i] = valor ; } int iguais (int arr1 [], int tam1 , int arr2 [], int tam2 ) { // Retorna verdadeiro sse arr1 = arr2 , componente a componente. int i; if (tam1 == tam2 ) for (i=0; i<tam1 ; i++) if (arr1 [i] == arr2 [i]) return 0; return 1; else return 0; }

Figura 6.2: Operaes comuns em arranjos

Outra observao importante referente necessidade de se especicar o tamanho do arranjo, ou seja, o nmero de valores inteiros a ser digitado, antes da leitura dos valores inteiros. Para evitar isso, h duas opes: i) no usar um arranjo, mas uma estrutura de dados encadeada, como descrito na seo 6.5, ou ii) adotar um certo valor como mximo para o nmero de valores a serem digitados (de modo a alocar um arranjo com tamanho igual a esse nmero mximo). Essa opo tem as desvantagens de que um nmero mximo pode no ser conhecido estaticamente ou ser difcil de ser determinado, e o uso de um valor mximo pode levar a alocao desnecessria de rea signicativa de memria, que no ser usada (ou seja, o mximo pode ser um valor muito maior do que o de fato necessrio). Para percorrer um arranjo, usado tipicamente um comando

for, pois o formato desse comando

apropriado para o uso de uma varivel que controla a tarefa de percorrer um arranjo: iniciao do valor da varivel usada para indexar o arranjo, teste para vericar se seu valor ainda um ndice vlido do arranjo, e atualizao do valor armazenado na varivel.

6.1.4 Operaes Comuns em Arranjos


A Figura 6.2 apresenta exemplos de funes que realizam operaes bsicas bastante comuns sobre arranjos de componentes de um tipo especco,

int.

As funes so: i) preencher todos

os componentes de um arranjo, passado como argumento, com um valor, tambm passado como argumento, e ii) testar igualdade de arranjos, passados como argumentos da funo. A primeira operao uma funo com efeito colateral. Isso signica que ela no uma funo que tem como domnio e contra-domnio os tipos anotados na denio da funo, mas precisa, para poder ser considerada como funo, que o domnio e contra-domnio abranjam (alm dos parmetros explicitamente indicados na denio da funo) tambm uma forma de representao do estado da computao. O estado da computao pode ser representado na forma de uma funo que associa variveis a valores armazenados nessas variveis. Nas duas funes acima, preenche e iguais , o tamanho do arranjo (nmero de posies alocadas) um parmetro da funo. do padro Isso feito para evitar o uso da funo predenida

sizeof

de

com argumento que um arranjo alocado dinamicamente: esta funo, anteriormente denio

C-99,

s podia ser usada quando o tamanho do arranjo era conhecido em tempo de

compilao. Note que o uso de

== no adequado para comparar igualdade de dois arranjos em C (i.e. comO teste de igualdade de valores de tipo arranjo com

parar se dois arranjos tm o mesmo nmero de componentes e os contedos dos componentes em cada ndice dos dois arranjos so iguais).

==

se refere a comparao apenas de ponteiros (i.e. comparao entre se os endereos da primeira

posio do primeiro e do segundo arranjo so iguais).

6.1 Arranjos

73

#include <stdio.h > #include <stdlib.h > int main () { int *arr1, *arr2, n1, n2, i; printf ("Digite inteiro positivo n1, n1 valores inteiros, "); printf ("inteiro positivo n2, n2 valores inteiros\n"); scanf ("%d", &n1 ); arr1 = malloc (n1 * sizeof(int)); for (i=0; i<n1; i++) scanf ("%d", &(arr1 [i])); scanf ("%d", &n2 ); arr2 = malloc (n2 * sizeof(int)); for (i=0; i<n2 ; i++) scanf ("%d", &arr2 [i]); printf ("Sequencia 1 de valores inteiros e' %s sequencia 2 de valores inteiros.", iguais (arr1,n1,arr2,n2 ) ? "igual a": "diferente da"); system ("PAUSE"); return 0; }

Figura 6.3: Exemplo de uso de funo que testa igualdade entre arranjos

O programa da Figura 6.3 ilustra o uso da funo iguais denida acima, escrevendo um programa que l, nesta ordem, um valor inteiro positivo o resultado de testar se os se o primeiro igual ao

primeiros dos

2 timesn

n-simo

mais um, o

n, 2 n valores inteiros, e em seguida imprime valores lidos so iguais aos n ltimos (ou seja, segundo igual ao n-simo mais dois, etc.).

6.1.5 Arranjos de arranjos


Considere o trecho de programa a seguir:

int **a, n=4, m=3; a = malloc (n * sizeof(int*)); int i; for (i=0; i<n; i++) a[i] = malloc (m * sizeof(int));

Aps a execuo desse trecho de programa, a representa um arranjo com quatro componentes, sendo cada componente um arranjo com trs componentes. Tal arranjo algumas vezes chamado de uma matriz , no caso uma matriz 4 por 3. Um arranjo de arranjos tambm chamado de arranjo

multidimensional .
Um arranjo pode ter como componentes arranjos de tamanhos diferentes, alocados dinamicamente, como ilustra o exemplo a seguir.

int **a ; = malloc(2 * sizeof(int*)); ... a [0] = malloc(10 * sizeof(int)); ... a [1] = malloc(40 * sizeof(int)); ...
a

74

Valores Compostos: Parte I

segundo, a [1], tem 40 componentes.

alocados dinamicamente, sendo que o primeiro deles, a [0], tem 10 componentes, enquanto o

O arranjo a um arranjo, alocado dinamicamente, de tamanho 2, contendo dois arranjos

6.1.6 Inicializao de Arranjos


Variveis devem em geral ser inicializadas na sua declarao. Do contrrio, o valor armazenado ser denido pelo valor que estiver na memria, durante a execuo do programa, quando a varivel criada. Isso pode provocar a ocorrncia de erros em outras partes do programa, que podem ser difceis de detectar. Para variveis de tipo arranjo, existe uma notao especial em os valores componentes do arranjo, separados por vrgulas. Por exemplo, pode-se escrever:

C,

que infelizmente s pode ser

usada em declaraes de variveis, que consiste em enumerar, entre os caracteres

'' e '',

todos

char* diasDaSemana[] = { "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab" };


para declarar uma varivel diasDaSemana que um arranjo de 7 componentes, sendo cada componente uma cadeia de caracteres. usado para inicializao do arranjo. Em casos em que no se pode inicializar um arranjo no instante de sua declarao (pois o valor inicial ou o tamanho de um arranjo ainda no so conhecidos), em geral conveniente inicializar a varivel de tipo arranjo com o valor O tamanho do arranho no precisa ser especicado, sendo determinado automaticamente de acordo com o nmero de componentes especicado no valor

NULL

(ponteiro nulo).

6.1.7 Exerccios Resolvidos


1. Escreva um programa que leia notas de alunos obtidas em tarefas de uma certa disciplina,
e imprima a nota total de cada aluno e a mdia das notas dos alunos nessa disciplina. A entrada dos dados contm, primeiramente, o nmero

de alunos da turma, depois o nmero

de tarefas da disciplina, e em seguida o nmero de cada aluno (de

n)

e as

notas desse

aluno. Cada valor separado do seguinte por um ou mais espaos ou linhas.

Soluo : A soluo dene uma funo calcNotas que recebe um arranjo com as notas de
cada aluno em cada tarefa, e retorna um arranjo com as notas nais de cada aluno. Em para-inteiros. Outra funo, chamada media , recebe um arranjo de inteiros (em e o tamanho do arranjo, e retorna um valor de tipo contidos no arranjo. Na funo main , um arranjo de notas de cada aluno em cada avaliao preenchido com valores lidos, o mtodo calcNotas chamado para clculo das notas nais, e as notas nais calculadas, assim como a mdia, calculada por chamada funo media , so impressas. As notas de cada aluno so representadas em

C,

o arranjo passado como argumento, alocado dinamicamente, um ponteiro para ponteiros-

float

C, um ponteiro para inteiros)

que igual mdia dos inteiros

como ponteiros para ponteiros-para-inteiros.

6.2 Exerccios

75

#include <stdio.h > #include <stdlib.h > int* calcNotas (int **notas, int numAlunos, int numAvaliacoes ) { int i, j , *notasFinais ; notasFinais = malloc (numAlunos * sizeof(int)); // Inicializa notas finais de cada aluno com 0 for (i=0; i < notasFinais [i] = 0; for (i=0; i < numAlunos ; i++) for (j =0; j < numAvaliacoes ; j ++) notasFinais [i] += notas [i][j ]; } return
notasFinais ; numAlunos ;

i++)

float media (int vs [], int n) { int i, soma =0; for (i=0; i<n; i++) soma += vs [i]; return ((float)soma )/n; } int main () { int n,k; scanf ("%d",&n); scanf ("%d",&k ); int **notas = int i,j ;
malloc (n*sizeof(int*));

for (i=0; i<n; i++) { notas [i] = malloc (k *sizeof(int)); for (j =0; j <k; j ++) scanf ("%d",&(notas [i][j ])); } int *notasFinais =
calcNotas (notas,n,k );

for (i=0; i<n; i++) printf ("Nota final do aluno %d = %d\n", i+1, notasFinais [i]); printf ("Media da turma = %f", media (notasFinais,n));

6.2 Exerccios
1. Considere nessa questo que um amigo seu dono de
pediu sua ajuda para fazer o seguinte programa em

C.

lojas de revendas de automveis e

Ele tem dados armazenados sobre o nmero de vendas de automveis vendidos em cada loja. No importa qual automvel, ele est interessado apenas no nmero de unidades vendidas. Escreva um programa, para ajudar seu amigo, que leia, do dispositivo de entrada padro, o valor de

v1 ,

...,

n (ele um negociante, e portanto o valor de n pode mudar), em seguida n valores vn que correspondem ao nmero de unidades vendidas em um ms nas lojas de 1 a

76

Valores Compostos: Parte I

n,

respectivamente, e imprima, no dispositivo de sada padro, quais foram as lojas de

nas quais o nmero de unidades vendidas foi maior ou igual mdia de unidades vendidas em suas lojas.

2. Escreva um programa que leia um texto qualquer, caractere a caractere, e imprima:

o nmero de algarismos que ocorrem no texto, o nmero de letras que ocorrem no texto, e o nmero de linhas do texto que contm pelo menos um caractere.

3. Escreva um programa que leia um valor inteiro positivo

n,

em seguida uma matriz quadrada

n n de valores inteiros,
um quadrado mgico.

e imprima uma mensagem indicando se a matriz quadrada ou no

Um quadrado mgico uma matriz quadrada na qual a soma dos nmeros em cada linha, coluna e diagonal igual.

4. Os votos de uma eleio so representados de modo que


signicam votos para os candidatos de nmeros diferente desses signica voto nulo.

signica voto em branco,

n,

respectivamente, e qualquer valor

A eleio tem um vencedor se o nmero de votos em branco mais o nmero de votos nulos menor do que 50% do total de votos, sendo vencedores, nesse caso, todos os candidatos com nmero de votos igual ao maior nmero de votos. Escreva um programa que leia os votos de uma eleio e determine se h um vencedor e, em caso positivo, determine o nmero de vencedores da eleio, quais so esses vencedores e o nmero de votos dos mesmos.

5. Escreva um programa que leia um valor inteiro positivo


positivo

n,

em seguida um valor inteiro

k,

depois uma sequncia

de

valores inteiros diferentes de zero, cada valor lido

separado do seguinte por um ou mais espaos ou linhas, e imprima linha contm os valores de o resto da diviso por

linhas tais que: a

1a

divisveis por

1, etc., at a

n, a 2 n-sima

linha contm os valores de

para os quais

linha, que contm os valores de

para os

quais o resto da diviso por Dica: use um arranjo de

n 1. k
valores inteiros,

posies com elementos que so arranjos de

sendo um valor igual a zero indicativo de ausncia de valor naquela posio. Exemplo: Considere a entrada:

4 12 13 15 16 17
Para essa entrada, a sada deve ser como a seguir:

Valores Valores Valores Valores

divisiveis por 4: 12 16 com resto da divisao por 4 igual a 1: 13 17 com resto da divisao por 4 igual a 2: com resto da divisao por 4 igual a 3: 15

6. Reescreva o programa referente a impresso do Tringulo de Pascall (exerccio 13 do captulo


anterior, pgina 66), usando um arranjo e o fato de que cada valor em uma posio ser obtido somando-se os valores nas posies

j (diferente

da primeira, que 1) de qualquer linha (diferente da primeira) do Tringulo de Pascal pode

j1

da linha anterior.

Por exemplo, o valor contido na terceira coluna da linha correspondente a (correspondente a

n = 5 no Tringulo

de Pascal (pgina 66) 10. Ele igual a 6+4: 6 o valor na mesma coluna da linha anterior

n = 4),

e 4 o valor anterior a 6 nesta mesma linha (correspondente a

n = 4).
7. Escreva um programa que leia, repetidamente, do dispositivo de entrada padro, os seguintes
valores, nesta ordem:

6.3 Ponteiros

77

(a) um nmero inteiro positivo (b)

n, vn ,

inteiros positivos

v1 ,

...,

(c) 3 inteiros positivos

i, j , k . n lido, uma linha com os valores vi , vi+k , vi+2k , . . . , vi+pk

O programa deve imprimir, para cada tais que

i + p k j.

Os valores devem ser separados por espaos e impressos no dispositivo

de sada padro. Por exemplo, para a entrada:

10 11 25 33 40 50 69 73 85 96 101 3 2 7 0
a sada deve ser:

33 50 73
Isso ocorre porque o primeiro valor impresso e o seguinte e ltimo

v3 (i = 3),

o seguinte

v5 (j = 2, 5 = i + j ),

v7 (k = 7 = i + 2 j ).

8. Escreva um programa que leia, do dispositivo de entrada padro, um texto qualquer, caractere
a caractere, e imprima, no dispositivo de sada padro, i) o nmero de palavras que tm um gnero, masculino ou feminino, ii) o nmero de palavras do gnero masculino, e iii) o nmero de palavras do gnero feminino. Voc pode considerar, para simplicar o problema, que:

Uma palavra uma sequncia de caracteres quaisquer seguida de um delimitador. Um delimitador um dos caracteres A entrada termina com

' ', '\t', '\n' ou EOF. EOF (caractere indicador de m dos dados de entrada: em entrada interativa, Control-z seguido de Enter no Windows, ou Control-d no Linux). 'o'
e a ltima letra for

'o' ou se a 'O' ou "Os". Analogamente, uma palavra tm gnero feminino se seu ltimo caractere for a letra 'a' ou se a penltima letra for 'a' e a ltima letra for 's', ou se a palavra igual a 'O' ou "Os".
Uma palavra tm gnero masculino se seu ltimo caractere for a letra penltima letra for

's',

ou se a palavra igual a

Dica: Use scanf com formato

%c

para ler um caractere, e use o valor retornado por scanf

para vericar m dos dados de entrada: scanf retorna

-1

para indicar m dos dados (EOF).

9. ?? Escreva um programa que leia, do dispositivo de entrada padro, um texto qualquer,


caractere a caractere, e imprima, no dispositivo de sada padro, o nmero de palavras do texto. Dica: para contar o nmero de palavras, use uma varivel booleana para indicar se a posio corrente de leitura corresponde a uma posio interna a uma palavra ou externa. posio fora de uma palavra uma posio correspondente a um delimitador. Lembre-se que, em Uma

C, uma varivel booleana uma varivel inteira:

o valor

0 representa falso

e qualquer valor diferente de zero verdadeiro

10. Estenda o exerccio 8 para imprimir tambm o nmero total de palavras do texto.

6.3 Ponteiros
Ponteiros so representaes de endereos na memria do computador. Eles constituem um recurso de programao de baixo nvel, que espelha a representao de estruturas de dados em memrias de computadores. Seu uso devido em grande parte a ecincia (ou seja, tem o objetivo de fazer com que programas sejam executadas rapidamente ou usando poucos recursos de memria) ou, algumas vezes, a necessidade de acesso ao hardware. O uso de ponteiros poderia ser em grande

78

Valores Compostos: Parte I

parte das vezes ser substitudo por uso de um estilo de programao baseado em abstraes mais prximas do domnio do problema e no da implementao de uma soluo do problema em um computador, abstraes essas tanto de dados quanto de denio de funes sobre esses dados. O uso de ponteiros deve ser feito com cuidado, para evitar erros em tempo de execuo que podem ser difceis de entender e de corrigir. Uma varivel um lugar da memria ao qual foi dado um nome (o nome da varivel). Toda varivel tem um endereo, que o endereo do lugar da memria que foi alocado para a varivel. Um ponteiro um endereo (da memria), ou um tipo (de valores que so ponteiros para variveis de um determinado tipo; por exemplo, o tipo para variveis de tipo

int;

int *

um tipo ponteiro, de valores que so ponteiros

dizemos apenas:

tipo ponteiro para

int).

Alm disso, quando o

contexto deixar claro, usamos tambm ponteiro para denotar varivel que contm um endereo. Um tipo ponteiro indicado pelo caractere para reas de memria deste tipo. Por exemplo:

* como suxo de um tipo, indicando o tipo ponteiro

int *p;
declara uma varivel de nome O caractere

p e tipo int *, ou seja, ponteiro para int. * considerado em um comando de declarao de variveis em C como qualicador

da varivel que o segue, de modo que, por exemplo:

int *p, q ;
declara um ponteiro O operador

e uma varivel

de tipo

int,

e no dois ponteiros.

O uso de ponteiros baseado principalmente no uso dos operadores

&,

&

*,

e da funo malloc . Por exemplo, a

aplicado a uma varivel, retorna o endereo dessa varivel.

sequncia de comandos:

int *p, q ; p = &q ;


p como uma varivel de tipo int *, q como uma varivel de tipo int e armazena o endereo p. O operador * um operador chamado de derreferenciao: aplicado a um ponteiro p, o resultado a varivel apontada por p (se usado como um valor, o resultado o valor contido na varivel apontada por p). O uso de * em uma declarao signica declarao de um ponteiro; o uso de * precedendo um
declara de

em

ponteiro em uma expresso signica derreferenciao.

a e b de tipo int.

Considere, por exemplo, o problema de trocar o contedo dos valores contidos em duas variveis Para fazer isso precisamos passar para uma funo troca o endereo das variveis,

i.e. devemos chamar troca (&a,&b), onde a funo troca denida como a seguir:

void troca (int &x, int &y ) { int t = *x; *x = *y ; *y = t; }


Note que uma chamada troca (a,b) em vez de troca (&a,&b) far com que a execuo do programa use endereos de memria que so valores inteiros contidos em memria. Note tambm que a funo scanf modica o valor de uma varivel, e por isso que o endereo da varivell que deve ser passado como argumento de scanf. Ocorre um erro durante a execuo de um programa quando um ponteiro derreferenciado e o ponteiro representa um endereo que no est no conjunto de endereos vlidos que o programa

b,

e isso far com que o

programa provavelmente termine com um erro devido a tentativa de acesso a endereo invlido de

6.4 Cadeias de caracteres

79

pode usar.

Esse erro comumente chamado em ingls de segmentation fault , ou seja, erro de Isso signica que foi usado um endereo que est fora do segmento (trecho da

segmentao. O endereo

memria) associado ao processo que est em execuo.

tambm denotado por NULL (denido em stdlib ) e usado para indicar um

ponteiro nulo, que no endereo de nenhuma varivel. em geral tambm usado em inicializaes de variveis de tipo ponteiro para as quais no se sabe o valor no instante da declarao.

6.3.1 Operaes de soma e subtrao de valores inteiros a ponteiros


Em

C,

possvel incrementar (somar um) a um ponteiro.

res de um tipo analogamente,

Sendo p um ponteiro para valot qualquer, p+1 representa o endereo seguinte ao endereo denotado por p e, p-1 representa o endereo anterior ao endereo denotado por p.

Com isso, possvel realizar operaes aritmticas quaisquer com um ponteiro e um inteiro. Essas operaes aritmticas so realizadas de modo a levar em conta o tamanho do tipo do valor denotado por um ponteiro: somar o inteiro 1 a um ponteiro signica somar uma unidade ao ponteiro igual ao tamanho do tipo de variveis apontadas por esse ponteiro. Por exemplo, um valor inteiro ocupa, em muitas implementaes, 4 bytes (32 bits). implementaes, se Nessas

do tipo

*int

(isto , um ponteiro para variveis de tipo

int), p+1

representa um endereo 4 bytes maior do que o endereo denotado por

p.

6.3.2 Ponteiros e arranjos


Em

C, o nome de uma varivel de tipo arranjo pode ser usado como um ponteiro para a primeiro const).

posio do arranjo, com a diferena que o valor dessa varivel no pode ser modicado (a varivel de tipo arranjo corresponde uma varivel de tipo ponteiro declarada com o atributo Por exemplo, quando se declara o arranjo:

int a[10];
um arranjo de 10 posies alocado e o nome desse arranjo. Ou seja, o nome pode mudar de valor. Sendo que seja, a i-sima posio do arranjo

representa um ponteiro para a primeira posio

representa o mesmo que

%a[0],

com a nica diferena que

no

i uma expresso qualquer de tipo int, a expresso a[i] denota o mesmo que (a+i)  ou a, i posies depois da 0-sima posio. Se usada em um contexto requer um valor, essa expresso derreferenciada, fornecendo o valor *(a+i). Portanto, em

a operao de indexao de arranjos expressa em termos de uma soma de um inteiro a um Assim, quando um arranjo passado.

ponteiro.

&a[0]

passado como argumento de uma funo

f,

apenas o endereo

6.4 Cadeias de caracteres


Em

C,

uma cadeia de caracteres  em ingls, um string  um arranjo de caracteres que

segue a conveno de que o ltimo componente o caractere caractere

'\0'

'\0' (chamado de caractere nulo).

inserido automaticamente em literais de tipo cadeia de caracteres, escritos entre

aspas duplas. Por exemplo:

char str[] = "1234"


o mesmo que:

char str[5] = "1234"


Note que so 5 componentes no arranjo str : caractere

'\0',

o ltimo componente, de ndice 4, contm o

e inserido automaticamentre no literal de tipo cadeia de caracteres, e o tamanho

do arranjo deve ser igual ao nmero de caracteres no literal mais 1.

80

Valores Compostos: Parte I

O uso de cadeias de caracteres em

C, e em particular a operao de ler uma cadeia de caracteres,

deve levar em conta de que toda varivel que uma cadeia de caracteres deve ter um tamanho xo. Para ler uma cadeia de caracteres, necessrio primeiro alocar espao  um tamanho mximo  para armazenar os caracteres que vo ser lidos. No entanto, no caso de passagem de parmetros para a funo main (veja seo

??), o sistema aloca, automaticamente, cadeias de caracteres de

tamanho suciente para armazenar os caracteres passados como parmetros para a funo main ). O uso de cadeias de caracteres em

C envolve muitas vezes o uso de funes denidas na biblioteca

string , dentre as quais destacamos as seguintes:

Assinatura

Signicado

int

strlen (char

*s )

Retorna tamanho da cadeia s , excluindo caractere nulo no nal de s Copia cadeia apontada por fonte , incluindo caractere

char* strcpy (char *dest, const char *fonte )

'\0'

que indica terminao da cadeia, para

dest; retorna fonte

char* strcat (char *dest, const char *fonte )

Insere cadeia apontada por fonte , incluindo caractere

'\0' que indica terminao da cadeia,

no

nal da cadeia apontada por dest , sobrepondo primeiro caractere de fonte com caractere nulo que termina dest ; retorna cadeia dest atualizada

char* strcmp (const char *s1, const char *s2 )

Comparao lexicogrca entre cadeias de caracteres, sendo retornado caractere a caractere, valor negativo se s1 <s2 , lexicogracamente, e positivo caso contrrio.

se as cadeias so iguais,

O uso do atributo

const na declarao de parmetros em assinaturas de funes acima signica char*, char[], ==,

que o parmetro no modicado no corpo da funo, e usado por questo de legibilidade. A comparao do contedo de duas cadeias de caracteres no pode ser feita com o operador pois esse operador, se aplicado a valores de tipo no do contedo apontado pelos ponteiros. Em ou

testa igualdade de ponteiros, e

C,

podem ocorrer erros difceis de serem detectados se no for seguida a conveno de que

uma cadeia de caracteres termina com o caractere nulo. As funes acima no devem ser usadas se houver sobreposio de cadeias fonte e destino, sendo o comportamento dessas funes no especicado nesses casos. A comparao lexicogrca entre duas cadeias s1 e s2 termina assim que ocorre uma diferena, e resulta em que s1

<

s2 se o tamanho de s1 menor que o de s2 ou se o caractere diferente de s1

menor do que o s2. A comparao entre caracteres feita pelo valor da representao no cdigo ASCII. Por exemplo:

"ab"

menor que

"abc"

e que

"ac",

e maior que

"aa"

"a".

6.4.1 Converso de cadeia de caracteres para valor numrico


As seguintes funes da biblioteca stdlib podem ser usadas para converter uma cadeia de caracteres em um valor numrico:

Assinatura

Signicado Converte cadeia de caracteres para valor de tipo Converte cadeia de caracteres para valor de tipo Converte cadeia de caracteres para valor de tipo

int atoi (char *) double atof (char *) int atol (char *)


Por exemplo:

int float long

6.4 Cadeias de caracteres

81

char char char char char

*s1 *s2 *s3 *s4 *s5

= = = = =

"1234"; "12.34"; "1234"; "123quatro"; "xpto1234";

int i; float f ;
i f i i i

= atoi (s1 ); // i = 1234 = atof (s2 ); // f = 12.34 = atoi (s3 ); // i = 1234 = atoi (s4 ); // i = 123 = atoi (s5 ); // i = 0

Note que:

espaos esquerda na cadeia de caracteres so ignorados; caracteres invlidos aps um numeral vlido so ignorados; se a converso no puder ser realizada, retornado

0.

6.4.2 Converso para cadeia de caracteres


Para converso de um valor numrico em uma cadeia de caracteres, a funo sprintf , similar a

printf , pode ser usada. Um uso de sprintf tem o seguinte formato: sprintf (str, formato ,

v1 , ..., vn )

onde formato um literal de tipo cadeia de caracteres de controle da operao de escrita na cadeia de caracteres str e

v1 ,...,vn

so argumentos (valores a serem escritos).

As especicaes de controle em formato so feitas como no caso de printf , usando o caractere seguido de outro caractere indicador do tipo de converso a ser realizada. A funo sprintf tem um comportamento semelhante ao de printf , com a diferena de que os

valores so escritos na cadeia de caracteres str , em vez de no dispositivo de sada padro. O tamanho da cadeia str deve ser suciente para conter todos os resultados da converso dos valores para uma cadeia de caracteres.

sprintf no pode ser usada quando se deseja o valor da cadeia de caracteres correspondente a
um inteiro (pois sprintf um comando, no retorna nenhum valor). Quando um valor desejado, uma funo pode ser denida pelo programador, ou pode ser usada a funo itoa , disponvel em grande parte das implementaes da biblioteca

stdlib,

embora no seja denida na linguagem C

padro (ANSI C). A funo itoa tem a seguinte assinatura:

char*

itoa (int valor,

char*

str,

int

base )

itoa converte valor para uma cadeia de caracteres, terminada com o caractere
base base , armazena essa cadeia em str , e retorna str . Se a base for 10 e valor for negativo, a cadeia resultante precedida do sinal outro caso, simplesmente se supe que o valor positivo.

NULL,

usando a

)-).

Em qualquer

str deve ser um arranjo com um tamanho grande o suciente para conter a cadeia.

6.4.3 Passando valores para a funo

main
'\ t').
O interpretador

A funo main pode receber como argumento vrias cadeias de caracteres, separadas entre si por espaos ou outros caracteres delimitadores de palavras (como tab, i.e. da linha de comandos do sistema operacional, quando chamado com um nome de um programa seguido de uma cadeia de caracteres, percorre essa cadeia de caracteres colocando cada sequncia

82

Valores Compostos: Parte I

de caracteres, separada da seguinte por um ou mais delimitadores, em uma posio de um arranjo que passado como argumento para a funo main, precedido do nmero de valores contidos neste arranjo. Por exemplo, para um programa com nome prog , o comando:

prog abc xy 123


faz com que o interpretador da linha de comandos percorra a cadeia de caracteres

"abc xy 123"

e coloque cada sequncia de caracteres que est separada da seguinte por um ou mais delimitadores

em uma posio de um arranjo, e passe como argumento para o mtodo main do programa (prog) o valor 3  que igual ao tamanho do arranjo (nmero de cadeias de caracteres)  e esse arranjo. Assim, o arranjo passado contm na varivel de ndice 2.

"123"

"abc"

na varivel de ndice

0, "xy"

na varivel de ndice

6.4.4 Exerccios Resolvidos


1. Escreva um programa que leia um valor inteiro positivo teres de tamanho menor que nesta cadeia. Por exemplo, para a entrada:

n,

em seguida uma cadeia de carac-

e imprima a frequncia de todos os caracteres que ocorrem

10 112223a
a sada deve ser:

1 2 3 a

aparece aparece aparece aparece

2 3 1 1

vezes vezes vez vez

A ordem de impresso dos caracteres e sua frequncia no importante, mas cada caractere deve aparecer, com sua frequncia de ocorrncia, apenas uma vez na sada, e somente se essa frequncia for diferente de zero.

Soluo : Cada caractere representado no cdigo ASCII por um valor inteiro compreendido
entre

0 e 127.

Para armazenar a informao sobre o nmero de ocorrncias de cada um desses

caracteres, podemos usar um arranjo de valores inteiros com responder a cada caractere representado pelo nmero inteiro arranjo pode ser declarado da seguinte forma:

128 componentes, fazendo cori a posio i desse arranjo. Tal

int max = 128; int *contchar =

malloc (max

* sizeof(char));

Um trecho de programa que armazena em contchar [i] o nmero de ocorrncias de cada caractere de uma cadeia de caracteres str pode ser escrito como a seguir, onde tam_str o nmero de caracteres em str:

void contFreqChar (const char str [], int int i; for (i=0; i<tamanho_str ; i++) contChar [str [i]]++; }

contChar [],

int

tamanho_str )

6.4 Cadeias de caracteres

83

O uso do atributo

const na declarao do parmetro str

da funo contFreqChar usado ape-

nas por questo de legibilidade, para indicar que a cadeia de caracteres str no modicada no corpo da funo.

Um exemplo de denio de uma funo main que usa a funo contFreqChar mostrada a seguir. Os valores de entrada so lidos, a funo contFreqChar chamada e a frequncia de cada caractere que ocorre na cadeia de caracteres especicada na entrada impressa.

int main () { int tam ; scanf ("%d",&tam ); char *s = malloc (tam * sizeof(char)); scanf ("%s",s); printf ("Na cadeia de caracteres %s\n",s); int max = 128; int *contChar = malloc (max * sizeof(int)); int i,freq ; for (i=0; i<max ; i++) contChar [i] = 0; contFreqChar (s,contChar,tam ); for (i=0; i<max ; i++) if (contChar [i] = 0) { freq = contChar [i]; printf ("%c aparece %d %s\n",(char)i,freq,freq ==1?"vez":"vezes"); } }

2. Escreva um programa para determinar a letra ou algarismo que ocorre com maior freqncia em uma cadeia de caracteres dada, com um tamanho mximo previamente fornecido, e imprimir esse algarismo no dispositivo de sada padro.

Soluo : Um caractere alfanumrico um caractere que pode ser um algarismo ou uma letra.
A soluo consiste em, inicialmente, determinar a freqncia de ocorrncia de cada caractere alfanumrico, de maneira anloga do exerccio anterior. Ou seja, a soluo armazena cria arranjos com componentes correspondentes a cada caractere alfanumrico. Cada componente contm a frequncia de ocorrncia do caractere alfanumrico correspondente. Em seguida, o ndice do componente de maior valor (isto , maior frequncia) desse arranjo determinado e o caractere alfanumrico correspondente a esse ndice impresso. Vamos usar trs arranjos, um arranjo para dgitos  com ndices de

, e os outros para letras minsculas e

'0',

maisculas. O ndice do arranjo de dgitos correspondente a um dgito

obtido por

d -

usando o fato de que a representao dos dgitos so crescentes, a partir do valor da

representao do caractere dos caractere

'0'.

Analogamente para letras minsculas e maisculas, que tm

valores de representao crescentes a partir, respectivamente, dos valores das representaes

'a'

'A'.

84

Valores Compostos: Parte I

#include <stdio.h > #include <stdlib.h > #include <ctype.h > // define isdigit int int void
letraMinusc (char letraMaiusc (char

c) { return (c >= 'a' && c <= 'z'); } c) { return (c >= 'A' && c <= 'Z'); }

contFreqAlfaNums (const

int i; char c; for (i=0; i<tam ; i++) { c = s[i]; if (isdigit (c)) contDigLets [c - '0']++; else if (letraMinusc (c)) contDigLets [tamDigs + (c - 'a')]++; else if (letraMaiusc (c)) contDigLets [tamDigs + tamLets + (c - 'A')]++; }

int

tamDigs,

char s[], int tam, int int tamLets ) {

contDigLets [],

char alfaNumMaisFreq (const char s[], int tams ) { int tamDigs = 10, tamLets = 26, tam = tamDigs + 2*tamLets, *contAlfaNums = malloc (tam * sizeof(int)), i; for (i=0; i<tam ; i++) contAlfaNums [i] = 0; contFreqAlfaNums (s,tams,contAlfaNums,tamDigs,tamLets ); i = maisFreq (contAlfaNums,tam ); return (i<tamDigs ? i + '0' : i<tamDigs + tamLets ? i + 'a' : i + 'A'); } int maisFreq (const int freq [], int tam ) { int i, maxi, maxFreq = 0; for (i=0; i<tam ; i++) if (freq [i] > maxFreq ) { maxi = i; maxFreq = freq [maxi ]; } return maxi ; } int main () { int tamstr ; scanf ("%d",&tamstr ); char *str = malloc (tamstr * sizeof(char)); scanf ("%s",str ); printf ("Caractere alfanumerico mais frequente em %s e': maisFreq (str,tamstr )); }

%c\n",str,

A funo maisFreq recebe como argumento uma cadeia de caracteres, em que cada caractere representa um algarismo, e retorna o algarismo mais freqente nessa cadeia. Quando existir mais de um algarismo com a maior freqncia de ocorrncia, o mtodo retorna, dentre esses, aquele que ocorre primeiro na cadeia. Por exemplo, maisFreq ("005552") retorna

maisFreq ("110022") retorna

'1'.

'5',

3. Escreva um programa para resolver o exerccio 15, pgina 61, considerando a condio de que inteiros podem ter at 1000 dgitos decimais.

Soluo : Usamos uma cadeia de caracteres de at 1001 dgitos para armazenar inteiros que

6.4 Cadeias de caracteres

85

podem ter at 1000 dgitos, e mais o caractere de tipo

'\0'

para indicar terminao da cadeia. A

soma dos dgitos de um inteiro de at 1000 dgitos sempre pode ser armazenada em um valor

int.

#include <stdio.h > #include <stdlib.h > int somaDigsInt (int n) { return somaDigsInt1 (n,0); } int somaDigsInt1 (int n, int soma ) { if (n==0) return soma ; else return somaDigsInt (n/10, n%10 + soma ); } int somaDigs (char* digs, int i) { if (digs [i] == '\0') return 0; else return (digs [i] - '0') + somaDigs (digs,i+1); } int grau9Int (int n) { if (n <= 9) return (n == 9? 1 : 0); else { int grau = grau9Int (somaDigsInt (n)); return (grau == 0 ? grau : 1 + grau ); } } int
grau9

(char *digs ) { return

grau9Int (somaDigs (digs,0));

int main () { const int numDigs = 1001; char v [numDigs ]; while (1) { scanf ("%s", &v ); int i=0; while (i < numDigs && v [i] == '0') i++; if (i < numDigs && v [i]=='\0') break; // Termina se inteiro lido igual a zero int = grau9 (v ); is%s a multiple of 9", v, grau9v ==0 ? if (grau9v ==0) printf ("\n"); else printf (" and has 9-degree %d\n",grau9v );
printf ("%s grau9v

"not":

);

} return 0;

6.4.5 Exerccios
1. Escreva uma funo inverte que estenda o Exerccio 5 da seo 3.2 para qualquer cadeia de caracteres o tamanho

s. A funo inverte tem como parmetro adicional (alm da cadeia de caracteres) n da cadeia passada como argumento. O programa que chama a funo inverte

deve ler, antes de cada cadeia de caracteres, um valor que, deve-se supor, maior que o tamanho da cadeia. 2. Dena uma funo decPraBin que receba um nmero inteiro no-negativo como argumento e retorne uma cadeia de caracteres que igual representao desse nmero em notao binria.

86

Valores Compostos: Parte I

Por exemplo, ao receber o nmero inteiro

8,

a funo deve retornar

"1000".

Para calcular o tamanho da cadeia de caracteres a ser alocada, dena e use uma funo que

m + 1 tal n pode ser dividido 3 4 por 2 at que o quociente da diviso seja zero. Por exemplo, 2 8 < 2 , e 4 o nmero de caracteres da cadeia "1000", necessrios para representao de 8 na base 2.
numDiv2 que, ao receber um nmero inteiro positivo m m+1
como argumento, retorne

n<2

. Esse nmero (m + 1) igual ao nmero de vezes que

Note que, para cada presentao de caracteres).

tam o resultado de numDiv2 (n), para conter, alm dos caracteres necessrios para re-

n,

deve ser alocada uma cadeia de caracteres de tamanho tam +2, onde

na base 2, o caractere

'\0'

(usado em

para terminao de cadeias de

Escreva um programa que leia vrios nmeros inteiros positivos do dispositivo de entrada padro e imprima, para cada inteiro lido, a sua representao em notao binria, usando a funo denida no item anterior (o programa que contm a funo main deve conter tambm a denio das funes denidas acima). A execuo deve terminar quando um inteiro negativo ou zero for lido. 3. Dena uma funo que receba como argumento um nmero inteiro no-negativo b, em notao binria, e retorne o valor inteiro (de tipo Por exemplo, ao receber o nmero inteiro Seu programa pode ler

int)

correspondente, em notao decimal. a funo deve retornar o valor

1000,

8.

como um valor inteiro  e supor que o valor lido pode ser arma-

zenado como um valor de tipo

tamanho mximo da cadeia de

int  ou como uma 30 dgitos binrios. int,

cadeia de caracteres  e supor que o

No caso de leitura como um valor de tipo diviso por igual a

10.

cada dgito deve ser obtido como resto de

Por exemplo, para obter cada dgito de

resto da diviso de

10)

101

por

10;

depois obtenha o quociente da diviso de

101, obtenha o 1 mais direita como 101 por 10 (que

e repita o processo.

Escreva um programa que leia, do dispositivo de entrada padro, vrias cadeias de caracteres que representam nmeros inteiros positivos em notao binria, e imprima, para cada valor lido, a sua representao em notao decimal, usando a funo denida acima (o programa que contm a funo main deve conter tambm a denio da funo denida acima). A execuo deve terminar com o m dos dados de entrada. 4. Escreva um programa que leia, do dispositivo de entrada padro, um valor inteiro positivo em seguida uma cadeia de caracteres

t, s de tamanho menor que t e, em seguida, vrias cadeias de caracteres s1 , . . . , sn , tambm com tamanho menor que t, e imprima, para cada cadeia si , para i entre 1 e n, uma mensagem que indica se s contm si ou no. A entrada deve
terminar com o m dos dados de entrada, isto , quando m-de-arquivo for detectado; em entrada interativa, quando scanf retornar (no Unix) ou Control-z (no Windows).

-1,

devido ao fato de o usurio digitar Control-d

Por exemplo, se a entrada for:

1000 abcdefghijklmnopqrstuvwxyz123456789 nopqr 789 xya abc


A sada deve ser uma mensagem como a seguir:

nopqr Sim 789 Sim xya Nao abc Sim

6.5 Registros

87

5. Escreva um programa que leia um inteiro positivo de caracteres, de tamanho menor que

n, em seguida vrios pares s1 , s2 de cadeias n, e imprima, para cada par lido, uma cadeia de caracteres que a concatenao de s1 e s2 (a concatenao de s1 com s2 a cadeia formada pelos caracteres de s1 seguidos pelos caracteres de s2 ).
No se esquea de considerar que, em C, cadeias de caracteres so armazenadas de modo a terminar com o caractere

'\0'.

6. Escreva um programa que leia um valor tamanho menor que aparecem em

n,

duas cadeias de caracteres

s1

s2 ,

ambas com

n,

e imprima o resultado de remover de

s2

todos os caracteres que

s1 .

Por exemplo, para a entrada:

100 abci adefghiabaf


A sada deve ser:

defghf

6.5 Registros
Um registro (ou, como chamado em

C,

uma estrutura ) um tipo, e tambm um valor desse

tipo, que um produto cartesiano de outros tipos, chamados de campos ou componentes do registro, com notaes especiais para denio dos componentes do produto e para acesso a esses componentes. valores para formao de novos valores a construo de pares. Por exemplo, formado por um primeiro componente, do do Em matemtica, e em algumas linguagens de programao, a forma mais simples de combinar

(10,'*') um par, 10, e um segundo componente, '*'. Um par um elemento produto cartesiano de dois conjuntos  o par (10,'*') um elemento do produto cartesiano conjunto dos valores inteiros pelo conjunto dos valores de tipo char.
Naturalmente, alm de pares, tambm possvel formar triplas, qudruplas, quntuplas etc. 

usualmente chamadas de tuplas  que so elementos de produtos cartesianos generalizados, ou seja, elementos de um produto de vrios conjuntos. Em linguagens de programao (como

C por exemplo), no entanto, mais comum o uso de regis-

tros , em vez de tuplas. Um registro uma representao de um valor de um produto cartesiano,


assim como uma tupla, mas cada componente, em vez de ser identicado pela sua posio (como no caso de tuplas), identicado por um nome  usualmente chamado de rtulo . Cada componente tem um nome a ele associado. Por exemplo, um valor como:

{
representa, em

= 10,

= '*'}

tem rtulo y . Nesse exemplo, o valor do componente separado do rtulo pelo smbolo  =. O registro

C,

um registro com dois componentes, em que um deles tem rtulo x e o outro

{y = '*',

= 10}

representa o mesmo valor que o registro

{x = 10,

= '*'}.

Ou

seja, a ordem em que os componentes de um registro escrita no relevante para determinao do valor representado, ao contrrio do que ocorre com relao ordem dos componentes de uma tupla. Deve existir, claro, uma operao para selecionar um componente de um registro, assim como ocorre no caso de tuplas. No entanto, em

a especicao de valores de tipo registro deve seguir uma ordem para os

valores dos campos, que a ordem em que os campos aparecem na denio do tipo registro, e s podem ser usados em declaraes de variveis, como veremos no exemplo a seguir. Uma declarao de um tipo registro consiste de uma sequncia de campos, cada um dos quais com um tipo e um nome. Por exemplo:

88

Valores Compostos: Parte I

struct contaBancaria { int numero ; char *idCorrentista ; float saldo ; }; C,

Em

a denio acima consiste na denio de um tipo, de nome account , ao qual se pode

referir usando a palavra

struct

seguida do nome account.

Por exemplo, a seguinte declarao cria uma varivel desse tipo:

struct

contaBancaria

conta;

O tipo contaBancaria , assim como a varivel conta , tm trs campos ou componentes: conta-

Bancaria, numeroDaConta e saldo .


nome do campo. Por exemplo, conta.numero tem tipo tem tipo O acesso aos componentes feito usando-se um valor de tipo registro seguido de um ponto e do

char*

e conta.saldo tem tipo

float.

int.

Analogamente, conta.idCorrentista

Esses componentes denotam (podem ser usados ou modicados como) uma varivel comum do tipo do campo. Valores de tipo registro podem ser construdos em

C mas apenas na inicializao de uma varivel

de tipo registro, de modo semelhante ao que ocorre no caso de arranjos. Um valor de tipo registro especica valores a cada um dos campos do registro, entre chaves, como mostrado no exemplo a seguir. O exemplo seguinte ilustra uma declarao de uma varivel do tipo contaBancaria , denido acima, especicando um valor inicial para a varivel:

struct

contaBancaria

conta = { 1, "MG1234567", 100.0 };

A atribuio de um valor de tipo registro a outro copia, como esperado, o valor de todos os campos do registro. Considere o seguinte exemplo:

#include <stdio.h > struct


Ponto

{ int x; int y ; } ;

int main () { struct Ponto p = {1,2}, q ; q = p; q .x = 2; printf ("p.x = %d\nq.x = %d\n", p.x, q .x); }

Esse programa imprime:

p.x = 1 q.x = 2

6.5 Registros

89

6.5.1 Declaraes de tipos com typedef


O uso de nomes para introduo de novos tipos bastante til, para documentao e legibilidade do programa, e isso ocorre particularmente no caso de tipos registro e outros tipos Por exemplo, para dar um nome para um tipo que representa coordenados do plano cartesiano, ou dados de uma conta bancria, pode-se denir e usar tipos Ponto e ContaBancaria como a seguir:

struct Ponto { int x; int y ; }; typedef struct Ponto Ponto ; struct contaBancaria { int numero ; char *idCorrentista ; float saldo ; }; typedef struct contaBancaria int }
main () Ponto

contaBancaria ;

{ p, q ;

ContaBancaria

c; // aqui vem uso de variaveis p,q ,c ...

6.5.2 Ponteiros para registros


Ponteiros para registros podem ser usadas para passar valores de tipo registro sem ter que copiar o registro, e tambm de modo a permitir a alterao de campos de registros. Um ponteiro para um registro pode ser derreferenciado como normalmente, usando o operador

*, mas existe em C a possibilidade de usar o operador ->, que alm da derreferenciao faz tambm
acesso a um campo de um registro. Por exemplo:

struct Ponto { int x; int y ; }; typedef struct Ponto Ponto ; void moveParaOrigem (Ponto *p) { p -> x = 0; // O mesmo que: (*p).x = 0; p -> y = 0; }

6.5.3 Estruturas de dados encadeadas


Em computao, uma estrutura de dados encadeada consiste de uma sequncia de registros de tipo

que contm um campo que uma ponteiro que pode ser nulo ou um ponteiro para um

prximo registro de tipo

T.

Por exemplo, uma lista encadeada de registros com campos de tipo registros do seguinte tipo:

int

pode ser formada com

struct ListaInt { int val ; struct ListaInt *prox ; };

90

Valores Compostos: Parte I

Listas encadeadas so estruturas de dados exveis, pois no requerem tamanho mximo, como arranjos. Elas podem crescer e decrescer de tamanho medida que dados vo sendo inseridos e removidos. A desvantagem, em relao ao uso de arranjos, que o acesso a um componente da estrutura de dados requer um tempo que depende da posio desse componente na estrutura: o acesso a cada componente depende de acesso a cada um dos componentes anteriores a ele na lista. rvores binrias podem ser formadas de modo similar. Por exemplo, uma rvore binria com nodos que contm campos de tipo

int

pode ser formada com registros do seguinte tipo:

struct ArvBinInt { int val ; struct ArvBinInt *esq ; struct ArvBinInt *dir ; };

O seguinte exemplo ilustra o uso de uma lista encadeada para evitar a restrio de se ter que especicar um nmero mximo de valores, necessrio para uso de arranjo. Considere o problema do Exerccio Resolvido 1, da seo 6.4.4, que prope que um texto qualquer seja lido e seja impressa a frequncia de todos os caracteres que ocorrem no texto. Considere que o problema no especica o tamanho do texto. A soluo a seguir usa uma lista encadeada de caracteres para armazenar o texto, em vez de uma cadeia de caracteres. Em casos como esse, pode ser mais adequado usar um arranjo exvel, com um tamanho mximo que pode ser aumentado, testando, antes de cada insero de um novo valor no arranjo, se esse tamanho mximo foi atingido. Se o tamanho mximo for atingido, um novo arranjo alocado com um tamanho maior (por exemplo, o dobro do tamanho anterior), o arranjo antigo copiado para o novo, e o novo arranjo passa a ser usado, no lugar do antigo. Arranjos exveis so estruturas de dados bastante usadas em programas escritos em linguagems como, por exemplo,

Java

C++.

6.6 Exerccios Resolvidos


1. Escreva um programa que funcione como o exemplo fornecido na seo 6.1.3 mas sem a condio de que o nmero de valores a serem impressos, em ordem impressa, seja fornecido. Ou seja, escreva um programa que leia do dispositivo de entrada padro qualquer nmero de valores inteiros, separados por um ou mais espaos ou linhas, e imprima esses valores na ordem inversa em que foram lidos.

Soluo :

6.6 Exerccios Resolvidos

91

#include <stdio.h > #include <stdlib.h > struct nodoLista { int val ; struct nodoLista *prev ; }; typedef struct nodoLista nodoLista ; int main () { int val, numValLidos ; struct nodoLista *cur, *prev = NULL; while (1) { numValLidos = scanf("%d",&val ); if (numValLidos != 1) break; cur = malloc (sizeof(nodoLista )); cur -> val = val ; cur -> prev = prev ; prev = cur ; } while (cur != NULL) { printf ("%d ", cur -> val ); cur = cur -> prev ; } } -> (*ponteiro ).campo .

A soluo usa a notao ponteiro

campo , que uma abreviao para

2. Escreva um programa que leia, do dispositivo de entrada padro, resultados de partidas de um campeonato e imprima, no dispositivo de sada padro, a lista dos nomes dos times que obtiveram maior nmero de pontos nesse campeonato. empate vale 1 ponto. A entrada consiste dos seguintes dados, nesta ordem: (a) uma linha contendo um nmero inteiro onato; (b) Cada vitria vale 3 pontos e cada

n,

que especica o nmero de times do campe-

linhas contendo dois valores

si ,

onde

um nmero inteiro entre 1 e

si

nome do time

i;

o nome de um time uma cadeia de caracteres de tamanho mximo

30; a ordem em que essas

linhas aparecem na entrada deve ser irrelevante;

(c) vrias linhas contendo 4 nmeros inteiros no-negativos resultado da partida entre o time

t1

t2 : t1

marcou

v1

gols e

t1 v1 t2 v2 , que indicam o t2 marcou v2 gols; os

resultados terminam com o m da entrada (EOF). Os times na lista impressa devem estar separados por vrgula (se houver mais de um time com mais pontos), e a ordem irrelevante. Por exemplo, se a entrada for:

3 1 2 3 1 1 2

America Atletico Cruzeiro 1 2 2 2 3 3 1 3 1 Atletico, Cruzeiro

A sada deve ser: Isso porque o com o

America perdeu do Atletico (1x2) e do Cruzeiro (2x3), e o Atletico empatou Cruzeiro (1x1).

92

Valores Compostos: Parte I

Soluo: A soluo apresentada usa arranjos para armazenar nomes de times e para armazenar pontos acumulados em partidas. Esses arranjos so indexados com o nmero do time menos 1 (porque os nmeros de time variam entre 1 e inicial igual a

0).

e arranjos em

sempre tm ndice

O programa constri uma lista de vencedores (times com maior nmero

de pontos) medida em que tal maior nmero de pontos calculado. A lista de vencedores  chamda de maiores  inicialmente nula. Para cada time, do primeiro ao ltimo, se o nmero de pontos maior do que o maior calculado at cada instante desta iteraco, o maior atualizado, seno, se o nmero de pontos for igual, este inserido na lista de maiores.

6.6 Exerccios Resolvidos

93

#include <stdio.h > #include <stdlib.h > struct Maiores { int num ; struct typedef struct Maiores Maiores ; int main () { int n, num ; scanf ("%d", &n); char** times = malloc (n * sizeof(char*)); int i; const int tamMaxNomeTime = 31; // 31 devido a terminacao com '\0' for (i=0; i<n; i++) { scanf ("%d", &num ); times [num -1] = malloc (tamMaxNomeTime *sizeof(char)); scanf ("%s", times [num -1]); } int *pontos ; = malloc (n * sizeof(int)); for (i=0; i<n; i++) pontos [i] = 0; while (1) { numValLidos = scanf ("%d%d%d%d",&num1, &gols1, &num2, &gols2 ); if (numValLidos != 4) break; if (gols1 >gols2 ) pontos [num1 -1] += 3; else if (gols2 >gols1 ) pontos [num2 -1] += 3; else { pontos [num1 -1]++; pontos [num2 -1]++; } }
pontos Maiores * maiores numValLidos, num1, num2, gols1, gols2, Maiores * prox ;

};

= NULL; int maior = 0; for (i=0; i<n; i++) if (pontos [i] > maior ) { maiores = malloc (sizeof(Maiores )); maiores ->num = i; maiores ->prox = NULL; maior = pontos [i]; } else if (pontos [i] == maior ) { // novo elemento em maiores Maiores * novo = malloc (sizeof(Maiores )); novo ->prox = maiores ; novo ->num = i; maiores = novo ; } printf ("%s", times [maiores ->num ]); maiores = maiores ->prox ; while (maiores !=NULL) { printf (", %s",times [maiores ->num ]); maiores = maiores -> prox ; } printf ("\n"); return 0;

3. Escreva um programa que leia uma sequncia de valores inteiros quaisquer e imprima esses

94

Valores Compostos: Parte I

valores em ordem no-decrescente (cada valor seguinte maior ou igual ao anterior). Valores iguais devem aparecer tantas vezes quantas existirem na entrada. Por exemplo, considere a entrada:

4 3 1 5 5 2 3
A sada deve ser:

1 2 3 3 4 5 5
Soluo: Vamos denir e usar uma funo para ordenao de valores conhecida como ordenao por seleo . O algoritmo simplesmente seleciona a cada iterao o maior elemento (a
menos de igualdade) e insere o valor selecionado no incio de uma lista no nal a lista

l de valores ordenados;

estar ordenada (no nal o menor elemento ser inserido no incio da lista).

6.7 Exerccios
1. Escreva um programa que leia um valor inteiro positivo caractere, uma cadeia de caracteres os

n,

em seguida leia, caractere a

n, e imprima n ltimos caracteres de s, armazenando para isso a cadeia s como uma uma lista encadeada
de um tamanho qualquer, maior do que

de caracteres onde um apontador usado para apontar para o caractere anterior da cadeia. 2. Escreva um programa que leia um valor inteiro positivo espaos ou linhas, e imprima por a

e, em seguida, uma sequncia

de valores inteiros diferentes de zero, cada valor lido separado do seguinte por um ou mais

linhas tais que: a

1a

linha contm os valores de

divisveis

n,

linha contm os valores de

para os quais o resto da diviso por

1, etc., at

n-sima

linha, que contm os valores de

para os quais o resto da diviso por

n 1.

A entrada termina quando um valor igual a zero for lido. A ordem dos valores impressos em cada linha no relevante. Use um arranjo de

n posies com elementos que so registros representando listas encadeadas

de valores inteiros. Exemplo: Considere a entrada:

4 12 13 15 16 17
Para essa entrada, a sada deve ser como a seguir:

Valores divisiveis por 4: 16 12 Valores com resto da divisao por 4 igual a 1: 17 13 Nenhum valor com resto da divisao por 4 igual a 2 Valores com resto da divisao por 4 igual a 3: 15
3. Reescreva o programa do exerccio anterior de modo a fazer com que a ordem dos valores impressos seja a mesma ordem que os valores ocorrem na entrada. Para isso, use um arranjo de ponteiros para a ltima posio de cada lista encadeada.

6.8 Notas Bibliogrcas


Existe uma vasta literatura sobre algoritmos e estruturas de dados em computao, que estendem o que foi abordado neste captulo principalmente com o estudo mais detalhado de algoritmos para busca, insero, remoo e ordenao de valores nessas estruturas de dados. Alm de aspectos de implementao de estruturas de dados e de operaes para manipulao das mesmas, essa literatura aborda em geral diferentes aplicaes dos algoritmos e discute tambm aspectos de ecincia (ou complexidade , como usual dizer em computao) de algoritmos. Livros didticos dedicados a esses temas incluem [?, a lngua portuguesa e o terceiro escrito em portugus. abordagem de programao funcional.

?, ?], os dois primeiros j traduzidos para [?] um livro interessante, que adota a

Captulo 7

Exerccios
Este captulo descreve a soluo de diversos exerccios, que mostram como usar e decidir quando usar os diversos comandos e estruturas de dados abordados neste livro. Os enunciados dos exerccios so obtidos da pgina Web

SPOJ (Sphere Online Judge ) um sistema disponvel na Internet (na pgina

http://br.spoj.pl/problems/. http://br.spoj.pl/) C.

que permite o registro de novos problemas e a submisso de solues de problemas registrados. H dezenas de milhares de usurios e milhares de problemas j registrados. A soluo pode ser submetida em dezenas de linguagens de programao, incluindo, claro,

7.1 ENCOTEL
Considere que uma representao alfanumrica de um nmero de telefone uma sequncia de caracteres tal que cada caractere pode ser: uma letra maiscula (de dgito

ou tt 0, sendo que letras maisculas representam dgitos de

9,

Z),

um hifen (-) ou um

de acordo com a tabela

abaixo.

Letras ABC DEF GHI JKL MNO PQRS TUV WXYZ

Nmero 2 3 4 5 6 7 8 9

Escreva um programa que leia vrias linhas, cada linha contendo uma tal representaco alfanumrica de nmero de telefone, e imprima uma sequncia de representaes para os nmeros de telefone, novamente uma em cada linha, que substitua letras maisculas por dgitos de acordo com a tabela mostrada. Considere que cada representaco alfanumrica possui entre 1 e 30 caracteres. terminada por m de arquivo (EOF). Por exemplo, para a entrada: A entrada

1-HOME-SWEET-HOME MY-MISERABLE-JOB
A sada deve ser:

1-4663-79338-4663 69-647372253-562

96

Exerccios

A soluo mostrada abaixo usa um arranjo que armazenada, para cada letra maiscula, seu cdigo, segundo a tabela apresentada. De fato, como em que ser menos um.

C o primeiro ndice de um arranjo tem 0, para cada letra maiscula corresponde um ndice entre 0 e o nmero de letras maisculas

#include <stdio.h > #include <stdlib.h > const int n = 26; // Numero de letras maiusculas char* mkCodLetras () { char i='A', codigo ='2', *cod =malloc (n*sizeof(char)); int j , k; while (i<='W') { k = (i == 'P' || i == 'W')? 4 : 3; for (j =0; j <k ; j ++) cod [i-'A'+j ] = codigo ; codigo ++; i+=k ; } return cod ; } int main () { const int max = 31; // 31 devido a terminacao com '\0' char *codLetras = mkCodLetras (), *exp = malloc (max *sizeof(char)); int numValLidos ; while (1) { numValLidos = scanf ("%s", exp ); if (numValLidos != 1) break; int i = 0, c_A; char c; while (exp [i] != '\0') { c = exp [i]; c_A = c-'A'; printf ("%c",c_A>=0 && c_A<n? codLetras [c_A] : c); i++; } printf ("\n"); } }

Essa soluo evita escrever um programa, relativamente ineciente e mais longo, que testa, aps a leitura de cada caractere, se o caractere uma letra pertencente a um grupo especco de letras na tabela mostrada, para impresso do dgito correspondente a esse grupo de letras na tabela.

7.2 PAPRIMAS
x Um nmero primo um nmero que possui somente dois divisores: ele mesmo e o nmero 1. Exemplos de nmeros primos so: 1, 2, 3, 5, 17, 101 e 10007. Neste problema voc deve ler um conjunto de palavras, onde cada palavra composta somente por letras no intervalo vale 28 e a letra

a-z

A-Z .

Cada letra possui um valor especco, a letra

vale 1, a letra

vale 2 e assim por diante, at a letra

z,

que vale 26. Do mesmo modo, a letra

vale 27, a letra

b B

vale 52.

Voc deve escrever um programa para determinar se uma palavra uma palavra prima ou no. Uma palavra uma palavra prima se a soma de suas letras um nmero primo.

7.2 PAPRIMAS

97

Entrada : A entrada consiste de um conjunto de palavras. Cada palavra est sozinha em uma
linha e possui

letras, onde

1 L 20.

A entrada terminada por m de arquivo (EOF).

Sada : Para cada palavra voc imprimir: It is a prime word., se a soma das letras da palavra
um nmero primo, caso contrrio voc deve imprimir It is not a prime word..

Exemplo : Para a entrada:


UFRN contest AcM a sada dever ser: It is a prime word. It is not a prime word. It is not a prime word.

Uma soluo completa mostrada na Figura 7.1.

A funo main l diversas palavras, at

encontrar m-de-arquvo, e imprime mensagem indicando se cada palavra lida uma palavra prima ou no. O tamanho de cada palavra restrito a um tamanho mximo de 20 caracteres. A funo primo verica se um dado nmero os nmeros menores que

primo ou no. Essa funo pode ser imple-

mentada de diversos modos. A Figura 7.1 usa o mtodo simples de divises sucessivas, por todos

n.

Para valores grandes de

n,

esse mtodo gasta mais tempo do que

outros algoritmos existentes. Os programas mostrados a seguir usam o algoritmo conhecido como

crivo de Eratstenes . O leitor interessado pode consultar e.g.:

http://en.wikipedia.org/wiki/Prime_number#Verifying_primality

98

Exerccios

const int

= 21; // Um a mais do que o tamanho mximo de uma palavra, // pois um caractere (o caractere '\0') usado // para indicar final da cadeia de caracteres int prima (char* palavra ); int ler (char* palavra );
max

int main () { char palavra [max ]; while (ler (palavra )) printf ("It is%s a prime word.\n", prima (palavra ) ? "" : " not"); } int minusc (char let ) { return (let >= 'a' && let <= 'z'); } int valor (char let ) { return (minusc (let ) ? let -'a'+1 : let -'A'+('z'-'a'+1)+1); } int prima (char* palavra ) { int i, somaLet =0; for (i=0; i < max && palavra [i] != '\0'; i++) somaLet += valor (palavra [i]); return primo (somaLet ); } int primo (int n) { if (n%2 == 0) return 0; int k = 3, sqrtn = sqrt ((double)n); // Se existir divisor prprio de n, tem que existir divisor prprio menor que n. while (k <= sqrtn ) { if (n%k == 0) return 0; k += 2; } return 1; } int ler (char* palavra ) { // Retorna verdadeiro sse leitura com sucesso. return (scanf ("%s",palavra ) == 1); }

Figura 7.1: Soluo de PAPRIMAS com teste simplicado de primalidade

7.2 PAPRIMAS

99

Uma soluo que usa um teste de primalidade baseado no algoritmo conhecido como crivo de

Eratstenes mostrada a seguir:

#include <stdio.h > // define scanf, printf #include <stdlib.h > // define malloc const int max = 21; int prima (char* palavra, int* primos, int); int ler (char* palavra ); int* listaDePrimos (int); // Criada com o algoritmo

Crivo de Eratstenes

int main () { const int m = max *(2*('z'-'a'+1)); // Primalidade deve ser testada para valores menores ou iguais a m char palavra [max ]; int *primos = listaDePrimos (m); while (ler (palavra )) printf ("It is%s a prime word.\n", prima (palavra,primos,m) ? "" : " not"); } int minusc (char let ) { . . . como na Figura 7.1 . . . } int valor (char let ) { . . . como na Figura 7.1 . . . } int* listaDePrimos (int m) { int p=2, m2 =m+2, nums [m2 ], *primos = malloc (m), // nums [0],nums [1] no usados (por simplicidade, i.e. // para que ndice de nums corresponda a nmero entre 2 e m); i,j ,k ; for (i=2,j =0; i<m2 ; i++,j ++) { nums [i]=0; primos [j ] = 0; } j = 0; do { // marca mltiplos de p for (i=p; i<m2 ; i+=p) nums [i] = 1; // procura prximo valor no marcado a partir de j for (k=p+1; k<m2 ; k++) if (!nums [k]) break; p = k ; // p o prximo primo primos [j ] = p; j ++; } while (p<m2 ); return primos ;

} int primo (int n, int* primos, int m) { int i; for (i=0; i<m; i++) if (primos [i] >= n) return (primos [i]==n); else if (primos [i] == 0) return 0; return 0; } int ler (char* palavra ) { . . . como na Figura 7.1 . . . int prima (char* palavra, int* primos, int m) { int i, somaLet =0; for (i=0; i < max && palavra [i] != '\0'; i++) somaLet += valor (palavra [i]); return primo (somaLet,primos,m); }

O algoritmo do Crivo de Eratstenes , inventado por Eratstenes em 350 A.C., cria uma lista de todos os primos menores que um valor mximo

m.

Para o problema PAPRIMAS, existe tal

100

Exerccios

valor mximo, que pode ser denido como vinte vezes o valor mximo possvel para uma eletra (uma vez que podem ocorrer no mximo 20 letras em uma palavra). O algoritmo de Eratstenes consiste no seguinte: 1. Criar lista

com todos os inteiros de 2 a

m.

2. Atribuir, inicialmente, 2 a

p. l
a partir de

3. Repetir o seguinte, at que no exista valor no removido em

p:

remover de

todos os mltiplos de

p; p,
em l, ainda no removido.

em seguida, fazer

igual ao nmero seguinte a

4. Retornar a lista dos nmeros no removidos. O programa cria um arranjo contendo todos os primos de 3 at dado nmero

e, para vericar se um

primo, percorre esse arranjo usando uma pesquisa sequencial (de componente a

componente), at encontrar um nmero primo igual ou maior a chamado de pesquisa binria , usado a seguir.

n ou at chegar ao nal do arranjo.

Essa pesquisa sequencial em um arranjo ordenado ineciente. Um algoritmo mais eciente,

struct ListaETamanho { int* lista ; int tam ; }; typedef struct ListaETamanho


ListaETamanho listaDePrimos

ListaETamanho ;

} int primo (int n, ListaETamanho primos ) { int linf =0, lsup =primos.tam -1, meio = (linf+lsup)/2; while (linf <lsup -1) { if ((primos.lista )[meio ] > n) { lsup = meio ; meio = (linf +lsup )/2;} else if ((primos.lista )[meio ] < n) { linf = meio ; meio = (linf +lsup )/2;} else return 1; } return ((primos.lista )[linf ]== n || (primos.lista )[lsup ]==n); }
O algoritmo de pesquisa binria compara o elemento que est sendo procurado com o valor que est na metade do arranjo ordenado, permitindo assim que a busca prossiga ou na metade inferior ou na superior do arranjo, conforme o elemento a ser procurado seja menor ou maior,

(int m) { int p=2, m2 =m+2, nums [m2 ], *primos = malloc (m), i ,j ,k ; for (i=2,j =0; i<m2 ; i++,j ++) { nums [i]=0; primos [j ] = 0; } j = 0; do { for (i=p; i<m2 ; i+=p) nums [i] = 1; for (k=p+1; k<m2 ; k++) if (!nums [k ]) break; p = k ; // p o prximo primo primos [j ] = p; j ++; } while (p<m2 ); ListaETamanho l; l.lista = primos ; l.tam = j ; return l;

7.3 ENERGIA

101

respectivamente, do que o valor que est na metade do arranjo. Se o elemento a ser procurado igual ao elemento na metade, ento, claro, a busca termina com sucesso. So mostradas apenas as funes modicadas, que so listaDePrimos e primo . A funo lista-

DePrimos modicada apenas para retornar, alm do arranjo contendo os primos, o nmero de
primos de fato armazenado nesse arranjo. A funo primo percorre esse arranjo usando pesquisa binria.

7.3 ENERGIA 7.4 CIRCUITO 7.5 POLEPOS

Referncias Bibliogrcas

104

REFERNCIAS BIBLIOGRFICAS

Apndice A

Anexo: Por que a linguagem C


O livro adota a linguagem de programao

C.

A escolha dessa linguagem foi motivada pela

necessidade de homogeneizao no ensino de disciplinas introdutrias de programao de computadores nos cursos de vrias universidades (por exemplo, nas disciplinas de Algoritmos e Estruturas

de Dados I do Departamento de Cincia da Computao da Universidade Federal de Minas Gerais). O uso da linguagem

apresenta, reconhecidamente, vantagens no ensino de tais disciplinas

para cursos como os de Engenharia Eltrica e Engenharia de Controle e Automao, por se tratar de linguagem adequada chamada programao de sistemas , na qual se faz acesso direto a dispositivos e recursos de hardware. Para tais sistemas, a programao na linguagem a linguagem permite acesso direto aos dispositivos e recursos de hardware, e portanto bibliotecas e programas que fazem tais acessos diretos ao hardware podem ser mais facilmente encontrados e usados. Para outros cursos, o uso da linguagem

C adequada pois

controverso, pelo fato de existir na linguagem

uma preocupao central com ecincia, e possibilidade de acesso direto a reas de memria, o que leva, principalmente, a duas consequncias indesejveis do ponto de vista de um aprendizado em programao: 1. Ausncia de vericao dos chamados erros de tipo. Tais erros ocorrem devido ao uso de

valores em contextos inadequados, ou seja, em contextos nos quais no faz sentido usar tais valores, e portanto nos quais tais valores no deveriam ser usados. Grande parte do desenvolvimento das linguagens de programao nos dias atuais relacionado ao objetivo de tornar as linguagens cada vez mais seguras, no sentido de possibilitar a deteo de um nmero cada vez maior de erros de tipo, e ao mesmo tempo dando exibilidade ao programador, de modo que ele no tenha que especicar ou mesmo se preocupar com a especicao de tipos em seus programas, e procurando manter o algoritmo que permite essa deteo de erros simples e eciente. NO ENTENDI O SEGUINTE ITEM (ANOLAN) 2. Mecanismos adequados na linguagem para suporte a abstraes, conceitos e construes comumente usados em programas. Exemplos de tais mecanismos so:

Tipos algbricos: tipos que permitem representar disjuno (ou ) entre tipos e estruturas de dados, de modo seguro e simples. Polimorsmo: tanto de valores (estruturas de dados) que podem ser instanciados para quaisquer tipos, mantendo a mesma forma, quanto de funes que realizam operaes sobre tais estruturas de dados, que funcionam do mesmo modo, independentemente da instncia sobre a qual realizam a operao.

(Leitores interessados em tais assuntos podem consultar as notas bibliogrcas do Captulo 1.6.) Em concluso, existe um compromisso entre a inteno de formar durante o tempo da graduao um programador pronto para as necessidades da indstria e o objetivo didtico promordial que consiste em oferecer uma formao slida aos graduandos nos aspectos tericos relacionados com a programao de computadores. Pelo primeiro motio, este livro foi escrito na linguagem C.

106

Anexo: Por que a linguagem C

Pelo segundo, se oferecem sees especiais que abordam temas mais complexos, destinados aos estudantes que procurem maior aprofundamento nos contedos lecionados.

Apndice B

Tipos bsicos em C
B.1 Nmeros
A rea de memria reservada para armazenar um dado valor em um computador tem um tamanho xo, por questes de custo e ecincia. Um valor de tipo um nmero de ponto utuante  em

int em C usualmente armazenado,


ou

nos computadores atuais, em uma poro de memria com tamanho de 32 ou 64 bits, assim como

C,

um valor de tipo

float

double.

Tambm por questes de ecincia (isto , para minimizar tempo ou espao consumidos), existem qualicadores que podem ser usados para aumentar ou restringir os conjuntos de valores numricos inteiros e de nmeros de ponto utuante que podem ser armazenados ou representados por um tipo numrico em O tipo

C. Os qualicadores podem ser: short e long. char, usado para representar caracteres, tambm considerado como tipo inteiro, nesse caso sem sinal. O qualicador unsigned tambm pode ser usado para tipos inteiros, indicando que
valores do tipo incluem apenas inteiros positivos ou zero. de um tipo numrico especco (char,

C no especica qual o tamanho do espao alocado para cada varivel short, int, long, float ou double), ou seja, a linguagem no especica qual o nmero de bits alocado. No caso de uso de um qualicador (short ou long), o nome int pode ser omitido.
A denio da linguagem Cada implementao da linguagem pode usar um tamanho que julgar apropriado. As nicas

condies impostas so as seguintes. Elas usam a funo

sizeof,

predenida em

C,

que retorna o

nmero de bytes de um nome de tipo, ou de uma expresso de um tipo qualquer:

sizeof(short) sizeof(int) sizeof(long) sizeof(short) 16 bits sizeof(int) 16 bits sizeof(long) 32 bits sizeof(long long int) 64 bits sizeof(float) sizeof(double) sizeof(long double)

int, 64 bits para long int e 16 short int. Um numeral inteiro do tipo long se ele tiver como suxo a letra L, ou l (mas a letra L deve ser preferida, pois a letra l se parece muito com o algarismo 1). Do contrrio, o numeral do tipo int. No existem numerais do tipo short. Entretanto, um numeral do tipo int pode ser armazenado em uma varivel do tipo short, ocorrendo, nesse caso, uma converso implcita de int para short. Por exemplo, no comando de atribuio:
Implementaes usam comumente 32 bits para variveis de tipo bits para variveis de tipo

short short.
o valor

= 10; short
e armazenado na varivel s , de tipo

10,

de tipo

int,

convertido para o tipo

Essa converso obtm apenas os

bits mais direita (menos signicativos) do valor a ser

convertido, onde

o nmero de bits usados para a representao de valores do tipo para o qual

feita a converso, sendo descartados os bits mais esquerda (mais signicativos) restantes.

108

Tipos bsicos em C

Nmeros inteiros podem tambm ser representados nos sistemas de numerao hexadecimal e octal. No primeiro caso, o numeral deve ser precedido dos caracteres pelas letras de com algarismos hexadecimais: os nmeros de 0 a 15 so representados pelos algarismos

at

f,

ou

at

F,

respectivamente. Um numeral

(seguido de um ou mais algarismos, de

7).

0x ou OX, sendo representado 0a9e octal iniciado com o dgito 0

Nmeros de ponto utuante so nmeros representados com uma parte inteira (mantissa) e outra parte fracionria, como, por exemplo:

2.0
expoente na base

3.1415

1.5e-3

7.16e1 e,
ou

Um ponto decimal usado para separar a parte inteira (mantissa) da parte fracionria. um inteiro, opcionalmente precedido de um sinal (+ ou Um suxo,

10

pode (ou no) ser especicado, sendo indicado pela letra

tambm um expoente de 10, representam, respectivamente,

1.5 103 e 7.16 101 . 1.43f, indica um valor do tipo float, e a ausncia especicao de um suxo d ou D, indica um valor do tipo double. Exemplos de numerais de tipo float:

-).

E,

Um

seguida de

Os dois ltimos exemplos, que contm do suxo, ou

ou

F,

como em

2e2f

4.f double: 4.

.5f

0f

2.71828e+4f

Exempos de numerais de tipo

2e2

.5

0.0

1e-9d

B.1.1 Consequncias de uma representao nita


Como nmeros so representados em um computador com um nmero xo de bits, a faixa de valores representveis de cada tipo numrico limitada. O uso de uma operao que retorna um valor positivo maior do que o maior inteiro representvel em uma varivel de tipo em geral, um erro. Em insuciente para armazenamento.

int

constitui,

Esse tipo de erro chamado, em computao, de overow , ou seja, espao

C,

um erro de overow no detectado, e o programa continua a sua execuo: como o

valor que causou a ocorrncia de overow no pode ser representado no espao reservado para que ele seja armazenado, o programa usa ento um outro valor (incorreto). Quando ocorre overow , por exemplo, na adio de dois nmeros inteiros positivos, o resultado um nmero negativo. O inverso tambm verdadeiro: quando ocorre overow na adio de dois nmeros inteiros negativos, o resultado um nmero positivo.

B.2 Caracteres
Valores do tipo

char,

ou caracteres, so usados para representar smbolos (caracteres visveis),

tais como letras, algarismos etc., e caracteres de controle, usados para indicar m de arquivo, mudana de linha, tabulao etc. Cada caractere representado, em um computador, por um determinado valor (binrio). O cdigo usado para representao de caracteres em cada caractere. Caracteres visveis so escritos em A associao entre esses valores e os caracteres correspondentes constitui o que se chama de cdigo .

o chamado cdigo ASCII (American

Standard Code for Information Interchange). O cdigo ASCII baseado no uso de 8 bits para

etc. preciso notar que, por exemplo, o caractere '3' diferente do numeral inteiro representa um smbolo, enquanto o segundo representa um nmero inteiro. Caracteres de controle e os caracteres caracteres. Por exemplo:

C entre aspas simples. '


e

Por exemplo:

'a', '3', '*', ' ', '%' 3. O primeiro

so escritos usando uma sequncia especial de

'\n' '\t' '\ '\\'

indica terminao de linha indica tabulao indica o caractere indica o caractere

' \

B.2 Caracteres

109

Um valor do tipo

char

pode ser usado em

como um valor inteiro. Nesse caso, ocorre uma

converso de tipo implcita, tal como no caso de converses entre dois valores inteiros de tipos diferentes (como, por exemplo, Valores de tipo do tipo

short

int).

O valor inteiro de um determinado caractere igual

ao seu cdigo ASCII (isto , ao valor associado a esse caractere no cdigo ASCII).

short,

char

incluem apenas valores no-negativos. O tipo

char

, portanto, diferente

que inclui valores positivos e negativos. Converses entre esses tipos, e converses

de tipo em geral, so abordadas na Seo 2.2. As seguintes funes ilustram o uso de caracteres em

C:

int minusc (char x ) { return (x >= 'a') && (x <= 'z'); } int maiusc (char x ) { return (x >= 'A') && (x <= 'Z'); } int digito (char x ) { return (x >= '0') && (x <= '9'); }
Essas funes determinam, respectivamante, se o caractere passado como argumento uma letra minscula, uma letra maiscula, ou um algarismo decimal (ou dgito). A funo a seguir ilustra o uso de caracteres como inteiros, usada na transformao de letras minsculas em maisculas, usando o fato de que letras minsculas e maisculas consecutivas tm (assim como dgitos) valores consecutivos no cdigo ASCII:

char minusc_maiusc (char x ) { int d = 'A' - 'a'; if (minusc (x )) return (x +d ); else return x; }
Note que o comportamento dessa funo no depende dos valores usados para representao de nenhuma letra, mas apenas do fato de que letras consecutivas tm valores consecutivos no cdigo ASCII usado para sua representao. Por exemplo, a avaliao de:

minusc_maiusc
tem como resultado o valor dado por

('b')
que igual a

'b' + ('A' - 'a'),

'A' + 1,

ou seja,

'B'.

Como mencionado na seo B.2, caracteres podem ser expressos tambm por meio do valor da sua representao no cdigo ASCII. Por exemplo, o caractere chamado nulo , que representado com o valor

0,

pode ser denotado por:

'\x0000'
Nessa notao, o valor associado ao caractere no cdigo ASCII (cdigo ASCII do caractere) escrito na base hexadecimal (usando os algarismos hexadecimais valores de 0 a 15, representveis com 4 bits), precedido pela letra

F,

que correspondem aos

minscula.

110

Tipos bsicos em C

Apndice C

Programas e Bibliotecas
Um programa en

C consiste em uma sequncia de uma ou mais denies de funes, sendo que C.

uma dessas funes, de nome main , a funo que inicia a execuo do programa. A denio de uma funo de nome main deve sempre estar presente, para que uma sequncia de denies de funes forme um programa A assinatura  ou interface  de uma funo uma denio de uma funo que omite o corpo (sequncia de comandos que executada quando a funo chamada) da funo, mas especica o nome, o tipo de cada argumento e do resultado da funo. Em uma assinatura, os nomes dos argumentos so opcionais (mas os tipos so necessrios). A assinatura da funo main de um programa

deve ser:

int
ou

main (void)

int
tado

main (int argc,

char*

argv [])

A primeira assinatura especica que a funo main no tem parmetros e o tipo do resul-

int.

Esse valor usado para especicar, para o sistema operacional, que a funo foi

executada normalmente, sem causar nenhum erro (nesse caso, o valor zero retornado), ou para especicar que a execuo da funo provocou a ocorrncia de algum erro (nesse caso, um valor diferente de zero retornado). Como o sistema operacional pode usar uma conveno diferente para indicar a presena ou ausncia de erro, boa prtica usar as constantes EXIT_SUCCESS e

EXIT_FAILURE , denidos na biblioteca stdlib , para indicar respectivamente sucesso e falha da


execuo. A linguagem o caractere

permite o uso de funes de bibliotecas, que requerem o uso de diretivas de

preprocessamento. Como mencionado na seo ??, Uma diretiva de preprocessamento comea com

#,

e deve ser inserida na primeira coluna de uma linha. Uma diretiva de preproces-

samento deve comear sempre na primeira coluna de uma linha. Aps o caractere do arquivo a ser lido, o qual deve ter uma extenso arquivo de uma biblioteca padro. sendo A

.h,

entre os caracteres

Para arquivos com extenso

.h

<

>,

vem o nome

no caso de um

denido pelo programador,

o nome do arquivo deve ser inserido entre aspas duplas (como por exemplo em

interface.h o nome do arquivo). diretiva #include <stdio.h > a diretiva

"interface.h", C.

mais comumente usada em programas

Existem muitas funes denidas em bibliotecas da linguagem

C, e a questo,

bastante comum

para iniciantes no aprendizado de programao em uma determinada linguagem, de quando existe ou no uma funo denida em uma biblioteca, s pode ser respondida em geral por experincia ou pesquisa em textos sobre a linguagem e as bibliotecas especcas. As bibliotecas mais comuns usadas em programas

so as seguintes (so includas a assinatura e uma descrio sucinta de

algumas funes de cada biblioteca):

stdio : contm funes para entrada e sada em dispositivos padro, como printf e scanf
(descritas na seo

??).

string : contm funes para manipulao de strings (seo 6.4).

112

Programas e Bibliotecas

ctype.h : contm funes sobre valores de tipo

char,

como:

    

int
(de

'0'

isdigit (char): retorna verdadeiro (diferente de zero) se o argumento um dgito


a

'9'),

int int int int

isalpha (char): retorna verdadeiro se o argumento uma letra, isalnum (char): retorna verdadeiro se o argumento uma letra ou um dgito, islower (char): retorna verdadeiro se o argumento uma letra minscula, isupper (char): retorna verdadeiro se o argumento uma letra maiscula

math : contm funes matemticas, como:

     

double double double double double double

cos (double): retorna o cosseno do argumento, sen (double): retorna o seno do argumento, exp (double): retorna

elevado ao argumento, onde

a base do logartmo

natural (constante de Euler),

fabs (double): retorna o valor absoluto do argumento, sqrt (double): retorna a raiz quadrada do argumento, pow (double

a, double b):

retorna

ab (a

elevado a

b).

stdlib : contm funes diversas, para diversos ns, como:

    

int long void

abs (int): retorna o valor absoluto do argumento, labs (long): retorna o valor absoluto do argumento, srand (unsigned

int):

especica a semente do gerador de nmeros pseudo-aleatrios

rand ,

int int

rand (): retorna um nmero pseudo-aleatrio entre 0 e RAND_MAX, RAND_MAX : valor maior ou igual a 32767, usado pelo gerador de nmeros

pseudo-aleatrios rand ,

 atoi, atol, atof convertem, respectivamente, string para valor de tipo

double int int

int, long int

(veja seo 6.4),

 malloc aloca memria dinamicamente (seo 6.3).  


EXIT_SUCCESS : valor usado para indicar que a execuo de um programa ocorreu com sucesso,

EXIT_FAILURE : valor usado para indicar que ocorreu alguma falha na execuo

de um programa. Na seo

?? mostraremos como um programa em

pode ser dividido em vrias unidades de

compilao, cada uma armazenada em um arquivo, e como nomes denidos em uma unidade de compilao so usados em outra unidade.

ndice Remissivo
= (atribuio), 19 != (operador de desigualdade), 16 < (operador menor), 16 <= (operador menor que), 16 == (operador de igualdade), 15, 16, 19 > (operador maior), 16 >= (operador maior que), 16 ! (operador lgico no), 16 & (operador bit-a-bit ), 17 & (operador lgico ), 16, 28 && (operador lgico ), 16, 28 (operador bit-a-bit ou exclusivo), 17 | (operador bit-a-bit ou), 17 | (operador lgico ou), 16, 28 || (operador lgico ou), 17, 28
EXIT_FAILURE, 111 EXIT_SUCCESS, 111 itoa, 81 sprintf, 81
algoritmo, 1 ecincia de, 39 Alocao dinmica de memria, 70, 71, 73 ambiente de programao, 5 rea de memria dinmica, 70 arranjo, 6975 ndice, 69 criao de, 71 declarao de, 70 indexao, 69 multidimensional, 73 tamanho, 69, 71 ndice fora dos limites de arranjo, 69 ASCII, 108 atribuio, veja comando de atribuio somador completo, 8, 9 somador paralelo, 8, 9 cdigo fonte, 5 objeto, 5 comando, 13

break,

5152, 56

composio seqencial de, 13,

continue,

26

44, 56

de atribuio, 13, 19 varivel alvo, 19 de escrita, 19 de leitura, 19 de repetio, 13, 3537

do-while, 44 for, 3738, 44, 72 while, 35, 44, 49 if, 33 switch,


33
5152

de seleo, 13,

iterativo, veja comando de repetio rtulo de, 52

return,

27, 38

comentrio, 25 compilao, 5 compilador, 5 complemento de dois, 9 computador funcionamento e organizao, 23 conectivo lgico, 7

const, 80 continue (comando),


Converso 80

44, 56

de cadeia de caracteres para valor numrico, para cadeia de caracteres, 81

bit, 2

boolean, 19 break (comando),


byte , 2 bytecodes , 5

cosseno (clculo aproximado de), 65 CPU, veja processador 5152, 56 Crivo de Eratstenes, 99 Declarao

caractere, 108 caractere nulo ('\0'), 79 Unicode, 108

de constante (varivel com atributo 80 declarao de varivel, denio recursiva, 37

const),

char, 107,

108

19

circuito somador, 89 meio-somador, 8

114

NDICE REMISSIVO

depurador, 6 dispositivo de entrada, 3 dispositico de entrada padro, 26 dispositivo de sada, 3 dispositico de sada padro, 26 divisao diviso inteira, 15 de repetio), 44

JVM (Java Virtual Machine), 5 Lgica Booleana, 7 conectivos, 7 Lgica Proposicional, veja Lgica Booleana linguagem Ada, 22 C, 22 C++, 22 de alto nvel, 5 de baixo nvel, 5 de mquina, 3 de montagem, 4 de programao em lgica, 22, 23 Eiel, 22 fonte, 5 funcional, 22, 23 Haskell, 22, 23 imperativa, 14, Java, 22 ML, 22, 23 Modula-2, 22 Modula-3, 22 objeto, 5 orientada por objetos, 22 Pascal, 22 Prolog, 22, 23 Smalltalk, 22 107

do-while (comando double, 107108


editor, 6

efeito colateral, 18, 19, 72 ecincia de algoritmo, veja algoritmo, ecincia ENCOTEL, 95 endereo de memria, 2 Eratstenes, 99 estado, 38, 46, 72 de uma computao, 38, 46, 72 estilo, 23 estruturas de dados, 69 heterogneas, 69 homogneas, 69 Exemplos:

14

Exemplo_continue, 56 ExemplosSelecaoMax3, 34 ExemplosSelecaoMax3, 34 Fibonacci, 55 PrimeirosExemplos, 27 Torres de Hani, 52


exponenciao, 39

long,

ex

(clculo aproximado de), 44

malloc, 70, 71, 73


Mquina Virtual Java, veja JVM matriz, veja arranjo multidimensional mximo divisor comum, veja mdc mdc (algoritmo de Euclides), 50 memria, 2 mtodo chamada de, 28 montador, 4 nmero de ponto utuante, 107108

expresso, 17 com efeito colateral, 18, 19 condicional, 29 ordem de avaliao de, 18 valor de, 18

false,

19

fatorial, 41 m de arquivo, 56

float, 107108 for (comando de


funo, 26 recursiva, 36 funo

repetio), 3738, 44, 72

nmeros de Fibonacci, 54 no-terminao, 45 notao arbica, 6 complemento de dois, 9 hexadecimal, 107 octal, 107 sinal-magnitude, 9 operao booleana, veja operao lgica operao lgica, 7 operador aritmtico, 1518 de igualdade (==), 15, 19 lgico, 16 precedncia de, 18 de comparao, veja operador relacional

chamada de, 37 chamada recursiva, 3839

heap (rea de memria dinmica), 70

if (comando int, 19, 107

de seleo),

33

Internet, 5, 23 interpretao, 5 interpretador, 5 iterao, veja comando iterativo Java, 22

NDICE REMISSIVO

115

relacional, 15, 16 ordem de avaliao de expresses, 18

tipo

overow , 108
palavra, 2 PAPRIMAS, 96 paradigma declarativo, 14 funcional, 22 imperativo, 14, 37 lgico, 22, 23 orientado por objetos, 22

boolean, 19 char, 108


converso implcita de, 107

double,

107108

erro de, 20 esttico, 20

float, 107108 int, 19, 107 long, 107 short, 107


Torres de Hani, 52 tringulo de Pascal, 66

(clculo aproximado de), 44

pilha, 38 polimorsmo, 23 de sobrecarga, veja sobrecarga porta lgica, 7 Prima Palavra, 96 Primalidade, 96 Primo Nmero, 96 procedimento, 26 processador, 2 programa, 1 fonte, 5 objeto, 5 raiz quadrada (clculo aproximado de), 52 RAM, 2 recurso, veja funo recursiva registro, 87 registro de ativao, 3839

true,

19

tupla, 87 Unicode,

unsigned, 107
varivel,

108

14 19

declarao de, global, 39 local, 38 tipo de, 19 von Neumann

arquitetura de, 2

while

(comando de repetio),

35, 44, 49

return

(comando),

27, 38

srie aritmtica, 42 srie geomtrica, 43 srie harmnica, 43 seno (clculo aproximado de), 65

short,

107

sistema de numerao, 6 binrio, 6 converso de base, 6 decimal, 6 sistema operacional, 6

sizeof, 107
SPOJ, 95

somatrio, 42

stdio, 26 strcat, 80 strcmp, 80 strcpy, 80 strlen, 80

switch

(comando de seleo), 5152

terminao de cadeias de caracteres, 79 teste de m de arquivo usando scanf, 56

Potrebbero piacerti anche