Você está na página 1de 221

Conceitos e

Exerccios de
Programao
Utilizando Linguagem C

Este texto destina-se a todos quanto se queiram iniciar na programao, desde as mais modestas s mais
elevadas ambies. O mtodo de aprendizagem consiste em explicar um conjunto de conceitos, suportados em
exemplos simples, aps o qual o leitor ter ao seu dispor um leque de exerccios desde simples a mais avanados,
que lhe permitam interiorizar os conceitos com base na sua experincia de programao.

Jos Coelho
2010

PREFCIO
O presente texto destina-se a todos quantos queiram iniciar-se na programao. A linguagem de
programao adoptada a linguagem C, mas este texto no se centra na linguagem de programao
mas sim nos conceitos, sendo a linguagem de programao apenas uma ferramenta para o ensino
da programao.
Este texto tem origem num conjunto de explicaes fornecidas no mbito da Unidade Curricular
(UC) de Programao da Universidade Aberta, de forma a responder s dificuldades sentidas pelos
estudantes. As explicaes foram organizadas em conceitos essenciais, que so introduzidos com
uma necessidade que os conceitos introduzidos anteriormente no satisfazem, e constitudos por
uma explicao do conceito, seguida de um ou mais exemplos. Os exemplos so to simples quanto
possvel, de forma a ser clara a aplicao do conceito. A compreenso completa dos exemplos passa
por analisar a execuo passo a passo de forma a remover qualquer dvida que o leitor tenha sobre
a forma como executado o exemplo que exemplifica o conceito em causa, e desta forma facilmente
interiorizar os conceitos essenciais. Cada conceito termina com um conjunto de erros comuns mais
directamente ligados ao conceito, e respectivos exemplos, para que ao serem compreendidos no
sejam cometidos. No anexo est a lista completa de erros comuns.
A estes conceitos juntou-se o pacote de actividades formativas a realizar na UC, aqui chamados de
exerccios, de nvel de dificuldade desde o mais simples (exerccios verdes), ao mais desafiante
(exerccios vermelhos), passando pela dificuldade mdia (exerccios azuis). Foi tido o maior
cuidado na escolha dos exerccios, de forma a pedir apenas exerccios no abstractos, que possam
ser reutilizados, e inspirados em matrias mais avanadas a abordar em outras UCs, para assim
tirar o maior partido possvel do trabalho do leitor. Aos enunciados dos exerccios foi dada a maior
ateno, tendo sido j utilizados e revistos com o decorrer da UC, para que o leitor no sinta que a
principal dificuldade compreender o enunciado do exerccio mas sim na sua realizao. No anexo
encontram-se mais dicas sobre os exerccios, uma forma de verificar se o exerccio est correcto
sem ver uma resoluo, e as resolues dos exerccios para comparao, aps resolv-los.
Os conceitos foram organizados primeiramente em pginas isoladas, e posteriormente ordenadas
por ordem de importncia, o mais importante primeiro. Os conceitos passaram a captulos neste
texto, que esto agrupados em trs partes, a que correspondem os mdulos na UC, e em que so
pontos apropriados para uma paragem mais longa no estudo, ou para a realizao de actividades de
avaliao. Os exerccios esto associados s partes, e no aos captulos.
A metodologia de estudo proposta assenta no princpio que programar aprende-se programando,
pelo que no se apresentam captulos com muitos exerccios resolvidos, mas apenas os conceitos
essenciais para que o leitor possa resolver por si os exerccios e adquirir a experincia de
programao que apenas sua e intransmissvel. Se o leitor tiver aspiraes modestas e disponha
de pouco tempo, deve no final de cada parte fazer apenas os exerccios verdes. Caso no consiga
realizar algum exerccio, deve reler os captulos anteriores. Aconselha-se a planear um estudo
regular, cada sesso de estudo com 1 a 2 horas, e uma periodicidade entre 1 dia a 1 semana. No
deve ler mais que um captulo na mesma sesso de estudo, e deve fazer os exerccios em sesses
distintas da leitura dos captulos. Caso o leitor tenha aspiraes mais elevadas e disponha de mais
tempo, deve fazer tudo o que atrs se aconselhou, mais a realizao dos exerccios azuis e
vermelhos.
Se o leitor tiver oportunidade de estudar em grupo, aconselha-se a que aplique em estudo isolado a
metodologia atrs referida, e partilhe ideias e exerccios aps os resolver, comentando as opes

tomadas. O estudo deve estar sincronizado para poder haver alguma vantagem na troca de ideias
aps a leitura de cada captulo e anlise dos exerccios realizados. No deve no entanto cair no erro
de aguardar pela resoluo dos exerccios, ou pelas explicaes do colega sobre uma parte do texto.
Ningum pode aprender por outra pessoa. Deve tirar partido de haver outras pessoas a estudar o
mesmo assunto, e no prescindir de aprender.
A escolha da linguagem de programao C deve-se a essencialmente dois motivos. Primeiro trata-se
de uma linguagem estruturada imperativa, que se considera ideal para aprender a programar.
Numa linguagem orientada por objectos tem que se de utilizar muitas caixas pretas, e dificilmente
se consegue explicar alguns conceitos que apenas fazem sentido em programas de maior dimenso,
algo que no deve ser prioridade de quem se inicia na programao. As linguagens funcionais e
declarativas, atendendo a que pertencem a paradigmas computacionais alternativos, embora
possveis na iniciao programao, no se consideram apropriadas uma vez que os ganhos
seriam muito limitados devido s suas fracas utilizaes reais. Segundo, a linguagem C uma
linguagem de alto nvel, bastante antiga, amplamente divulgada e em utilizao, implementada e
disponvel em praticamente todas as plataformas. Com a sua simplicidade permite compiladores
que tirem o melhor partido da mquina, resultando normalmente em binrios mais eficientes que
em outras linguagens. Todos os conceitos aqui introduzidos sero teis no s em outras
linguagens estruturadas, como na programao orientada por objectos.

NDICE
PARTE I VARIVEIS E ESTRUTURAS DE CONTROLO .............................................................. 6
1.

Primeiro Programa..................................................................................................................................... 7

2.

Variveis.................................................................................................................................................... 9

3.

Condicinais ...............................................................................................................................................16

4.

Ciclos ........................................................................................................................................................23

1)

Exerccios .................................................................................................................................................29

PARTE II FUNES, VECTORES E RECURSO .......................................................................... 39


5.

Funes ....................................................................................................................................................40

6.

Mais Ciclos e Condicionais.........................................................................................................................49

7.

Vectores ...................................................................................................................................................57

8.

Procedimentos .........................................................................................................................................69

9.

Recurso...................................................................................................................................................78

2)

Exerccios .................................................................................................................................................83

PARTE III MEMRIA, ESTRUTURAS E FICHEIROS ................................................................. 99


10.

Memria ............................................................................................................................................ 100

11.

Estruturas ........................................................................................................................................... 113

12.

Ficheiros ............................................................................................................................................. 130

13.

Truques .............................................................................................................................................. 143

3)

Exerccios ............................................................................................................................................... 151

PARTE IV ANEXOS ......................................................................................................................... 160


14.

Compilador e Editor de C .................................................................................................................... 161

15.

No consigo perceber o resultado do programa .................................................................................. 164

16.

Funes standard mais utilizadas ........................................................................................................ 169

17.

Erros Comuns ..................................................................................................................................... 170

18.

Exerccios: Dicas, Respostas e Resolues............................................................................................ 176

PARTE I VARIVEIS E ESTRUTURAS


DE CONTROLO
Na Parte I so introduzidos os primeiros conceitos de programao, existentes em todas as
linguagens de programao modernas. A leitura atenta e pausada essencial para a compreenso
destes conceitos que embora aparentemente simples, a sua no compreenso tem consequncias
em todo o resto do texto, mas que poder detectar de imediato no final da Parte I ao no conseguir
resolver alguns dos exerccios propostos. Aps a realizao desta parte, fica com as bases para
programar em qualquer linguagem de programao simples, para implementao de pequenas
funcionalidades, como Javascript para adicionar dinamismo a uma pgina Web, VBA para
programar macros no Microsoft Excel, ou Linguagem R para realizar clculos estatsticos.

1. Primeiro Programa

1. PRIMEIRO PROGRAMA
Um programa uma sequncia de instrues, dirigidas a um computador, para que este execute
uma determinada funo pretendida.
A actividade de programar consiste na elaborao dessa sequncia de instrues numa
determinada linguagem de programao, que satisfaz o que pretendido.
A linguagem de programao tem que seguir uma sintaxe rgida, de forma a poder ser convertida
em instrues que o computador possa compreender, mas por outro lado deve ser facilmente
escrita e lida por uma pessoa.
H diferentes linguagens de programao, utilizaremos a linguagem C, no entanto no ser a
sintaxe da linguagem o mais relevante a aprender, mas sim competncias que so independentes da
linguagem.
A linguagem C uma linguagem imperativa, cada instruo uma ordem para o computador.
Existem outros paradigmas de programao, mas fora do mbito deste texto.
Manda a tradio que o primeiro programa a desenvolver numa linguagem, seja o ol mundo. No
a vamos quebrar, este o programa mais curto possvel, apenas escreve um texto e termina.
1
2
3
4
5
6

#include <stdio.h>
int main()
{
printf("Ola Mundo!");
}
Programa 1-1 Ol Mundo

O programa acima est escrito em linguagem C, e pode ser executado pelo computador. No entanto
o computador no entende directamente a linguagem C, apenas ficheiros executveis (no
Windows com extenso exe). No entanto, atravs de um programa chamado de Compilador,
podemos converter o cdigo C acima num executvel. Esse executvel contm j instrues em
cdigo mquina, cujo computador pode executar.
Vamos ver em que consiste o cdigo acima:

Existe numerao das linhas e cores, que so apenas para nossa convenincia. A
numerao das linhas no faz parte do ficheiro C, e as cores so colocadas pelo editor. Um
programa em C um ficheiro de texto plano, com a extenso .c e pode ser editado num
editor de texto plano como o Notepad, sem qualquer cor ou numerao das linhas.
Linha 1: comea por um # (cardinal), pelo que esta linha uma instruo no para o
computador mas sim para o compilador. Essas instrues chamam-se de comandos de prprocessamento, dado que so executadas antes do processamento.
O comando #include instrui o compilador a incluir o ficheiro indicado de seguida. Esse
ficheiro uma biblioteca standard do C (existe em todos os compiladores de C), stdio.h, e
consiste na declarao de funes de entrada e sada. Estas funes podem ser utilizadas de
seguida no programa, sendo utilizada neste caso a funo printf.
Linha 2: esta linha no tem nada. Podem existir quantas linhas em branco o programador
quiser, o compilador no ficar atrapalhado, nem far nada com elas, mas tal pode ser til

Parte I Variveis e Estruturas de Controlo

para espaar os comandos e tornar mais legvel o programa. O mesmo vlido para os
espaos ou tabs numa linha de cdigo, como por exemplo os espaos da linha 5 que
antecedem o printf.
Linha 3: comea a implementao da funo main. O programa comea sempre na funo
main. Para j, vamos deixar por explicar o int, e os parnteses vazios. Realar que as
instrues que dizem respeito funo main, e portanto ao que o programa tem de fazer,
esto limitadas por duas chavetas a abrir na linha 4 e a fechar na linha 6. Todas as
instrues entre estas chavetas, pertencem funo main.
Linha 5: nesta linha est a nica instruo. Existe uma chamada funo printf, e
sabemos que uma funo porque aps o seu nome tem uns parnteses a abrir. Dentro dos
parnteses colocam-se os argumentos a passar funo. Neste caso colocamos o texto que
queremos imprimir: "Ola mundo!". O texto coloca-se entre aspas, para no se confundir com
instrues.

Se no o fez j, crie e execute o programa acima. Veja as instrues para instalar um compilador no
anexo Compilador e Editor de C.
tudo. Acabou de dar o primeiro passo no mundo da programao, editou e compilou o primeiro
programa. Houve conceitos referidos aqui que no compreende agora completamente: biblioteca;
funo; argumentos. Para j no lhes d muito importncia.

2. Variveis

2. VARIVEIS
Uma varivel num programa uma entidade que tem um valor a cada instante, podendo esse valor
ao longo do programa ser utilizado e/ou alterado.
nas variveis que se guarda a informao necessria para realizar a funo pretendida pelo
programa. Podem existir quantas variveis forem necessrias, mas cada uma utiliza memria do
computador.
O programa seguinte tem por objectivo trocar o valor de duas variveis:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#include <stdio.h>
int main()
{
/* declarao de trs variveis inteiras */
int x=3;
int y=7;
int aux;
/* trocar o valor de x com y */
aux=x;
x=y;
y=aux;
/* mostrar os valores em x e em y */
printf("x: %d, y: %d",x,y);
}
Programa 2-1 Troca o valor de duas variveis

Comentrios:

Este programa tem uma estrutura inicial idntica ao Programa 1-1, nomeadamente nas
linhas 1 a 4, e linha 17.
A linha 5, 10 e 15 tm comentrios ao cdigo. Os comentrios so todo o texto entre /* e
*/, sendo ignorados pelo compilador. O objectivo que seja possvel ao programador
colocar texto explicativo de partes mais complexas do programa, no para que o compilador
o melhor entenda, mas para que um outro programador que leia o cdigo, ou o prprio
programador mais tarde, compreenda mais facilmente o cdigo. Os comentrios ficam a cor
verde, dado que o editor identifica que o contedo dessas linhas ser ignorado pelo
compilador1.
Nas linhas 6 a 8, esto declaradas as variveis x, y e aux. As variveis x e y so no s
criadas, como tambm inicializadas. Ao ficarem com um valor inicial, podem desde j ser
utilizadas. Na linguagem C, todas as variveis utilizadas tm de ser declaradas no incio da
funo2.
Nas linhas 11 a 13, esto comandos que so atribuies. Numa atribuio, coloca-se
uma varivel do lado esquerdo da igualdade, e uma expresso do lado direito. Neste
caso, todas as atribuies tm expresses com uma s varivel.

1 A linguagem C++ tem os comentrios at ao final da linha, aps duas barras: //. A maior parte dos
compiladores de C tambm permite este tipo de comentrios.
2 A linguagem C++ bem como a generalidade dos compiladores de C permitem a declarao fora do incio das
funes, mas pelos motivos referidos no erro Declarao de variveis fora do incio das funes, do anexo
Erros Comuns esta restrio ser mantida.

10

Parte I Variveis e Estruturas de Controlo

Na linha 16, mostrado o valor das variveis. Notar que a funo printf tem 3 argumentos
separados por vrgulas. O primeiro uma string, em que comea com aspas e acaba nas
aspas seguintes, o segundo uma expresso com a varivel x, seguido de uma expresso
com a varivel y. Na string existem dois %d. Em cada %d o printf em vez de mostrar no
ecr %d coloca o valor da varivel seguinte na sua lista de argumentos. No primeiro %d vai
colocar x, e no segundo %d vai colocar o y.
Notar que cada instruo acaba com um ponto e vrgula, sendo esta uma caracterstica do C.

Execuo do programa:
C:\>troca
x: 7, y: 3

Os valores esto correctamente trocados, mas vamos ver


com mais detalhe qual o valor das variveis a cada
momento, na execuo passo-a-passo, direita.
A execuo passo-a-passo tem em cada linha da tabela um
passo. Em cada passo indicada a linha de cdigo
correspondente, e a respectiva instruo por vezes
encurtada como no passo 7. A coluna resultado mostra o
resultado da instruo, excepto se tratar de uma declarao, em que uma ou mais variveis ocupam
a coluna seguinte disponvel, ou uma atribuio, em que a respectiva varivel fica com o valor
atribudo. Para poder-se escrever a linha de um determinado passo, suficiente a informao da
linha do passo anterior.
A pergunta "Qual o valor de x?" no tem muito sentido, a no ser que se identifique o instante. No
passo 3 ou no final do programa? Uma varivel tem um valor at que lhe seja atribudo outro.
Vamos agora resolver o mesmo problema, troca dos valores de duas variveis, mas sem utilizar
uma varivel auxiliar:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#include <stdio.h>
int main()
{
/* declarao de duas variveis inteiras */
int x=3, y=7;
/* trocar o valor de x com y */
x=x+y;
y=x-y;
x=x-y;
/* mostrar os valores em x e em y */
printf("x: %d, y: %d",x,y);
}
Programa 2-2 Troca o valor de duas variveis sem varivel auxiliar

A diferena para o exemplo anterior que no se


declarou uma varivel aux, e declarou-se ambas as
variveis x e y num s comando. As atribuies
utilizam agora expresses com duas variveis,
incluindo a prpria varivel que est a ser atribuda.

2. Variveis

11

Embora este programa parea confuso, o certo que tem a mesma funcionalidade que o anterior
como se comprova de seguida, vendo a execuo passo-a-passo. No passo 2, o valor de x utilizado
na expresso o que existia antes do passo 2, o valor 3, passando a ser o valor 10 aps a execuo
do passo 2. No h qualquer problema em colocar a varivel x de um lado e do outro de uma
atribuio. A varivel x esquerda, significa que ficar com o resultado da expresso, e a varivel x
direita, significa que se deve utilizar o valor da varivel x antes desta instruo.
Os tipos de dados utilizados podem no ser apenas variveis inteiras. Os tipos elementares da
linguagem C so os seguintes:

char - um carcter, ou um inteiro muito pequeno (guarda apenas 256 valores distintos)
short - inteiro pequeno (pouco utilizado)
int - tipo standard para inteiros
long - inteiro longo
float - nmero real (pode ter casas decimais)
double - nmero real com preciso dupla (igual ao float mas com mais preciso)

Pode-se nos tipos inteiros (char a long), anteceder com as palavras unsigned ou signed, para
indicar que o nmero inteiro tem ou no sinal. Se omitir, considerado que tm sinal. Pode-se
tambm considerar o tipo long long para um inteiro muito longo3, ou o long double, mas esses
tipos nem sempre esto implementados.
Quais os valores mximos/mnimos que podemos atribuir s variveis de cada tipo? Isso depende
do espao reservado para cada tipo de varivel, que por sua vez depende tanto do compilador,
como do computador em que o programa for compilado e executado. Para saber quanto espao
necessrio para guardar cada varivel, pode-se utilizar o operador sizeof. O resultado
de sizeof(int) retorna o nmero de bytes que uma varivel do tipo inteiro ocupa na memria.
1
2
3
4
5
6

#include <stdio.h>
int main()
{
printf("sizeof(int): %d",sizeof(int));
}
Programa 2-3 Imprime o nmero de bytes que um inteiro ocupa

Execuo do programa:
C:\>sizeofint
sizeof(int): 4

Execute este programa para ver quantos bytes ocupa um inteiro no seu compilador e computador.
Para que os resultados sejam idnticos na resoluo dos exerccios, importante que todos utilizem
inteiros do mesmo tamanho. Se o resultado no for no seu caso 4, experimente o sizeof(short)
ou sizeof(long), e utilize o tipo que tiver tamanho 4 para todas as variveis inteiras nos
exerccios das actividades formativas.
As variveis do tipo char e float/double, no utilizam a mesma string de formatao no printf
da que os inteiros. Para os inteiros, utiliza-se o %d na string do primeiro argumento, mas esse
3

O tipo long long no faz parte da norma inicial da linguagem C, actualmente a generalidade dos
compiladores implementa-o com um inteiro de 64 bits. A string de formatao varia: %I64d; %lld. Para uma
soluo mais portvel, ver inttypes.h e utilizar PRId64.

12

Parte I Variveis e Estruturas de Controlo

cdigo significa que se pretende colocar l um valor inteiro. Se pretendermos colocar um carcter
ou um valor real, tem que se utilizar o %c (carcter) ou %f (float), ou ainda %g (para mostrar o real
na notao cientfica). Note-se no entanto que o carcter tanto pode ser considerado como um
carcter ou como com um inteiro pequeno.
1
2
3
4
5
6
7
8
9
10
11

#include <stdio.h>
int main()
{
char c='a';
float x=12354234.2346;
double y=12354234.2346;
/* mostrar valores */
printf("c: %c (%d), x: %f, y: %g", c, c, x, y);
}
Programa 2-4 Imprime diferentes tipos de variveis

Comentrios:

O carcter c atribudo com a letra "a". Os caracteres so delimitados entre plicas, em


vez de aspas, para no se confundirem com outros identificadores.
O printf tem ordem para:
o No %c ir buscar a primeira varivel e colocar um carcter (a varivel c)
o No %d ir buscar a segunda varivel (c novamente), e colocar o seu valor numrico
o No %f ir buscar a terceira varivel (x), e coloca o nmero real com parte decimal
o No %g ir buscar a quarta varivel (y), e coloca o nmero real em notao cientfica

Execuo do programa:
C:\>charfloat
c: a (97), x: 12354234.000000, y: 1.23542e+007

Note que o valor numrico da letra 'a' o 97. Existe uma correspondncia entre as letras e os
nmeros pequenos, mas esta questo ser tratada mais frente.
O valor de x no o que est no programa. Isto acontece porque o tipo float tem uma preciso no
muito grande (ocupa 4 bytes). O tipo double tem uma preciso maior, mas em qualquer caso no
se pode contar com a preciso infinita do valor l colocado. Uma varivel real fica sempre com o
valor mais prximo que a representao interna do nmero permite, e no com o valor exacto.
A funo scanf a funo inversa ao printf, e serve para introduzir valores em variveis,
valores introduzidos pelo utilizador, em vez de os mostrar como no printf.
1 #include <stdio.h>
2
3 int main()
4 {
5
char c;
6
int x;
7
double d;
8
9
printf("Introduza um caracter: ");
10
scanf("%c", &c);
11
printf("Introduza um inteiro: ");
12
scanf("%d", &x);
13
printf("Introduza um real: ");
14
scanf("%lf", &d);
15

2. Variveis
16
17 }

13

printf("Valores introduzidos: %c %d %f",c,x,d);


Programa 2-5 Pede valores de variveis de diferentes tipos que depois imprime

No cdigo acima pode-se ver parecenas do scanf com o printf, mas h um & antes de cada
varivel. Porqu? Esta questo apenas pode ser respondida completamente na Parte III, por agora
explica-se apenas que se destina a poder fazer uma atribuio e no uma leitura s variveis.
Notar que foi utilizado para ler o valor para double, %lf, e no %f, dado que o real um double, e
no um float. Pela mesma razo que deve utilizar inteiros de 4 bytes, deve utilizar para os
nmeros reais o tipo double.
Execuo do programa:
C:\>scanf
Introduza um caracter: c
Introduza um inteiro: 123
Introduza um real: 142323.2435
Valores introduzidos: c 123 142323.243500

Na execuo do programa o texto introduzido pelo utilizador vem a bold e itlico. Como o
programa mostra o contedo das variveis que leu na ltima linha, est verificado que as variveis
foram lidas correctamente.
Consegue agora criar variveis dos tipos bsicos na linguagem C, ler e mostrar os valores das
variveis, efectuar atribuies e se necessrio fazer uma execuo detalhada e ver o valor das
variveis em cada instante.
No anexo est listado um conjunto de erros comuns para o ajudar a melhorar a qualidade do seu
cdigo. No entanto, muitos dos erros s os compreender aps o estudo da matria
correspondente, e outros apenas quando lhe for apontado no seu cdigo uma ocorrncia do erro.
No final de cada captulo so introduzidos os erros comuns, que esto mais directamente
relacionados com o captulo. Neste captulo temos quatro erros:
o

Declaraes ou atribuies a variveis nunca utilizadas


o Forma: Por vezes declaram-se variveis, ou fazem-se atribuies a variveis, um
pouco para ver se o programa passa a funcionar. No entanto, aps muito corte e
costura, por vezes ficam atribuies a variveis que na verdade nunca so utilizadas,
embora tal no afecte o bom funcionamento do programa.
o Problema: Se h uma varivel declarada que no utilizada, essa varivel pode ser
removida e o cdigo fica igual. Se h uma atribuio a uma varivel que depois no
utilizada em nenhuma expresso, ento a atribuio desnecessria. Ter variveis,
expresses, atribuies a mais, uma situao indesejvel, dado que compromete a
leitura do cdigo que realmente funciona.
o Resoluo: Verificar se h variveis declaradas que no so utilizadas, e apag-las.
Para cada atribuio, seguir o fluxo do cdigo at uma possvel utilizao. Se no
existir, apagar a atribuio, reinicializando o processo de verificao. No final o
cdigo real poder ser muito mais reduzido, do que o gerado em situaes de stress
em modo de tentativa/erro.
Uma atribuio, uma leitura

14

Parte I Variveis e Estruturas de Controlo


Forma: Uma varivel atribuda com um valor, para depois utiliz-lo na instruo
seguinte, no sendo mais necessrio o valor da varivel. Por exemplo, para reunir os
valores necessrios chamada de uma funo.
o Problema: Esta situao pode indicar a existncia de uma varivel desnecessria, e
quantas mais variveis desnecessrias o cdigo tiver, mais complexo fica, para no
falar que ocupam memria desnecessariamente. apenas justificvel no caso de a
expresso ser muito complexa, de forma a clarificar o cdigo.
o Resoluo: Ao fazer a atribuio do valor varivel, utiliza uma expresso. Essa
varivel depois utilizada de seguida tambm uma s vez, pelo que mais vale
colocar a prpria expresso no local onde a varivel utilizada.
Variveis desnecessrias
o Forma: Ao criar uma varivel por cada necessidade de registo de valores, pode
acontecer haver variveis sempre com o mesmo valor, mas tal no impede que o
programa funcione correctamente.
o Problema: Ter muitas variveis com a mesma funo tem o mesmo problema que
uma atribuio, uma leitura. Fica o cdigo mais complexo e difcil de ler sem
qualquer ganho. Pode acontecer que este problema se reflicta no apenas por ter
duas variveis sempre com os mesmos valores, mas terem valores com uma relao
simples (o simtrico, por exemplo).
o Resoluo: Deve identificar as variveis com atribuies e utilizaes perto uma da
outra, eventualmente os nomes, e verificar se pode a cada momento utilizar o valor
de uma varivel em vez da outra. Por vezes no simples a identificao/remoo
destas situaes quando h muitas variveis. prefervel aplicar primeiramente a
remoo de declaraes/atribuies desnecessrias, e aplicar a resoluo do erro
uma atribuio, uma leitura, antes deste erro.
Comentrios explicam a linguagem C
o Forma: De forma a explicar tudo o que se passa no programa, e no ser penalizado,
por vezes desce-se to baixo que se explica inclusive linguagem C.
o Problema: Esta situao um claro excesso de comentrios, que prejudica a
legibilidade. Quem l cdigo C sabe escrever cdigo C, pelo que qualquer comentrio
do tipo atribuir o valor X varivel Y, desrespeitoso para quem l, fazendo
apenas perder tempo. Ao comentar instrues da linguagem e deixar de fora
comentrios mais relevantes, pode indicar que o cdigo foi reformulado por quem
na verdade no sabe o que este realmente faz.
o Resoluo: Escreva como se estivesse a escrever a si prprio, supondo que no se
lembrava de nada daqui a uns 6 meses.
o

No Programa 2-4 podemos ver uma ocorrncia do erro uma atribuio, uma leitura, dado que
todas as variveis so atribudas na declarao e utilizadas uma s vez. Neste caso pretende-se
mostrar a string de formatao para os diversos tipos, mas poder-se-ia igualmente ter colocado os
prprios valores no printf. O mesmo programa tem ainda uma ocorrncia do erro comentrios
explicam a linguagem C, e este erro ser cometido ao longo dos programas apresentados neste
texto, dado que o cdigo se destina a quem est a aprender, mas num programa C no faz sentido
colocar um comentrio mostrar valores antes do printf.
No h ocorrncias dos outros dois erros nos programas deste captulo, e a sua exemplificao seria
forada. A melhor fonte de exemplos tem de ser o seu cdigo, aps resolver os exerccios, procure
identificar ocorrncias desses erros.

2. Variveis

15

O conceito de varivel, aparentemente simples, no no entanto to facilmente interiorizado como


possa pensar. Saber no primeiro exerccio que seja necessria uma varivel auxiliar no declarada
no enunciado, se consegue ou no identificar a varivel e seu modo de utilizao, ou se por outro
lado considera o enunciado complexo e confuso.
Se for o segundo caso, antes de passar para o exerccio seguinte, imperativo que olhe para a
soluo que acabar por obter, com a sua mxima ateno. A experincia levar a que identifique
imediatamente as variveis que necessita para resolver cada problema, sendo esta uma das
principais caractersticas de um programador experiente.

16

Parte I Variveis e Estruturas de Controlo

3. CONDICINAIS
As instrues dadas nos programas at aqui so sequenciais, isto , existindo 4 instrues estas vo
sendo executadas sem sequncia, numa determinada ordem fixa. No entanto, o que fazer se
pretendermos ter um programa que tenha mais que uma execuo alternativa? Por exemplo,
estamos interessados em determinar se um determinado nmero inteiro introduzido pelo
utilizador, par ou mpar. Como h duas possibilidades para apresentar ao utilizador, tem de existir
duas instrues alternativas, uma para o caso do nmero ser par, e outra para o caso de ser mpar.
Para alm disso, temos de saber determinar se o nmero par ou mpar.
A linguagem C permite que um determinado cdigo seja executado dependente do valor de uma
expresso lgica. Se a expresso for verdadeira, executa uma instruo, caso contrrio executa a
outra instruo.
1
2
3
4
5
6
7
8
9
10
11
12

#include <stdio.h>
int main()
{
int numero;
printf("Indique um numero: ");
scanf("%d",&numero);
if(numero%2==0)
printf("par");
else
printf("impar");
}
Programa 3-1 Determina se um nmero par ou impar

Comentrios:

Linha 8: a instruo if tem entre parnteses uma expresso lgica. Essa expresso utiliza o
operador lgico == (no confundir com a atribuio), e do lado esquerdo utiliza o operador
% (resto da diviso). Se o resto da diviso do nmero por 2 for zero, ento a expresso lgica
verdadeira e executa a instruo da linha 9
Linha 10: existe uma instruo else. Essa instruo est associada ao if, mas o if pode ser
utilizado sem o else. Se existir indica uma instruo a executar no caso da expresso lgica
ser falsa

Execuo do programa:
C:\>paridade
Indique um numero: 234
par
C:\>paridade
Indique um numero: 23
impar

O resultado foi o pretendido, vejamos agora


a execuo passo-a-passo. Nesta execuo o
nmero introduzido foi o "234", levando a
que o condicional fosse verdadeiro e que no
passo 5 a linha 9 fosse executada.

3. Condicionais

17

Na segunda execuo, o valor introduzido foi


23, levando a que o condicional fosse falso e
que a linha 11 fosse executada no passo 5,
em vez da linha 9.
Podem ser utilizados os operadores lgicos
normais em expresses lgicas, e os parnteses curvos que forem necessrios:

A e B so expresses lgicas:
o A||B
A ou B
o A&&B
AeB
o ! A
no A
A e B expresses numricas:
o A==B
A igual a B
o A!= B A diferente de B
o A>B
A maior que B
o A>=B
A maior ou igual a B
o A<B
A menor que B
o A<=B
A menor ou igual a B
Nas expresses numricas:
o A+B
A mais B
o A-B
A menos B
o A*B
A vezes B
o A/B
A a dividir por B
o A%B
resto da diviso de A por B

Vamos aplicar estes operadores num problema mais complexo. Pretende-se saber se um ano ou
no bissexto. Um ano bissexto se for mltiplo de 4, mas de 100 em 100 anos esta regra no
vlida, excepto de 400 em 400 anos. Temos um problema que tambm requer apenas duas
execues alternativas, mas com uma expresso lgica mais complexa. O programa vai ter a mesma
estrutura que o anterior, mas tem que se construir a expresso lgica. Vejamos:

Mltiplo de 4:
o ano%4==0 resto da diviso com 4 nula, portanto o ano mltiplo de 4
Excepto de 100 em 100 anos:
o ano%4==0 && ano%100!=0 retorna verdadeiro para mltiplos de 4, mas se for
mltiplo de 100 j no
Excepto de 400 em 400 anos:
o (ano%4==0 && ano%100!=0) || ano%400==0 como a excepo positiva,
coloca-se uma disjuno e no uma conjuno como no caso anterior

18

Parte I Variveis e Estruturas de Controlo


1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include <stdio.h>
int main()
{
int ano;
printf("Indique ano: ");
scanf("%d", &ano);
/* teste de ano bissexto */
if(ano%4==0 && ano%100!=0 || ano%400==0)
printf("Bissexto");
else
printf("Normal");
}
Programa 3-2 Determina se um ano normal ou bissexto

Notar que no foram colocados parnteses para a conjuno. No necessrio dado que tem
sempre prioridade relativamente disjuno.
Execuo do programa:
C:\>bissexto
Indique ano: 2345
Normal
C:\>bissexto
Indique ano: 2344
Bissexto

Na execuo passo-a-passo pode-se


ver que na expresso lgica do passo
4, o segundo argumento do operador
&& (AND) no foi avaliado porque o
primeiro argumento j era falso.
Desta vez o segundo argumento do
operador || (OR) no foi avaliado
porque o primeiro argumento j era
verdadeiro.
Pode acontecer que seja necessrio
mais que uma instruo dentro do if, ou que sejam necessrias vrias instrues if encadeadas.
o caso do programa seguinte, em que est implementado um algoritmo para determinar o nmero
de dias do ms, dado o nmero do ano e ms.

3. Condicionais
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

19

#include <stdio.h>
int main()
{
int ano, mes, dias;
printf("Indique ano: ");
scanf("%d", &ano);
printf("Indique mes: ");
scanf("%d", &mes);
if(mes==2)
{
/* teste de ano bissexto */
if(ano%400==0 || ano%4==0 && ano%100!=0)
printf("29");
else
printf("28");
} else if(mes==1 || mes==3 || mes==5 || mes==7 ||
mes==8 || mes==10 || mes==12)
{
printf("31");
} else
{
printf("30");
}
}
Programa 3-3 Determina o nmero de dias de um ms/ano

Comentrios:

O primeiro if testa se o ms 2 (Fevereiro). Neste caso tem que se testar se o ano ou no


bissexto de forma a retornar 29 ou 28. Utilizamos o cdigo do exerccio anterior, mas teve
de ser colocado dentro do if. Para no haver confuses com o else a que pertence este if,
colocou-se o if num bloco (dentro de chavetas).
Um bloco de cdigo pode ser colocado em qualquer lado em que se coloca um s
comando, como o caso do if. Assim possvel executar quantas instrues se quiser
utilizando o mesmo condicional.
Aps se saber que o ms no 2, tem que se testar se um ms de 31 dias, ou se um ms
de 30 dias. Tem de portanto existir outro if logo a seguir ao else. Embora seja um if
distinto, est em sequncia do primeiro if e na verdade permite que o programa siga um de
trs caminhos possveis (Fevereiro / ms 31 / ms 30), e no apenas 2 como aconteceria se
utilizar apenas um s if.
A expresso lgica para determinar que o ms tem 31 dias longa, pelo que muda de linha.
No h problema com isso, mas neste caso foi uma m opo, podendo-se ter testado se o
ms tinha 30 dias, que so menos meses (4, 6, 9 e 11).

Execuo do programa:
C:\>diasdomes
Indique ano: 2344
Indique mes: 2
29
C:\>diasdomes
Indique ano: 2342
Indique mes: 4
30

Esta
execuo
tem
dois
condicionais encadeados, o passo 7

20

Parte I Variveis e Estruturas de Controlo

est um segundo condicional que apenas executado porque no passo 6 o primeiro condicional
retornou verdadeiro. Notar que h uma varivel dias que afinal no necessria. Neste caso nem
sequer atribuda, pelo que pode ser apagada do cdigo.
Na segunda execuo o resultado
no passo 6 falso, pelo que a
execuo segue no passo 7 para um
outro
condicional
sobre
o
respectivo else, no confundindo o
else do condicional interior, dado
que est dentro de um bloco de
cdigo.
No se pode deixar de referir que as expresses lgicas em C so valores inteiros, ou se quiser, um
inteiro o tipo de dados utilizado nas expresses lgicas, em que o valor zero significa o falso, e o
valor no zero significa o verdadeiro. Com este conhecimento pode-se em vez do teste utilizado no
primeiro exemplo:
8
9
10
11

if(numero%2==0)
printf("par");
else
printf("impar");

Fazer simplesmente:
8
9
10
11

if(numero%2)
printf("impar");
else
printf("par");

No entanto no se aconselha a fazer utilizao abusiva deste conhecimento, dado que torna mais
pesada a leitura do cdigo. Cada expresso lgica deve ter pelo menos um operador lgico, de
forma a facilitar a leitura do cdigo e identificar a expresso como uma expresso lgica, e no
confundir com uma expresso numrica, embora em C qualquer expresso numrica uma
expresso lgica.
Os erros comuns mais directamente associados a este captulo so:

Indentao4 varivel
o Forma: Como a indentao ao gosto do programador, cada qual coloca a
indentao como lhe d mais jeito ao escrever.
o Problema: A leitura fica prejudicada se a indentao no for constante. Por um lado
no fcil identificar se h ou no algum erro do programador em abrir/fechar
chavetas, dado que a indentao pode ser sua opo e no esquecimento de
abrir/fechar chavetas. Por outro lado, no possvel a visualizao rpida das
instrues num determinado nvel, dado que o nvel varivel, sendo necessrio
para compreender algo, de analisar o cdigo por completo. Com o cdigo indentado,
pode-se ver o correcto funcionamento de um ciclo externo, por exemplo, sem ligar
ao cdigo interno do ciclo, e assim sucessivamente.

A indentao de uma instruo consiste em anteced-la de um nmero de espaos constante (so utilizados
4 espaos neste texto), por cada bloco em que a instruo est inserida

3. Condicionais

Resoluo: Indente o cdigo com 2 a 8 espaos, mas com o mesmo valor ao longo de
todo o cdigo. Se utilizar tabs, deve indicar o nmero de espaos equivalente, no
cabealho do cdigo fonte, caso contrrio considera-se que o cdigo est mal
indentado. Se pretender subir a indentao com a abertura de uma chaveta, pode
faz-lo mas tem de utilizar sempre chavetas, caso contrrio ficam instrues que
esto no mesmo nvel mas no cdigo ficam e indentaoes distintas. Qualquer que
seja as opes, no pode existir instrues no mesmo alinhamento que pertenam a
nveis distintos (ou vice-versa). A posio das chavetas indiferente, mas tem que se
apresentar um estilo coerente ao longo do cdigo.
Duas instrues na mesma linha
o Forma: Quando as instrues so pequenas e esto relacionadas, mais simples
coloc-las todas na mesma linha. Como uma questo de estilo, no afecta o
funcionamento.
o Problema: Se h instrues relacionadas, estas devem ser colocadas na mesma
funo, se tal se justificar. Colocar duas ou mais instrues na mesma linha vai
prejudicar a legibilidade e no s. O compilador quando encontra algum problema
refere a linha do cdigo, e existindo mais que uma instruo nessa linha, a
informao menos precisa. Deixa de ser possvel tambm seguir um nvel de
indentao para passar por todas as instrues nesse nvel, tem que se fazer uma
leitura atenta por todo o cdigo do bloco
o Resoluo: Em todas as linhas com mais que uma instruo, colocar uma instruo
por linha
Linhas de cdigo nunca executadas
o Forma: Por vezes em situaes de stress tentam-se vrias alternativas, e fica algum
cdigo que na verdade nunca tem hiptese de ser executado, mas que tambm no
atrapalha.
o Problema: Se h cdigo a mais, este deve ser removido sobre pena de perda de
legibilidade e manuteno. Esta situao ampliada quando h cdigo de diversas
provenincias, em que ao integr-lo ningum se atreve a reeditar cdigo que
funciona, para seleccionar a parte do cdigo que deve ser integrado.
o Resoluo: Identificar as zonas de cdigo que de certeza que no so executadas de
modo algum, e apag-las. Tanto pode ser feito por inspeco ao cdigo como atravs
de testes, submetendo o cdigo a diversos casos de teste, desde que previamente se
tenha colocado informao de debug em cada bloco de cdigo. Os blocos de cdigo
em que no tiver sido produzida nenhuma informao de debug, so provavelmente
os blocos de cdigo que nunca so executados.
o

21

22

Parte I Variveis e Estruturas de Controlo


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include <stdio.h>
int main()
{
int ano, mes, dias;
printf("Indique ano: "); scanf("%d", &ano);
printf("Indique mes: "); scanf("%d", &mes);
if(mes==2)
if(ano%400==0 || ano%4==0 && ano%100!=0)
printf("29");
else
printf("28");
else if(mes==1 || mes==3 || mes==5 || mes==7 ||
mes==8 || mes==10 || mes==12)
printf("31");
else if(mes!=2)
printf("30");
else
printf("erro");
}

O cdigo acima uma rplica do Programa 3-3 com a mesma funcionalidade, no entanto contendo
os 3 erros indicados. A indentao na linha 5 a 4 espaos, mas na linha 10 quando o condicional
est debaixo do condicional na linha 9, no sobe a indentao. O leitor nessa situao no sabe se o
que aconteceu foi um esquecimento da instruo para o primeiro condicional, ou se o que
pretendido realmente um condicional sobre o outro. Os outros dois erros, o printf e o scanf
esto na mesma linha, e a linha 20 nunca tem hiptese de ser executada, j que o condicional da
linha 17 sempre verdadeiro, uma vez que complementar do condicional no mesmo nvel na linha
9.
Os condicionais um conceito relativamente simples de interiorizar, muito provavelmente no ter
dificuldade em identificar a necessidade de execues alternativas. No entanto, na escrita do cdigo
muito frequente cometer erros nas expresses lgicas, e ao ler pensa que a expresso tem um
efeito quando na realidade tem outro. A utilizao de expresses simples, e bons nomes nas
variveis, o aconselhado para que o cdigo tenha uma boa leitura, e consequentemente tenha
menos erros.

4. Ciclos

23

4. CICLOS
Um programa serve para executar um conjunto de instrues. No entanto, com o que aprendemos
at agora, cada linha de cdigo executada quanto muito uma vez. Isto limita muito a utilidade de
programas.
Consideremos que se pretende calcular a soma dos primeiros 4 quadrados, no conhecendo
nenhuma expresso matemtica para obter o valor directamente. O programa seguinte resolveria o
problema:
1
2
3
4
5
6
7
8
9
10
11

#include <stdio.h>
int main()
{
int soma=0;
soma=soma+1*1;
soma=soma+2*2;
soma=soma+3*3;
soma=soma+4*4;
printf("Soma dos primeiros 4 quadrados: %d", soma);
}
Programa 4-1 Soma dos primeiros 4 quadrados naturais

Execuo do programa:
C:\>somaquadrados
Soma dos primeiros 4 quadrados: 30

Execuo passo-a-passo
novidades.

no

apresenta

Problemas com este programa:


o

o
o
o

Se pretender obter a soma dos


primeiros 9 quadrados, temos de refazer o programa. sempre mais interessante ter um
programa que pode ser utilizado numa maior gama de situaes, que um programa que
serve apenas para uma situao especfica.
O cdigo repetido pode at ser facilmente copivel, mas se for necessrio alterar algum
pormenor, essa alterao tem de ser reproduzida em todas as cpias realizadas.
A varivel soma repetida antes e depois da atribuio. A repetio da mesma entidade ou
bloco de cdigo "pesa" no s ao editar e corrigir algum ponto, como tambm na leitura.
O tamanho do programa cresce se pretender um clculo com um valor maior.

Vale a pena ter um programa assim? claro que sim, fazer as contas mo que no. No entanto se
no existisse alternativa, um computador teria uma utilidade pouco mais do que se obtm com uma
calculadora. A soluo passa por utilizar um ciclo, em que no C o mais simples o ciclo while.
muito parecido com o condicional if, mas ao contrrio do if o cdigo repetido enquanto a
expresso lgica for verdadeira.

24

Parte I Variveis e Estruturas de Controlo


1
2
3
4
5
6
7
8
9
10
11
12

#include <stdio.h>
int main()
{
int soma=0, i=1;
while(i<=4)
{
soma+=i*i;
i++;
}
printf("Soma dos primeiros 4 quadrados: %d", soma);
}
Programa 4-2 Soma dos primeiros 4 quadrados naturais utilizando um ciclo

Comentrios:
o
o

o
o

O ciclo while avalia a expresso lgica, i<=4, e enquanto esta for verdadeira soma o
quadrado de i e incrementa o valor de i.
Utilizou-se o operador +=, que em vez de efectuar uma atribuio soma varivel
esquerda o valor da expresso direita. Existem tambm os operadores equivalentes =, *=, /=, %=.
Para incrementar o valor de i, existe ainda um operador especial ++, j que se pretende
somar apenas o valor 1. Existe tambm o operador inverso -- que subtrai uma unidade.
Neste programa, se for necessrio troca-se facilmente a constante 4 por uma varivel que
introduzida pelo utilizador, ficando o programa mais genrico. O nmero de instrues no
depende agora do argumento pretendido.

A execuo passo-a-passo um pouco mais


longa. importante que veja com ateno
esta
execuo
passo-a-passo,
para
compreender o funcionamento do ciclo.
Repare que cada linha no ciclo foi
executada vrias vezes, apenas os valores
que as variveis tinham quando essas linhas
foram executadas que eram diferentes.
Como que o ciclo foi construdo? Tnhamos
um conjunto de operaes seguidas para
realizar, e criou-se uma varivel auxiliar para
contar o nmero de vezes que o ciclo foi executado, e tambm para controlar a paragem do ciclo.
Pretendamos 4 passos, colocou-se a condio de paragem no 4 e incrementou-se em cada ciclo o
valor da varivel.
Este tipo de varivel praticamente forosa em todos os ciclos, tendo merecido a distino de se
chamar uma varivel iteradora. habitual utilizar-se os nomes i e j para variveis iteradoras.
Vamos agora apresentar um outro exemplo.

4. Ciclos

25

Pretende-se saber o nmero associado aos caracteres de 'a' a 'z'.


1
2
3
4
5
6
7
8
9
10
11

#include <stdio.h>
int main()
{
char c='a';
while(c<='z')
{
printf("\nLetra %c = %d",c,c);
c++;
}
}
Programa 4-3 Imprime o cdigo numrico correspondente a cada letra

Comentrios:
o

Neste caso temos a varivel iteradora como um carcter (tipo char). Como o tipo char
tambm um inteiro pequeno, no h problema em utilizar desigualdades com caracteres,
incluindo c<='z'. As letras tm o cdigo numrico por ordem.
No printf, aps as aspas temos \n. A \ nas strings significa que se segue um carcter
especial, neste a mudana de linha. O \n traduzido no cdigo final para um valor que
interpretado no sistema operativo como uma instruo para reposicionar o cursor na linha
seguinte. Desta forma as letras so colocadas em linhas distintas. O printf utiliza o que j
vimos para a letra 'a', colocando a varivel char no printf tanto em formato de carcter
como em formato numrico.

Execuo do programa:
C:\>caracteres
Letra
Letra
Letra
Letra
Letra
Letra
Letra
Letra
...
Letra
Letra
Letra
Letra

a
b
c
d
e
f
g
h

=
=
=
=
=
=
=
=

97
98
99
100
101
102
103
104

w
x
y
z

=
=
=
=

119
120
121
122

Apresenta-se a execuo passo-a-passo com os primeiros


7 passos. Estes nmeros correspondem ao cdigo
associado a cada letra, que ir ser explicado quando se
falar de vectores e strings.
Vamos agora dar um exemplo mais complexo. Pretendese saber para um determinado nmero inteiro K,
quantos pares de nmeros inteiros (A,B) existem que
verifiquem as seguintes condies: A+B<=K, A*B<=K,
A>=1 e B>=1.

26

Parte I Variveis e Estruturas de Controlo


1 #include <stdio.h>
2
3 int main()
4 {
5
int A,B,K,contagem;
6
printf("Indique K: ");
7
scanf("%d",&K);
8
contagem=0;
9
A=1;
10
while(A<=K)
11
{
12
B=1;
13
while(B<=K)
14
{
15
if(A+B<=K && A*B<=K)
16
contagem++;
17
B++;
18
}
19
A++;
20
}
21
printf("Total: %d",contagem);
22 }
Programa 4-4 Calculo do nmero de pares de inteiros que respeitam uma determinada condio

Comentrios:
o
o

O programa necessita iterar em A e em B. Como A e B tm de ser maior ou igual a 1, e


nenhum pode ser maior que K, cada ciclo tem de ser feito entre 1 e K.
A expresso lgica colocada conforme a definio. A expresso lgica do condicional
poderia ser utilizada directamente no segundo ciclo. No entanto nesse caso seria
conveniente um bom comentrio a explicar a mudana, dado que embora mais eficiente o
cdigo fica mais difcil de compreender.
Com um ciclo dentro do outro, as variveis A e B a comearem em 1 e a acabarem em K,
todos os pares (A,B) foram verificados. Repare que a inicializao de B=1; est dentro do
ciclo da varivel A. Cada varivel (A e B) so inicializadas antes do ciclo, utilizadas no
condicional do ciclo, e actualizadas (incrementadas) no final do seu ciclo.

Execuo do programa:
C:\>somamul
Indique K: 10
Total: 25

Vamos ver a execuo passo-a-passo para


K=3.
Este um exemplo j bastante longo, mas
importante que o veja com algum
cuidado, dado que o primeiro ciclo
dentro de outro ciclo. Escolha um passo
aleatoriamente, e verifique se consegue
construir
o
passo
seguinte.
Se
compreender esta execuo passo-apasso, muito provavelmente no ter
problemas em construir ciclos.
Os ciclos so fceis de identificar, tal

4. Ciclos

27

como os condicionais, mas quando


necessrio um ciclo dentro do outro, ou
mesmo ainda um terceiro ciclo, a
compreenso do cdigo baixa dado que
necessrio em cada ciclo considerar os
ciclos sobre o qual est a correr. Cada
ciclo tem uma expresso lgica, que por si
s pode ser complexa tal como nos
condicionais, mas como tem uma varivel
iteradora para fazer variar a expresso
lgica, leva a que deva ser dada a mxima
ateno nos ciclos.
Erros
comuns
mais
associados a este captulo:
o

directamente

Utilizao do goto
o Forma:
Numa
dada
instruo,
sabe-se
a
condio pretendida para
voltar para cima ou saltar
para baixo. suficiente
colocar um label no local
desejado, e colocar o goto dentro do condicional. Este tipo de instruo pode ser
motivado pelo uso de fluxogramas
o Problema: Perde-se nada mais nada menos que a estrutura das instrues. A
utilizao ou no desta instruo, que define se a linguagem estruturada, ou no
estruturada (por exemplo o Assembly). Se de uma linha de cdigo se poder saltar
para qualquer outra linha de cdigo, ao analisar/escrever cada linha de cdigo, tem
que se considerar no apenas as linhas que a antecedem dentro do bloco actual,
como todas as linhas de cdigo, dado que de qualquer parte do programa pode
haver um salto para esse local. Para compreender 1 linha de cdigo, necessrio
considerar todas as instrues no programa. A complexidade do programa nesta
situao cresce de forma quadrtica com o nmero de linhas.
o Resoluo: Utilizar as estruturas de ciclos disponveis, bem como condicionais, e
funes5. Se o salto para trs dentro da mesma funo certamente um ciclo o que
pretendido, mas se para a frente, provavelmente um condicional. Se um salto
para uma zona muito distante, ento provavelmente uma funo que falta. No caso
de utilizar fluxogramas, deve considerar primeiro utilizar o seu tempo de arranque
vendo execues passo-a-passo de forma a compreender o que realmente um
programa, sem segredos, e resolver exerccios
Instrues parecidas seguidas
o Forma: Quando necessrio uma instruo que parecida com a anterior, basta
seleccion-la e fazer uso de uma das principais vantagens dos documentos digitais:
copy/paste

O conceito de funo introduzido no captulo seguinte

28

Parte I Variveis e Estruturas de Controlo


o

Problema: O cdigo ficar muito pesado de ler, podendo tambm prejudicar a


escrita. sinal que est a falhar um ciclo, em que as pequenas mudanas entre
instrues, em vez de serem editadas e alteradas, essa alterao colocada
dependente da varivel iteradora, fazendo numa s instruo a chamada a todas as
instrues parecidas, debaixo de um ciclo. O cdigo no s fica mais simples de ler,
como se for necessria outra instruo basta alterar a expresso lgica de paragem
do ciclo, em vez de um copy/paste e edio das alteraes
Resoluo: Se encontra situaes destas no seu cdigo, estude alternativas para
utilizar ciclos

O segundo erro foi cometido no Programa 4-1, de forma a motivar os ciclos. O primeiro erro est
cometido no programa seguinte, que ser tambm o nico programa neste texto a utilizar a
instruo goto.
1
2
3
4
5
6
7
8
9
10
11
12

#include <stdio.h>
int main()
{
int soma=0, i=1;
loop:
soma+=i*i;
i++;
if(i<=4)
goto loop;
printf("Soma dos primeiros 4 quadrados: %d", soma);
}

Tem que existir um label na linha 6 de forma a utiliz-lo na instruo goto da linha 10. Os labels
so identificadores a terminarem com dois pontos. Cdigo deste tipo nem sequer pode ter uma
indentao com algum significado, j que as instrues nas linhas 7 e 8 no podem estar indentadas,
no entanto esto sobre um ciclo feito com if/goto.
imperativo que realize agora actividades formativas para consolidar os conceitos introduzidos.
Nesta altura est em condies de fazer todos os exerccios, no entanto ir sentir dificuldades nos
azuis e vermelhos, o que normal, dado que fazem uso mais abusivamente dos conceitos
introduzidos, que necessitam tempo de maturao e consolidao.

1) Exerccios

29

1) Exerccios
Notas gerais a ter em ateno em todos os exerccios:

Escreva sem acentos;


Utilize para as variveis inteiras, o tipo int, confirmando que tem 4 bytes no exerccio
olamundosizeof.c, caso contrrio utilize o tipo equivalente;
Utilize para as variveis reais a preciso dupla, o tipo double;
Durante o desenvolvimento do programa, imprima resultados parciais, de forma a garantir
o que o programa faz, eventualmente comentando esse cdigo na verso final;
O separador decimal da linguagem C o ponto final, pelo que ser utilizado nos resultados
dos exerccios (12.435 correcto, 12,435 incorrecto), mas na introduo dos dados e
impresso dos resultados, poder variar conforme as configuraes no computador;
Se no consegue resolver um exerccio, pense numa varivel auxiliar que lhe d jeito;
Existem dicas adicionais sobre alguns exerccios no anexo Exerccios: Dicas, Respostas e
Resolues, bem como forma de validar a resposta de um exerccio sem ver uma resoluo,
e resolues dos exerccios para comparar aps resolv-los.

olamundosizeof.c
Faa um programa que coloque Ol Mundo! em bom portugus (com acentos), e que indique
o tamanho em bytes (operador sizeof) dos seguintes tipos de dados: char; short; int; long;
long long; float; double; long double.
Notas:

Se os caracteres no lhe aparecerem bem na linha de comando, antes de executar o


programa execute o comando C:\>chcp 1252

Execuo de exemplo:
C:\>chcp 1252
Active code page: 1252
C:\>olamundosizeof
Ol Mundo!
sizeof(char): xx
sizeof(short): xx
sizeof(int): xx
sizeof(long): xx
sizeof(long long): xx
sizeof(float): xx
sizeof(double): xx
sizeof(long double): xx

Pergunta: qual a soma dos tamanhos dos tipos pedidos compilado no tcc?

30

Parte I Variveis e Estruturas de Controlo

soma.c
Somar os primeiros N nmeros inteiros, sendo N definido pelo utilizador:
N

i
i =1

Notas:

Escreva sem acentos neste e nos restantes exerccios, para que o cdigo funcione sempre
correctamente mesmo sem mudar o cdigo de pgina para 1252.
Durante o desenvolvimento do programa, imprima resultados parciais, de forma a garantir
o que o programa faz, eventualmente comentando esse cdigo na verso final, dado que
esse texto poder fazer sentido apenas para o programador e no para o utilizador.

Execuo de exemplo:
C:\>soma
Calculo da soma dos primeiros N numeros.
Indique N:10
adicionar
adicionar
adicionar
adicionar
adicionar
adicionar
adicionar
adicionar
adicionar
adicionar
Total: 55

1, parcial 1
2, parcial 3
3, parcial 6
4, parcial 10
5, parcial 15
6, parcial 21
7, parcial 28
8, parcial 36
9, parcial 45
10, parcial 55

Pergunta: qual a soma dos primeiros 21090 nmeros inteiros?

hms.c
Faa um programa que leia as horas, minutos e segundos, e calcule o nmero de segundos que
passaram desde o incio do dia.
Notas:

No faa verificao da validade dos parmetros de entrada

Execuo de exemplo:
C:\>hms
Calculo do numero de segundos desde o inicio do dia.
Hora: 2
Minuto: 15
Segundos: 30
Numero de segundos desde o inicio do dia: 8130

1) Exerccios

31

Pergunta: qual a resposta no caso de se colocar a 25 hora, o minuto 100 e o segundo 200?

produto.c
Multiplicar os primeiros N nmeros inteiros positivos (factorial de N), sendo N definido pelo
utilizador:
N

N!= i
i =1

Execuo de exemplo:
C:\>produto
Calculo do produto dos primeiros N numeros.
Indique N:5
Factorial(1)=1
Factorial(2)=2
Factorial(3)=6
Factorial(4)=24
Factorial(5)=120
Resultado: 120

Pergunta: qual o factorial de 12?

arranjos.c
Calculo dos arranjos de N, R a R: multiplicar os nmeros de N-R+1 at N:
N
N!
A( N , R ) =
=
i
(N R)! i=
N R+1

Notas:

Ateno que R tem de ser menor que N


Os arranjos de 3 elementos {A, B, C}, 2 a 2, so os seguintes 6: (A,B); (A,C); (B,A); (B,C);
(C,A); (C,B).

Execuo de exemplo:
C:\>arranjos
Calculo dos arranjos de N, R a R:
Indique N:5
Indique R:3
i=3; arranjos=3
i=4; arranjos=12
i=5; arranjos=60
Resultado: 60

Pergunta: qual o resultado retornado dos arranjos de 20, 8 a 8?

32

Parte I Variveis e Estruturas de Controlo

somadigitos.c
Calcule a soma dos quadrados dos dgitos de um nmero introduzido pelo utilizador.
Notas:

Pode obter o valor do dgito mais baixo, calculando o resto da diviso por 10.
Mostre o resultado parcial, neste e nos restantes exerccios.

Execuo de exemplo:
C:\>somadigitos
Calculo da soma do quadrado dos digitos de um numero:
Numero: 1234
n=1234; soma=16
n=123; soma=25
n=12; soma=29
n=1; soma=30
Resultado: 30

Pergunta: se o utilizador introduzir o nmero 856734789, qual o resultado do programa?

fibonacci.c
Calcular o valor da funo fibonacci6, para um dado argumento N. Para N=1 ou 2, deve
retornar N, caso contrrio retorna a soma dos dois valores anteriores:

n
,n 2

F (n ) =
F (n 1) + F (n 2) , n > 2
Notas:

Utilize duas variveis auxiliares.

Execuo de exemplo:
C:\>fibonacci
Calculo do valor da funcao Fibonacci:
Indique N:6
Fib(3)=3
Fib(4)=5
Fib(5)=8
Fib(6)=13
Resultado: 13

Pergunta: qual o valor do programa para N=40?

definio da funo Fibonacci no padro

1) Exerccios

33

combinacoes.c
Calcule as combinaes de N, R a R. A frmula idntica dos arranjos, multiplicar de N-R+1
at N, mas tem de se dividir pelo factorial de R:

C (N , R ) =

N
N!
= i
(N R )!R! i= N R+1

i
i =1

Notas:

Se efectuar as multiplicaes e s dividir no final, rapidamente ultrapassa o limite do


inteiro. Se multiplicar e depois dividir em cada passo, consegue obter valores correctos para
uma maior gama de nmeros.
As combinaes de 3 elementos {A, B, C}, 2 a 2, so as seguintes 3: {A,B}; {A,C}; {B,C}. Notar
que relativamente aos arranjos, neste caso a ordem no interessa.

Execuo de exemplo:
C:\>combinacoes
Calculo das combinacoes de N, R a R:
Indique N:5
Indique R:3
1*3=3/1=3
3*4=12/2=6
6*5=30/3=10
Resultado: 10

Pergunta: qual o nmero de combinaes de 26, 13 a 13?

euler.c
Calcular o nmero de Euler e , atravs da utilizao da srie de Taylor para e x quando x = 1
(soma do inverso dos factoriais):
K

1
n=0 n!

e=
Notas:

Considere o factorial de zero como sendo 1


Utilize a preciso dupla para valores reais, neste e em outros exerccios
Na funo printf pode imprimir um nmero real em notao cientfica, e especificando a
preciso a 16 dgitos utilizando a string de formatao: %.16g

34

Parte I Variveis e Estruturas de Controlo

Execuo de exemplo:
C:\>euler
0: 1
1: 2
...
19: 2.7183
20: 2.7183
Resultado: 2.71828182845xxxx
xxxx

Pergunta: qual o valor com preciso 16, em notao cientfica, da execuo da frmula acima com
K=20?

trocos.c
Faa um programa que receba um montante em euros (com cntimos), e que determina o
menor nmero de moedas de cada tipo necessrio para perfazer esse montante. Pode utilizar
moedas de euros de todos os valores disponveis (2, 1, ...).
Notas:

Deve efectuar os arredondamentos para a unidade mais prxima, no caso de o utilizador


introduzir um valor com preciso abaixo do cntimo.

Execuo de exemplo:
C:\>trocos
Introduza um montante em euros, podendo ter centimos: 1.79
1 euro: 1
50 centimos: 1
20 centimos: 1
5 centimos: 1
2 centimos: 2

Pergunta: para devolver 19.99 euros, quantas moedas so retornadas?

primo.c
Faa um programa que verifica se um determinado nmero N um nmero primo. Um
nmero primo se divisvel apenas por ele prprio e pela unidade. Se no for primo deve
identificar o menor nmero pelo qual divisvel.
Notas:

suficiente testar at raiz quadrada de N (pode utilizar a funo sqrt da biblioteca


math.h)
Em vez de calcular a raiz quadrada de N, pode calcular o quadrado do divisor.

1) Exerccios

35

Execuo de exemplo:
C:\>primo
Funcao que verifica se um numero N e' primo:
Indique N:99
2
Numero divisvel por 3
C:\>primo
Funcao que verifica se um numero N e' primo:
Indique N:97
2 3 4 5 6 7 8 9
Numero primo!

Pergunta: Some os nmeros retornados dos seguintes nmeros (no caso dos nmeros primos no
some nada): 241134319; 241234319; 13212311.

triplasoma.c
Dado um inteiro positivo N, escrever todas as decomposies distintas possveis como soma
de trs inteiros positivos (considerar iguais as triplas com os mesmos valores mas por outra
ordem). Calcular tambm o nmero de somas distintas.
Notas:

Assumir que os nmeros mais altos aparecem sempre primeiro.

Execuo de exemplo:
C:\>triplasoma
Escreva um numero para decompor em somas de tres parcelas.
Numero:7
5+1+1
4+2+1
3+3+1
3+2+2
Numero de somas: 4

Pergunta: quantas somas existem para N=1000?

pi.c
Calcular o valor de com base na frmula de Ramanujan:

2 2 K (4k )! (1103 + 26390k )

9801 k =0
(k!)4 396 4 k

Notas:

Utilize preciso dupla


Pode utilizar a funo sqrt para calcular a raiz quadrada, da biblioteca math.h

36

Parte I Variveis e Estruturas de Controlo

Execuo de exemplo:
C:\>pi
Valor de PI (x
x iteracoes): 3.14159265xxxxxxxx
xxxxxxxx

Pergunta: qual o valor de com preciso 17 em notao cientfica, para K=2?

formularesolvente.c
Faa um programa que pea os coeficientes de um polinmio do segundo grau, e retorna as
razes reais, caso existam. Adicionalmente o programa deve retornar todos os conjuntos de
coeficientes inteiros, que tm apenas razes inteiras reais. Os coeficientes esto entre -K e K no
tendo nenhum coeficiente nulo (K introduzido pelo utilizador e um inteiro pequeno). Relembrase a frmula resolvente:

b b2 4ac
ax + bx + c = 0 x =
2a
2

Notas:

Para calcular a raiz quadrada, utilize a funo sqrt, disponvel na biblioteca math.h
Faa trs ciclos um dentro do outro, um ciclo por cada um dos coeficientes, e varie a varivel
iteradora entre -K e K.

Execuo de exemplo:
C:\>formularesolvente
Equacao do segundo grau a*x^2+b*x+c=0.
Indique a b c: 2 4 2
Delta: 0.000000
A equacao tem uma raiz unica, x=-1.000000
Calculo de coeficientes entre -K e K inteiros nao nulos, com razes
inteiras.
Introduza K:2
Coeficientes de -2 a 2 inteiros nao nulos, com razes inteiras:
[-1 -2 -1] [-1 -1 2] [-1 1 2] [-1 2 -1] [1 -2 1] [1 -1 -2] [1 1 -2] [1
2 1]
Total: 8

Pergunta: quantas equaes do segundo grau existem, com coeficientes entre -10 e 10 inteiros no
nulos, e com razes inteiras?

PARTE II FUNES, VECTORES E


RECURSO
Apenas com a Parte I possvel fazer programas, mas no h nenhuma ferramenta de controlo de
complexidade. O cdigo medida que cresce requer cada vez mais ateno para o escrever e
manter. Na Parte II vamos dar uma ferramenta da programao que permite controlar a
complexidade de um programa: a abstraco funcional. O bom uso desta ferramenta leva a que a
nossa ateno apenas tenha de estar a cada momento centrada num reduzido conjunto de linhas de
cdigo, e mesmo assim garante que toda a funcionalidade do programa satisfeita. Desta forma
possvel escrever programas de qualquer dimenso. Sero apresentados tambm os vectores, de
forma a poder lidar com grande volume de variveis, bem como alternativas mais elegantes para
ciclos e condicionais. Aps a realizao da Parte II, ficar com bases slidas para escrever
programas de qualquer dimenso, sem grandes limitaes.

40

Parte II Funes, Vectores e Recurso

5. FUNES
O termo "funo" em portugus muito lato, aplicvel a diversas entidades na linguagem C sejam
instrues sejam variveis. Tudo tem uma funo, caso contrrio no estaria na linguagem. As
atribuies tm a funo de atribuir o valor de uma expresso a uma varivel, os condicionais tm a
funo de executar uma instruo mediante o resultado de uma expresso lgica, os ciclos tm a
funo de executar um conjunto de instrues enquanto uma expresso lgica se mantiver
verdadeira, e as variveis tm a funo de manter um valor de forma a este ser utilizado em
expresses, ou ser trocado por outro atravs de uma atribuio.
Na linguagem C o termo funo tem no entanto um sentido muito concreto, e deve o seu nome
precisamente devido importncia que tem.
Uma funo um bloco de cdigo que pode ser chamado de qualquer parte do programa,
quantas vezes se quiser. Por chamar, significa que se tem uma instruo a ordenar o computador a
executar o bloco de cdigo correspondente funo identificada, e s depois continuar com a
execuo das restantes linhas de cdigo.
Vamos clarificar este ponto com o seguinte exemplo, ainda sem utilizar funes. Pretende-se um
programa que oferece um menu ao utilizador de 3 opes, e uma opo de sada do programa.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include <stdio.h>
int main()
{
int opcao;
/* mostrar as opes do menu */
printf("\nMenu:\n1 - opcao A\n2 - opcao B\n3 - opcao C\n0 - sair");
printf("\nOpcao: ");
scanf("%d",&opcao);
while(opo>0)
{
/* ver qual a opo */
if(opcao==1)
printf("Opcao escolhida A");
else if(opcao==2)
printf("Opcao escolhida B");
else if(opcao==3)
printf("Opcao escolhida C");
else
printf("Opcao invalida");
/* mostrar as opes do menu */
printf("\nMenu:\n1 - opcao A\n2 - opcao B\n3 - opcao C\n0 - sair");
printf("\nOpcao: ");
scanf("%d",&opcao);
}
printf("Fim do programa.");
}
Programa 5-1 Escolha de opes num menu

Este programa utiliza apenas conceitos j introduzidos. Nada de novo.

5. Funes

41

Execuo do programa:
C:\>menu
Menu:
1 - opcao A
2 - opcao B
3 - opcao C
0 - sair
Opcao: 2
Opcao escolhida B
Menu:
1 - opcao A
2 - opcao B
3 - opcao C
0 - sair
Opcao: 5
Opcao invalida
Menu:
1 - opcao A
2 - opcao B
3 - opcao C
0 - sair
Opcao: 0
Fim do programa.

O programa funcionou de acordo com o esperado. No entanto o cdigo correspondente ao menu


est repetido tanto no incio do programa, como no ciclo. Isto porque para entrar no ciclo
necessrio que o menu j tenha sido mostrado, e o utilizador j tenha introduzido uma opo.
O cdigo repetido no teve grande trabalho a ser escrito, foi apenas uma operao de Copy/Paste.
Se fosse necessrio em mais partes do programa, fazia-se mais cpias desse cdigo. Esta operao
to vulgar em aplicaes informticas, no entanto proibida em programao. Porqu?

Para fazer uma alterao numa parte do cdigo duplicada, tem que se conhecer as partes
para a qual o cdigo foi copiado para reproduzir a alterao nessas partes;
Se o cdigo duplicado sofrer pequenas alteraes no aplicveis ao cdigo original, torna-se
complicado manter ambos os cdigos, dado que algumas das futuras correces no sero
aplicveis a ambos os cdigos;
O custo de leitura poder ser at maior que o custo de escrita, o que dificulta a manuteno
do cdigo;
Um bloco de cdigo pode funcionar bem numa zona, mas aps ser copiado para outra zona,
devido s instrues precedentes ou sucessoras, pode no ter o funcionamento esperado.

Qual a soluo? Fazer uma funo, ou seja, um bloco de cdigo com as instrues correspondente
ao menu, e chamar essa funo nos dois locais onde o menu necessrio.

42

Parte II Funes, Vectores e Recurso


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

#include <stdio.h>
int Menu()
{
int opcao;
/* mostrar as opes do menu */
printf("\nMenu:\n1 - opcao A\n2 - opcao B\n3 - opcao C\n0 - sair");
printf("nOpcao: ");
scanf("%d",&opcao);
/* retornar a opo seleccionada */
return opcao;
}
int main()
{
int opcao;
opcao=Menu();
while(opcao>0)
{
/* ver qual a opo */
if(opcao==1)
printf("Opcao escolhida A");
else if(opcao==2)
printf("Opcao escolhida B");
else if(opcao==3)
printf("Opcao escolhida C");
else
printf("Opcao invalida");
opcao=Menu();
}
printf("Fim do programa.");
}
Programa 5-2 Escolha de opes num menu, utilizando uma funo

Dentro da funo main, chama-se a funo Menu(), e atribui-se o valor retornado


varivel opo que j era utilizada para esta funo. Este procedimento repetido nos dois locais
onde necessrio o menu. Os parntesis curvos aps Menu, significam que o identificador uma
funo.
O cdigo correspondente funo est definido em cima, e idntico ao formato da funo main,
mas com o nome Menu. Este nome foi dado de forma a ficar o mais perto possvel relativamente
funcionalidade que implementa. Dentro da funo Menu no existe a varivel opo, que est
declarada na funo main, pelo que necessrio declarar tambm aqui a varivel opo para a
poder utilizar. So variveis distintas, embora tenham o mesmo nome, uma est na funo main e
outra na funo Menu. A funo Menu na ltima instruo return opcao; retorna o valor da
varivel opcao, sendo esse valor utilizado no local onde a funo foi chamada.
Na execuo passo-a-passo, a chamada funo Menu leva a uma criao de uma varivel, que o
nome da funo chamada, sendo as variveis criadas dentro da funo direita, sendo destrudas
quando a funo retorna. Verifique que no passo 6 a varivel opcao da funo Menu ficou com o
valor atribudo, mas apenas no passo 8 que este valor passou para a varivel opcao da
funo main.

5. Funes

43

A execuo de ambos os programas igual, mas


os problemas apontados j no esto presentes
no segundo programa:

Para fazer uma alterao, basta editar o


cdigo da funo e a alterao fica vlida
para todos os locais em que a funo
utilizada;
Se for necessrio utilizar a funo Menu
com ligeiras diferenas, pode-se utilizar
argumentos e mantm-se o cdigo num s
local.
O custo de leitura mais baixo, j que no
h linhas repetidas;
O cdigo da funo Menu funciona
independentemente do local onde
chamado, j que apenas depende das
instrues que esto na funo. Se fosse
copiado, necessitava de estar declarada
uma varivel opcao do tipo int, que no
estivesse a ser utilizada para outros fins.

A principal vantagem das funes no no


entanto evitar os problemas acima descritos
inerentes ao Copy/Paste, mas sim possibilitar a abstraco funcional. No h nenhuma outra
ferramenta na informtica, mais poderosa que a abstraco funcional.
O termo "abstraco" em portugus significa que nos podemos abstrair de parte do problema. Um
problema dividido fica mais simples, num caso extremo fica-se com problemas muito pequenos
que so facilmente implementados. precisamente esse caso extremo que se pretende, uma vez
que no s a capacidade do programador limitada, como quanto mais simples um programa
estiver escrito, mais facilmente lido e menos erros ter.
Na programao aplica-se a abstraco s funes, comeando pela funo main, que deve
implementar o problema completo. Esta pode ir sendo desagregada em funes mais simples, at se
obter dimenses de funes razoveis, facilmente implementveis e de leitura simples.
Ao implementar uma funo s interessa saber o que a funo tem de fazer. O resto do cdigo
no tem qualquer relevncia, podemos abstrair-nos dele, nem sequer nos interessa saber onde a
funo ser chamada.
Ao utilizar uma funo s interessa saber o que a funo faz. Como a funo est implementada
no tem qualquer relevncia, podemos abstrair-nos disso, nem sequer interessa saber se a funo
utiliza condicionais ou ciclos, se tem muitas ou poucas linhas de cdigo.
Desta forma, mesmo um programador muito limitado poder implementar um problema muito
complexo, aparentemente fora do alcance de alguns. Na verdade, por vezes acontece
que um programador sem restries, ao fazer as opes na escolha das funes a utilizar
adaptadas para a sua maior capacidade de programao, arrisca-se a ficar com funes com grande

44

Parte II Funes, Vectores e Recurso

nmero de variveis, condicionais e ciclos, levando muito mais tempo a implementar e testar e
ficando o cdigo de menor qualidade que o programador mais limitado. Este, ao no conseguir
implementar funes acima da sua capacidade, procura sempre dividir o problema at que este
tenha uma dimenso apropriada, pelo que encontrar sempre uma tarefa facilitada.
Vamos voltar ao programa que calcula se um ano ou no bissexto, mas utilizando funes (ver
Programa 3-2).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include <stdio.h>
int Bissexto(int ano)
{
return ano%400==0 || ano%4==0 && ano%100!=0;
}
int main()
{
int ano;
printf("Indique ano: ");
scanf("%d", &ano);
/* teste de ano bissexto */
if(Bissexto(ano))
printf("Bissexto");
else
printf("Normal");
}
Programa 5-3 Determina se um ano normal ou bissexto utilizando funes

Este programa manteve o funcionamento igual ao original, mas foi feita uma funo Bissexto para
obter o resultado da expresso lgica que indica se um ano ou no bissexto. Esta funo recebe no
entanto um argumento, a varivel ano. A funo poderia ela prpria pedir o valor do ano, e nesse
caso no necessitava do argumento, mas assim no poderia ser utilizada numa parte do cdigo
onde no tivesse interesse pedir o ano. Com o argumento o cdigo funciona de acordo com o valor
recebido, podendo ser utilizado em maior nmero de situaes.
Os argumentos de uma
funo so colocados entre
os parnteses, tanto na
declarao
como
na
utilizao. Se houver mais
que um argumento, estes
tm de ser separados por
vrgulas, tal como j temos vindo a fazer no printf e no scanf. Na funo Bissexto tem uma
varivel local ano, uma vez que os argumentos de uma funo so tambm variveis locais funo.
Na execuo passo-a-passo
pode-se ver no passo 5 a
declarao e atribuio do
parmetro da funo. Os
parmetros das funes tm
de ser declaradas como
qualquer outra varivel
local.

5. Funes

45

Neste caso pode-se considerar que no houve grande vantagem em fazer a funo Bissexto, no
entanto, atendendo a que h outro projecto dos dias do ms que necessita desta funo, essa
vantagem realmente existe, dado que desta forma possvel passar a funo de um projecto para o
outro sem qualquer alterao da funo, nem ser necessrio rever a implementao do cdigo da
funo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

#include <stdio.h>
int Bissexto(int ano)
{
return ano%400==0 || ano%4==0 && ano%100!=0;
}
int DiasDoMes(int mes, int ano)
{
if(mes==2)
{
/* teste de ano bissexto */
if(Bissexto(ano))
return 29;
else
return 28;
} else if(mes==1 || mes==3 || mes==5 || mes==7 ||
mes==8 || mes==10 || mes==12)
{
return 31;
} else
{
return 30;
}
}
int main()
{
int ano, mes, dias;
printf("Indique ano: ");
scanf("%d", &ano);
printf("Indique mes: ");
scanf("%d", &mes);
printf("%d",DiasDoMes(ano,mes));
}
Programa 5-4 Determina o nmero de dias de um ms/ano utilizando funes

Este programa tem a mesma funcionalidade que o Programa 3-3.


Execues passo-a-passo:

46

Parte II Funes, Vectores e Recurso

Comecemos pela funo main. Esta funo ficou muito curta quando comparado com o cdigo
original, dado que o calculo do dia do ms feito numa funo que retorna o nmero de dias. A
funo main apenas tem de ler os argumentos e chamar a funo respectiva. Para chamar a
funo DiasDoMes no interessa sequer saber que esta utiliza a funo Bissexto.
A funo DiasDoMes tem os testes que estavam na funo main do cdigo original, mas em vez do
condicional mais complexo do ano bissexto, tem uma chamada funo Bissexto. Esta funo tem
dois parmetros, ao contrrio da funo Bissexto. Desta forma move-se complexidade do
problema para outra funo, tornado a leitura mais simples de cada parte do cdigo.
A funo Bissexto foi copiada do outro programa, portanto reutilizada. Quanto mais genrica for a
funo maior o seu potencial para ser til tanto em vrias zonas do cdigo, como em outros
programas. Ao copiar cdigo de um programa para outro pode-se utilizar o cdigo copiado com
alguma garantia, dado que j foi testado e utilizado em outro programa, e provvel que contenha
poucos erros.
Erros comuns mais directamente associados a este captulo:

Funes com parmetros no nome


o Forma: Para distinguir uma constante importante na funo, basta colocar o valor
do parmetro no nome da funo, por exemplo funcao12, para a funo que retorne
o resto da diviso por 12. Desta forma evita-se utilizar um argumento
o Problema: Se distingue uma constante importante no nome da funo, ento deve
colocar a constante como argumento da funo, ficando a funo a funcionar para
qualquer constante. Caso no o faa, corre o risco de ao lado de funcao12, ser
necessrio a funcao4, funcao10, etc., ficando com instrues parecidas. No poupa
sequer na escrita dado que coloca no nome da funo a constante que utiliza na
funo
o Resoluo: Se tem casos destes no seu cdigo, deve fazer uma troca simples:
substituir todas as constantes dependentes de 12 pelo argumento, ou uma
expresso dependente do argumento.
Instrues parecidas no seguidas
o Forma: Quando necessrio uma ou mais instrues que so parecidas com outras
que j escritas noutra parte do cdigo, basta seleccionar e fazer uso de uma das
principais vantagens dos documentos digitais: copy/paste
o Problema: O copy/paste vai dificultar a leitura, e tambm a escrita tem de ser com
muito cuidado, para poder editar as diferenas. Se esta situao ocorrer, uma

5. Funes

47

indicao clara que necessria uma funo com as instrues que so parecidas e
devem ser reutilizadas. Nos argumentos da funo deve ir informao suficiente
para que a funo possa implementar as diferenas
o Resoluo: Se aconteceu no seu cdigo, deve estudar quais as diferentes
alternativas para utilizao de funes que tem, de forma a garantir que no lhe
escapam hipteses antes de optar
Funes especficas
o Forma: Para uma situao necessria uma funo concreta, pelo que essa funo
que definida, utilizando as constantes necessrias dentro dessa funo
o Problema: Se as funes so muito especficas, no podem ser reutilizadas, tanto no
mesmo programa, como em outros programas. Quanto mais genrica a funo,
maior o seu grau de reutilizao, sendo mais simples de manter e detectar algum
problema
o Resoluo: Reveja todas as constantes que tem numa funo especfica, e passe as
constantes que sejam passveis de serem mudadas para argumentos da funo, de
forma a aumentar as possibilidades da funo ser til sem alteraes em outras
situaes
Declaraes de variveis fora do incio das funes
o Forma: As variveis devem ser declaradas o mais perto possvel do local onde so
utilizadas, e como alguns compiladores de C permitem a declarao fora do incio
das funes, h que aproveitar
o Problema: verdadeira a frase, mas no para quem se inicie na programao, nem
para a linguagem C. A generalidade dos compiladores de C permite declarao das
variveis em qualquer parte do cdigo, mas nem todos, pelo que perde
compatibilidade. Mais relevante a m influncia que tal situao ter, facilitando a
existncia de funes grandes. Com as variveis declaradas em cada bloco, no ir
sofrer os efeitos de funes grandes to intensamente, o que o pode levar a no
sentir a necessidade de criar funes
o Resoluo: Todas as declaraes fora do incio das funes passam para o incio das
funes. Reveja os nomes das variveis se necessrio

O Programa 5-1 tem um exemplo de um erro de instrues parecidas no seguidas, de forma a


motivar a utilizao de funes. Um exemplo de funes com parmetros no nome no Programa
5-4 existir uma funo DiasDoMes2010, que recebe um ms e retorna o nmero de dias desse ms
em 2010. Esta verso tambm um exemplo de uma funo especfica, dado que contm uma
constante que poderia estar num argumento, mas a situao da constante ir para o nome da funo
de maior gravidade. A existncia de uma constante interna a uma funo, justifica-se se o seu
valor raramente fizer sentido ser distinto do utilizado na implementao.
Estes exemplos so muito curtos, mas num conceito to importante quanto este, tem que se dar
tempo para que entre devagar e seja compreendido parte da sua potencialidade. Com a experincia
ficar claro quais as funes que deve criar, de forma a obter cdigo de qualidade, modular,
reutilizvel, e de simples leitura. Por agora, duas caractersticas de uma boa funo:

Uma funo, uma operao (no juntar alhos com bugalhos);


Se no tem uma funcionalidade ou nome claro, ento no uma funo.

48

Parte II Funes, Vectores e Recurso

Reveja cdigo que tenha feito no mdulo anterior, e faa uma verso modular de cada um,
utilizando funes.
Da boa diviso do problema em funes resultar o menor trabalho necessrio para implement-lo,
enquanto uma m diviso resulta no a inviabilizao da implementao, mas sim um aumento de
trabalho necessrio. Quanto mais trabalho, provavelmente menor qualidade ter o cdigo.

6. Mais Ciclos e Condicionais

49

6. MAIS CICLOS E CONDICIONAIS


Este captulo comea em "mais", porque na verdade no introduz novos conceitos, mas sim
apresenta alternativas que a linguagem C oferece para aplicar esses conceitos: ciclos e condicionais.
A primeira situao refere-se constatao que a maior parte dos ciclos tem uma varivel
iteradora, que controla o teste de paragem do ciclo. Essa varivel iteradora tem de ser inicializada
antes do ciclo, e incrementada no final do ciclo, de forma a ficar com o valor vlido quando a
expresso lgica avaliada. Exemplo disso o primeiro programa no captulo Ciclos.
1
2
3
4
5
6
7
8
9
10
11
12

#include <stdio.h>
int main()
{
int soma=0, i=1;
while(i<=4)
{
soma+=i*i;
i++;
}
printf("Soma dos primeiros 4 quadrados: %d", soma);
}
Programa 6-1 Rplica do Programa 4-2

Note-se na atribuio da varivel i ao valor 1 antes do ciclo, e da operao de incremento no


final. No entanto a operao que interessa fazer a operao de soma+=i*i;. Esta situao
prejudica a legibilidade do cdigo porque h duas instrues de controlo do ciclo, uma antes do
ciclo e outra no ciclo, que nada tm a ver com o que se pretende fazer, apenas esto l para
controlar a paragem do ciclo, perturbando a leitura.
A linguagem C permite efectuar ciclos iterativos juntando a inicializao e actualizao de variveis,
junto com o teste de paragem, podendo ficar tudo numa s linha, de forma a deixar as restantes
linhas para instrues no relacionadas com o controlo do ciclo.
1
2
3
4
5
6
7
8
9
10
11

#include <stdio.h>
int main()
{
int soma=0, i;
for(i=1; i<=4; i++)
soma+=i*i;
printf("Soma dos primeiros 4 quadrados: %d", soma);
}
Programa 6-2 Soma dos primeiros 4 quadrados naturais com o ciclo for

O cdigo acima tem a mesma funcionalidade que o original, mas todas as instrues de controlo do
ciclo na linha for. O ciclo for, ao contrrio do ciclo while, aceita trs parmetros entre parnteses,
mas separados por ponto e vrgulas. Na primeira zona est a inicializao, na zona central est a
expresso lgica tal como no while, e na ltima zona est a actualizao da varivel iteradora.
Pode-se ver que na execuo passo-a-passo, o ciclo for fica sempre em dois passos. Na primeira
passagem, tem que se executar a inicializao, e num segundo passo mantendo na mesma linha, tem
que se avaliar a expresso lgica. Nas restantes passagens, tem que se executar a actualizao, e de

50

Parte II Funes, Vectores e Recurso

seguida a avaliao. Para no haver confuso


entre a zona do ciclo for que est a ser
executada num dado passo, as zonas no
executadas so substitudas por um cardinal
(#).
Tm que existir sempre trs parmetros no
ciclo for, mesmo que um campo seja vazio,
tem que se colocar o ponto e vrgula. Se houver
lugar a mais que uma instruo de
inicializao, estas devem estar separadas por
uma vrgula, acontecendo o mesmo para a
instruo de actualizao. Por exemplo, pode-se querer inicializar o valor da varivel soma no ciclo.
1
2
3
4
5
6
7
8
9
10
11

#include <stdio.h>
int main()
{
int soma, i;
for(i=1, soma=0; i<=4; i++)
soma+=i*i;
printf("Soma dos primeiros 4 quadrados: %d", soma);
}
Programa 6-3 Utilizao alternativa do ciclo for na soma dos 4 quadrados naturais

Este programa idntico ao anterior, mas no se aconselha efectuar inicializaes nem


actualizaes no ciclo for, que no digam respeito varivel iteradora e ao controle do ciclo. Caso
contrrio o cdigo torna-se de mais leitura pesada. Em vez de se esperar instrues menores de
controlo de ciclo, dentro do ciclo for passa a poder estar apenas instrues com significado mais
relevante para o algoritmo a implementar.
Quantos mais ciclos, maior so as vantagens do ciclo for relativamente ao ciclo while, como se
pode ver na verso mais simples do Programa 4-4.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include <stdio.h>
int main()
{
int A,B,K,contagem;
printf("Indique K: ");
scanf("%d",&K);
contagem=0;
for(A=1; A<=K; A++)
for(B=1; B<=K; B++)
if(A+B<=K && A*B<=K)
contagem++;
printf("Total: %d",contagem);
}
Programa 6-4 Verso com ciclos for do Programa 4-4

Nesta verso do cdigo observa-se uma clara diminuio do nmero de instrues e aumento de
legibilidade, dado que tudo o que trata de cada ciclo est numa s linha, e no em diversas linhas e
alternado linhas de controlo de um ciclo com as do outro.

6. Mais Ciclos e Condicionais

51

Mostra-se a execuo passo-a-passo dos


primeiros 20 passos. Esta execuo,
quando comparada com a verso
utilizando while, pode-se verificar que
no h diferena excepto na troca do
for pelo while.

Para que no exista qualquer dvida, o ciclo for genrico:


for(inicializao; expresso lgica; actualizao)
instruo;

equivalente ao ciclo while genrico:


inicializao;
inicializao;
while(expresso lgica)
{
instruo;
actualizao;
}

Se a expresso lgica for falsa logo no primeiro ciclo, as instrues do ciclo nunca so executadas,
quer se utilize o ciclo for ou o ciclo while. Pode-se sempre alterar a expresso lgica para que seja
verdadeira no primeiro ciclo, mas complicando a expresso, pelo que a linguagem C tem uma outra
forma de executar ciclos, o dodo-while:
do
{

instruo;
} while(expresso lgica);
lgica);

Exemplifica-se o ciclo do-while com o exerccio do menu em Funes, que pode ser reescrito da
seguinte forma:

52

Parte II Funes, Vectores e Recurso


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#include <stdio.h>
int Menu()
{
int opcao;
/* mostrar as opes do menu */
printf("\nMenu:\n1 - opcao A\n2 - opcao B\n3 - opcao C\n0 - sair");
printf("\nOpcao: ");
scanf("%d",&opcao);
/* retornar a opo seleccionada */
return opcao;
}
int main()
{
int opcao;
do
{
opcao=Menu();
/* ver qual a opo */
if(opcao==1)
printf("Opcao escolhida A");
else if(opcao==2)
printf("Opcao escolhida B");
else if(opcao==3)
printf("Opcao escolhida C");
else if(opcao!=0)
printf("Opcao invalida");
} while(opcao>0);
printf("Fim do programa.");
}
Programa 6-5 Verso com ciclos do-while do Programa 5-2

Mostra-se a execuo passo-a-passo dos


primeiros 20 passos. Este programa tem o mesmo
funcionamento que o original, mas com a
utilizao do ciclo do-while possvel chamar
apenas uma s vez a funo Menu. Com esta
soluo pode-se ponderar a necessidade de uma
funo para o menu, uma vez que a funo
chamada apenas uma s vez, e tem uma dimenso
reduzida.

6. Mais Ciclos e Condicionais


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#include <stdio.h>
int main()
{
int opcao;
do
{
/* mostrar as opes do menu */
printf("\nMenu:\n1 - opcao A\n2 - opcao B\n3 - opcao C\n0 - sair");
printf("nOpcao: ");
scanf("%d",&opcao);
/* ver qual a opo */
if(opcao==1)
printf("Opcao escolhida A");
else if(opcao==2)
printf("Opcao escolhida B");
else if(opcao==3)
printf("Opcao escolhida C");
else if(opcao!=0)
printf("Opcao invalida");
} while(opo>0);
printf("Fim do programa.");
}
Programa 6-6 Verso removendo a funo Menu do Programa 6-5

Os ciclos while e do-while devem ser utilizados nos ciclos


menos ortodoxos quando no h uma varivel iteradora
clara, ou a expresso lgica muito complexa, de forma a no
ter numa s linha do ciclo for muito longa. Nos ciclos
normais, a opo pelo ciclo for resulta quase sempre em
ganho de legibilidade.
Os condicionais tm tambm uma alternativa cadeia ifelse utilizada no programa acima. Em situaes em que as
expresses lgicas na cadeia if-else so comparaes com
valores concretos, como o caso do exemplo do menu, podese utilizar o switch, que recebe uma varivel e lista-se todos
os casos.

53

54

Parte II Funes, Vectores e Recurso


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#include <stdio.h>
int main()
{
int opcao;
do
{
/* mostrar as opes do menu */
printf("\nMenu:\n1 - opcao A\n2 - opcao B\n3 - opcao C\n0 - sair");
printf("\nOpcao: ");
scanf("%d",&opcao);
/* ver qual a opo */
switch(opo)
{
case 1:
printf("Opcao escolhida A");
break;
case 2:
printf("Opcao escolhida B");
break;
case 3:
printf("Opcao escolhida C");
break;
case 0:
break;
default:
printf("Opcao invalida");
}
} while(opcao>0);
printf("Fim do programa.");
}
Programa 6-7 Verso com switch do Programa 6-6

O switch tem de ter o valor de uma expresso numrica


como base, e no uma expresso lgica, sendo colocada a
execuo na linha de cdigo que tiver o caso que ocorreu
(case valor:). Se o valor no existir em nenhum dos
casos listados, a execuo passa para a linha onde estiver
"default:" se existir. Com estas regras, poderia haver
problema em utilizar o switch no fosse a existncia dos
comandos break; que saem nesse instante do comando
switch. Se no fosse assim, no caso da opo 1 ser escolhida,
o programa passaria para a linha do caso 1, e de seguida
seguiria normalmente para o caso 2, 3 e a opo invlida, s
porque esto nas linhas de cdigo aps a opo 1. Colocando
um break; aps o fim do cdigo que trata cada opo,
resolve-se o problema.
Pode-se ver na execuo passo-a-passo que nos passos 5, 13
e 20, o switch passou imediatamente para a linha que trata
o caso pretendido, em vez de executar vrios condicionais.
Num outro exemplo podemos ver uma implementao alternativa da funo DiasDoMes, extrada
do Programa 5-4:

6. Mais Ciclos e Condicionais

55

1 int DiasDoMes(int mes, int ano)


2 {
3
switch(mes)
4
{
5
case 2:
6
/* teste de ano bissexto */
7
if(Bissexto(ano))
8
return 29;
9
else
10
return 28;
11
/* meses de dias 31 */
12
case 1:
13
case 3:
14
case 5:
15
case 7:
16
case 8:
17
case 10:
18
case 12:
19
return 31;
20
default:
21
return 30;
22
}
23 }
Programa 6-8 Funo DiasDoMes com switch do Programa 5-4

Neste caso pretende-se identificar todos os casos de meses de 31, pelo que basta colocar os
diferentes casos em cada linha, e retornar o valor correcto apenas aps a linha que identifica o
ltimo ms de 31 dias. Como aqui utilizam-se comandos return, que saem imediatamente da
funo, no h necessidade de utilizar a instruo break; no switch.
O comando break;
break; pode ser utilizado tambm em ciclos, para sair do ciclo actual. Existe ainda o
comando continue;
continue; utilizvel dentro de ciclos, que permite saltar para a prxima iterao do ciclo.
Estes comandos no se devem no entanto utilizar com frequncia, dado que dessa forma as
expresses lgicas, inicializaes e actualizaes associados ao ciclo, no controlam completamente
o ciclo, sendo auxiliadas por eventuais condicionais dentro do ciclo, sobre a qual os comandos
break; e continue; esto. Sendo de evitar a utilizao destes comandos, no ser dado nenhum
exemplo em que tais comandos sejam teis.
Erros comuns mais directamente associados a este captulo:

Ciclos contendo apenas uma varivel lgica no teste de paragem


o Forma: Esta estratgia consiste em utilizar nos ciclos (normalmente while), uma
varivel no teste de paragem, atribuindo o valor verdadeiro e dentro do ciclo,
quando for necessrio sair dele, fazer uma atribuio varivel a falso
o Problema: O real teste de paragem fica diludo nas instrues que alteram esta
varivel, bem como condicionais que utilizem o break, e ter algo em vez de num
local em vrios, torna sempre mais complicada a leitura e manuteno de cdigo.
Esta situao ocorre porque falhou na identificao do real teste de paragem. pior
se necessitar de acompanhar esta varivel com uma outra varivel que altera de
valor em cada ciclo (uma varivel iteradora)
o Resoluo: Se optar com frequncia para esta situao, deve rever o seu cdigo
procurando alternativas

Um exemplo simples deste erro a seguinte verso do Programa 4-2:

56

Parte II Funes, Vectores e Recurso


1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include <stdio.h>
int main()
{
int soma=0, i=1, continua=1;
while(continua)
{
soma+=i*i;
i++;
if(i>4)
continua=0;
}
printf("Soma dos primeiros 4 quadrados: %d", soma);
}

A varivel lgica continua pode ser actualizada em qualquer parte do ciclo, sendo a verdadeira
condio de paragem distribuda pelos condicionais associados sua alterao. Este exemplo to
pequeno que no d para dividir a condio de paragem, apenas desloc-la do ciclo while para o
final do ciclo, mas mesmo assim pode-se verificar a degradao da clareza do cdigo. Uma outra
verso a evitar teria sido em vez da varivel continua utilizar-se a constante 1 de forma a obter-se
um ciclo infinito, utilizando-se a instruo break; em vez de continua=0; para sair do ciclo.
Todos os ciclos tm a mesma complexidade, pode-se trocar um ciclo por outro, no entanto com o
ciclo for o cdigo fica normalmente melhor arrumado. A utilizao do switch normalmente no
melhora a leitura do cdigo, mas sempre uma opo em relao ao if. Agora que conhece todas as
alternativas, faa uma reviso aos exerccios que fez e refaa os ciclos e condicionais de forma a
melhorar a simplicidade e leitura do seu cdigo.

7. Vectores

57

7. VECTORES
Nos programas tratados at este momento, foram utilizadas variveis de diferentes tipos, mas
nunca se abordou conjuntos de variveis.
Suponhamos que estamos interessados em registar um valor por cada dia da semana. A natureza
dos valores no relevante, tanto pode ser o nmero de emails enviados, como o nmero de cafs
tomados, o que interessa que se pretende guardar um valor por cada dia da semana, e no final
obter a soma e a mdia dos valores introduzidos.
Com o que sabemos, o seguinte programa resolveria o problema:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

#include <stdio.h>
int main()
{
int segunda, terca, quarta, quinta, sexta, sabado, domingo;
int total;
/* introduo de valores */
printf("Segunda: ");
scanf("%d",&segunda);
printf("Terca: ");
scanf("%d",&terca);
printf("Quarta: ");
scanf("%d",&quarta);
printf("Quinta: ");
scanf("%d",&quinta);
printf("Sexta: ");
scanf("%d",&sexta);
printf("Sabado: ");
scanf("%d",&sabado);
printf("Domingo: ");
scanf("%d",&domingo);
/* calculos */
total=segunda+terca+quarta+quinta+sexta+sabado+domingo;
printf("Soma: %d\n",total);
printf("Media: %f\n",total/7.0);
}
Programa 7-1 Calculo do total e mdia de um indicador por cada dia da semana

Execuo do programa:
C:\
C:\>semana
Segunda: 2
Terca: 3
Quarta: 1
Quinta: 2
Sexta: 3
Sabado: 4
Domingo: 1
Soma: 16
Media:
2.285714

Se necessitamos de um
valor por cada dia da
semana, temos de criar
uma varivel por cada dia
da semana. Neste caso a

58

Parte II Funes, Vectores e Recurso

operao pedida muito simples, a varivel total pode ir sendo actualizada medida que se
introduz os valores, podendo-se evitar ter criado uma varivel por cada dia. Mas apenas um
exemplo que pretende ser o mais simples possvel. Se quiser calcular a varincia, tem-se de fazer
outra passagem pelos valores da semana aps obter a mdia, sendo nesse caso essencial que os
valores fiquem em memria.
Como temos variveis distintas, no podemos fazer um ciclo para fazer a operao de soma, por
exemplo. Evidentemente que esta opo no seria prtica se em vez de termos 7 variveis, fossem
necessrias 31 para cada dia do ms, ou 365 para cada dia do ano.
A soluo poder criar com um s nome, um conjunto de variveis, quantas forem necessrias, e
ter uma forma de acesso atravs de um ndice.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include <stdio.h>
int main()
{
int diasDaSemana[7];
int total=0,i;
/* introduo de valores */
printf("Segunda: ");
scanf("%d",&diasDaSemana[0]);
printf("Terca: ");
scanf("%d",&diasDaSemana[1]);
printf("Quarta: ");
scanf("%d",&diasDaSemana[2]);
printf("Quinta: ");
scanf("%d",&diasDaSemana[3]);
printf("Sexta: ");
scanf("%d",&diasDaSemana[4]);
printf("Sabado: ");
scanf("%d",&diasDaSemana[5]);
printf("Domingo: ");
scanf("%d",&diasDaSemana[6]);
/* calculos */
for(i=0;i<7;i++)
total+=diasDaSemana[i];
printf("Soma: %d\n",total);
printf("Media: %f\n",total/7.0);
}
Programa 7-2 Calculo do total e mdia, verso com vectores

Note-se na declarao da
varivel na linha 5. Alm do
nome, tem entre parntesis
rectos, o nmero 7. Significa isto
que a declarao no se refere a
uma s varivel inteira, mas sim
a um conjunto de 7 variveis,
um vector com 7 elementos.
Em vez de int poderia estar
qualquer outro tipo, no h
diferena nenhuma, podem-se
declarar vectores de qualquer
tipo.

7. Vectores

59

Como aceder a cada uma das 7 variveis? Simplesmente utilizando tambm entre parntesis rectos,
o ndice da varivel, de 0 a 6. isso que feito na introduo dos valores, e que permite na linha 25
e 26 construir um ciclo que processa todos os dias da semana.
Se em vez de 7 fossem 31, no
havia problema, bastaria alterar
a constante 7 para 31. No h
problema excepto na introduo
de dados, que agora teria de ser
copiada e estendida para
introduzir 31 valores em vez de
7. Ser possvel colocar tambm
esta zona de baixo de um ciclo?
Sim, desde que se tenha as
strings tambm debaixo de um
vector. Mas afinal qual o tipo
associado string?
Uma string um conjunto de
caracteres. Acabmos de fazer
um conjunto de 7 valores inteiros, com um vector, e sabemos como criar variveis que sejam
caracteres. O que obtemos se declararmos simplesmente um vector de caracteres?
char str[10];

A resposta simples, uma string. A declarao da varivel str acima como sendo um vector de 10
caracteres, uma string. Na primeira posio str[0] est o primeiro carcter da string, na
segunda posio str[1] est o segundo carcter, e assim sucessivamente.
No entanto h aqui um problema. Se der a varivel str a uma funo, tem de passar tambm o
tamanho da string, caso contrrio se estivesse a processar a string, haveria o risco de aceder fora
dela, em str[10], str[11], etc.
Na declarao dos vectores na linguagem C, o espao em memria para guardar as 10 variveis
pedidas reservado, e apenas isso, no guardado nenhum registo do tamanho do vector. Nas
instrues de acesso aos elementos, se existir um acesso a elementos fora da dimenso alocada vaise estar a aceder a espao de memria que poder estar a ser utilizado para outras variveis,
provavelmente as variveis que esto declaradas a seguir, e que podem at ser de outro tipo. da
responsabilidade do programador garantir que isto no acontea.
Para as strings, de forma a evitar andar a guardar o nmero de caracteres, e tambm por outros
motivos, convencionou-se que o ltimo elemento da string o carcter 0. Esse carcter no faz
parte da string, apenas indica que a string acabou. Portanto na declarao conveniente alocar
espao suficiente para guardar todos os caracteres da string, mais um que o carcter terminador.

60

Parte II Funes, Vectores e Recurso


1
2
3
4
5
6
7
8
9
10
11
12
13

#include <stdio.h>
int main()
{
char str[10];
int i;
printf("Introduza uma string: ");
gets(str);
for(i=0;str[i]!=0;i++)
printf("\nCaracter %d: %c",i,str[i]);
}
Programa 7-3 Confirmao que uma string um vector de caracteres

Execuo do programa:
C:\>string
Introduza uma string: asdf
Caracter
Caracter
Caracter
Caracter

0:
1:
2:
3:

a
s
d
f

Pode-se verificar que condio para continuar


no ciclo que carcter actual no seja igual a
zero. Veja este exemplo com ateno para
compreender como a string introduzida na
verdade realmente apenas um vector do tipo
char, com a particularidade de acabar em 0.
Sem esta particularidade seria necessrio ter
outra varivel com a dimenso da string
introduzida. Repare que o tamanho da string na declarao uma constante do programa,
enquanto o tamanho da string introduzida foi apenas de 4 caracteres.
No exemplo acima a varivel str, que tem no um mas 10 caracteres, foi utilizada sem parnteses
rectos na linha 9. Na verdade, enquanto a varivel str[2] do tipo char, a varivel str do
tipo char[], ou char*, e representa um vector (a dimenso no relevante). O que a funo gets
recebe portanto o vector e no um s carcter. Atendendo a que este tipo no mais que o
endereo de memria onde os caracteres esto alojados, tambm chamado de apontador. So os
apontadores e no cada elemento do vector que passado para as funes, quando se utiliza
vectores. Vamos fazer uma funo strlen que retorna o tamanho da string, que faz parte da
biblioteca string.h, mas vamos implement-la para ilustrar a passagem de vectores para dentro
das funes.

7. Vectores
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

61

#include <stdio.h>
int strlen(char str[])
{
int i;
for(i=0;str[i]!=0;i++);
return i;
}
int main()
{
char str[10];
printf("Introduza uma string: ");
gets(str);
printf("A string '%s', tem %d caracteres.",str,strlen(str));
}
Programa 7-4 Calcular o tamanho de uma string

Execuo do programa:
C:\>strlen
strlen
Introduza uma string: sdf sd
A string 'sdf sd', tem 6 caracteres.

A passagem da string na linha 15, foi igual


passagem da string na linha 14, e na declarao
da string na funo strlen na linha 3, utilizouse o tipo char[] (em alternativa pode-se
utilizar o tipo char*). Na linha 3 no se indicou a
dimenso do vector, dado que essa no
conhecida, apenas que um vector de caracteres.
Para a funo passado apenas o endereo do
vector, mas com isso a funo consegue aceder a
todos os elementos do vector, como se pode ver
na linha 6.
Na execuo passo-a-passo est apenas a
chamada funo strlen. O argumento passado
apenas a referncia ao vector, e no o contedo
do vector, neste caso 7 caracteres. Os valores
acedidos por str[i] so os caracteres que esto declarados na funo main, e no uma cpia.
Falta agora responder a uma questo. Como inicializar os valores num vector? Para as variveis
basta colocar uma atribuio na declarao, mas para os vectores tem-se de colocar uma atribuio
para vrios valores. isso mesmo que feito, como podemos ver neste exemplo da funo dos
DiasDoMes.

62

Parte II Funes, Vectores e Recurso

int DiasDoMes(int mes, int ano)


{
int diasDoMes[] =
{
31,28,31,30,31,30, /* Janeiro a Junho */
31,31,30,31,30,31 /* Julho a Dezembro */
};
if(mes==2 && Bissexto(ano))
return 29;
return diasDoMes[mes-1];
}
Programa 7-5 Funo DiasDoMes com um vector inicializado na declarao

Nesta implementao do DiasDoMes, o nmero de dias que cada ms tem colocado na


inicializao do vector. Neste caso at a dimenso do vector deixada em aberto, mas poderia
estar l o 12. Como no est no vector fica com o tamanho da lista indicada na atribuio. Ao colocar
os valores em memria, deixam de ser necessrios os condicionais utilizados anteriormente, e
suficiente retornar o valor com o ndice correspondente. Notar que tem que se subtrair por 1, uma
vez que o primeiro ms o 1, enquanto o primeiro ndice do vector o 0.
Agora como que so inicializadas as strings? Ser que as strings no printf so diferentes das
que falamos aqui, j que so delimitadas por aspas? Vamos ver as respostas no resultado do
seguinte programa:
1
2
3
4
5
6
7
8
9
10

#include <stdio.h>
int main()
{
char str1[]="ABC";
char str2[]={'A','B','C','\0'};
char str3[]={65,66,67,0};
printf("str1: %s\nstr2: %s\nstr3: %s",str1,str2,str3);
}
Programa 7-6 Exemplificao das diferentes maneiras de se inicializar strings

Execuo do programa:
C:\>string2
str1: ABC
str2: ABC
str3: ABC

As trs strings so declaradas e inicializadas de maneiras distintas, no entanto ficam exactamente


com o mesmo valor, como se pode confirmar na execuo do programa. A string str1 utiliza o
formato que temos vindo a utilizar no printf, ou seja, delimitar as strings com aspas. A string
entre aspas convertida para algo idntico verso 2, ou seja, uma lista como no exemplo anterior,
mas em vez de nmeros tem letras, qual se junta um ltimo carcter com o valor zero. A verso 3,
segue o exemplo anterior, considerando os caracteres como nmeros, fazendo uso do
conhecimento do nmero do carcter A o 65, e como as letras so nmeros, os trs formatos so
exactamente iguais. A string tem 3 caracteres, e ocupa 4 bytes em qualquer dos casos.
Estamos agora em condies de resolver o problema inicial de forma mais adequada, ou seja,
colocar em ciclo a entrada de dados. Para tal basta colocar num vector as strings a utilizar no
printf.

7. Vectores
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

#include <stdio.h>
int main()
{
int diasDaSemana[7];
int total=0,i;
char str[10];
char prompt[7][] = {
"Segunda: ",
"Terca: ",
"Quarta: ",
"Quinta: ",
"Sexta: ",
"Sabado: ",
"Domingo: "
};
/* introduo de valores */
for(i=0;i<7;i++)
{
printf(prompt[i]);
scanf("%d",&diasDaSemana[i]);
}
/* calculos */
for(i=0;i<7;i++)
total+=diasDaSemana[i];
printf("Soma: %d\n",total);
printf("Media: %f\n",total/7.0);
}
Programa 7-7 Calculo do total e mdia, sem cdigo repetido, utilizando vectores

Ao colocar a informao
varivel na inicializao
da varivel prompt, foi
possvel
colocar
o
cdigo genrico junto,
permitindo
assim
executar ciclos onde
estava cdigo duplicado
com ligeiras diferenas.
Assim ser mais fcil
alterar o cdigo para
funcionar de forma
idntica
mas
com
outros dados, uma vez
que se separou a parte
genrica
da
parte
especfica.
Execuo passo-a-passo
dos primeiros e ltimos
passos.
A varivel prompt na
verdade um conjunto
de
conjuntos
de
caracteres, o que o

63

64

Parte II Funes, Vectores e Recurso

mesmo que dizer que um conjunto de strings. Um exemplo de uma varivel muito parecida com
a prompt o argumento argv que a funo main pode ter, e pretende precisamente ter tambm
um conjunto de strings que foram colocadas como argumentos do programa.
1
2
3
4
5
6

#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Argumentos: %d\nArgumento 0: %s",argc,argv[0]);
}
Programa 7-8 Leitura de argumentos passados ao programa

Execuo do programa:
C:\>arg sdfsd
Argumentos: 2
Argumento 0: arg

O programa imprime o primeiro argumento, que o nome do prprio programa. A declarao


de argv no tem dois pares de parnteses rectos vazios, dado que apenas pode haver um, que o
ltimo, embora alguns compiladores o permitam mais que um, como o caso do TCC. A alternativa
aos parnteses rectos vazios o asterisco antes. Outra declarao vlida seria char **argv.
Nos restantes tipos no h problema em utilizar vectores de vectores, portanto matrizes, ou
vectores de vectores de vectores. normal referir-se a estes tipos como sendo vectores multidimensionais. Na linguagem C no h limite de dimenses pr-definido, mas no se aconselha a
utilizar mais que 3 dimenses num vector. Vamos dar um exemplo da soma de duas matrizes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#include <stdio.h>
#define SIZE 10
int main()
{
int matriz1[SIZE][SIZE];
int matriz2[SIZE][SIZE];
int matrizSoma[SIZE][SIZE];
int i,j;
/* inicializar matrizes */
for(i=0;i<SIZE;i++)
for(j=0;j<SIZE;j++)
{
matriz1[i][j]=i+j;
matriz2[i][j]=i*j;
}
/* somar matrizes */
for(i=0;i<SIZE;i++)
for(j=0;j<SIZE;j++)
matrizSoma[i][j]=matriz1[i][j]+matriz2[i][j];
/* mostrar resultado */
for(i=0;i<SIZE;i++)
{
/* mudar de linha */
printf("\n");
for(j=0;j<SIZE;j++)
printf("%3d ",matrizSoma[i][j]);
}
}
Programa 7-9 Soma de duas matrizes

7. Vectores

65

Execuo do programa:
C:\>matriz
0
1
2
3
4
5
6
7
8
9
1
3
5
7
9 11 13 15 17 19
2
5
8 11 14 17 20 23 26 29
3
7 11 15 19 23 27 31 35 39
4
9 14 19 24 29 34 39 44 49
5 11 17 23 29 35 41 47 53 59
6 13 20 27 34 41 48 55 62 69
7 15 23 31 39 47 55 63 71 79
8 17 26 35 44 53 62 71 80 89
9 19 29 39 49 59 69 79 89 99
O acesso a um valor da matriz (vector de vectores) idntico ao dos vectores, mas em vez de
um ndice tem que se fornecer dois, um para indicar o vector, e outro para indicar o elemento

dentro desse vector.


No printf foi utilizado %3d e no %d, uma vez que se pretende que os nmeros da matriz estejam
alinhados, sendo importante que todos os nmeros ocupem o mesmo espao. Para indicar o
nmero de caracteres a
utilizar num nmero
inteiro, pode-se colocar
esse valor entre a % e o d.
Este exemplo comea com
uma instruo de prprocessador ainda no
conhecida, o #define.
Estas instrues no so
para
o
computador
executar, mas sim para o
compilador na altura em
que est a processar o
cdigo do programa. A
instruo uma macro
que indica que deve ser
substitudo o texto "SIZE"
pelo valor "10" em todo o
cdigo onde o texto
"SIZE"
ocorra.
Desta
forma possvel alterar
essa constante num s
local, e essas alteraes
reflectirem-se em todo o
cdigo. o que se vai
fazer
para
poder
apresentar uma execuo
passo-a-passo para este programa, caso contrrio torna-se demasiado longo, em que foi alterado
para #define SIZE 2.

66

Parte II Funes, Vectores e Recurso

O valor mximo das


dimenses dos vectores,
strings, matrizes, normal
definir-se desta forma,
atravs de macros. Isto
porque no podem ser
utilizadas variveis nas
dimenses dos vectores,
at porque as declaraes
tm de estar no incio das
funes, e nessa altura
pode no estar ainda
calculada a dimenso que
necessria. Tem que se
utilizar um valor maior
que o necessrio, e se
houver problemas, alterase a macro e fica todo o
cdigo actualizado. No
mdulo 3 vamos saber
como alocar memria
indicando a dimenso
necessria em tempo de
execuo.
Erros comuns mais directamente associados a este captulo:

Variveis com nomes quase iguais


o Forma: Algumas variveis necessrias tm na verdade o mesmo nome, pelo que se
utiliza os nomes nomeA, nomeB, nomeC, nomeD, ou nome1, nome2, nome3, nome4
o Problema: Esta situao uma indicao clara que necessita de um vector nome[4].
Se no utilizar o vector no possvel depois fazer alguns ciclos a iterar pelas
variveis, e iro aparecer forosamente instrues idnticas repetidas por cada
varivel
o Resoluo: Deve nesta situao procurar alternativas de fazer cdigo em que esta
situao lhe tenha ocorrido. No utilize cdigo de outra pessoa, apenas o seu cdigo
com este tipo de erro lhe poder ser til
M utilizao do switch ou cadeia if-else
o Forma: De forma a executar instrues dependente do valor de um ndice, utilizar
um switch colocando as diversas instrues para cada caso do ndice
o Problema: Muitas vezes ao utilizar no switch um ndice (ou cadeia if-else), as
instrues em cada caso ficam muito parecidas, ficando com cdigo longo e de custo
de manuteno elevado. Se essa situao ocorrer deve rever o seu cdigo, dado que
est provavelmente a falhar uma alternativa mais potente, que a utilizao de
vectores
o Resoluo: Criar um vector com as constantes necessrias para chamar numa s
instruo todas as outras dependentes do ndice

7. Vectores

67

Macros em todas as constantes


o Forma: Tudo o que constante tem direito a uma macro, de forma a poder ser
facilmente alterada
o Problema: A utilizao de macros tem um custo, que incorre nas linhas de cdigo
onde utilizada: no conhecido o valor real da macro. Se esta for utilizada em
situaes em que o fluxo de informao seja claro, como um valor inteiro limite
sendo os ciclos desde uma constante at esse limite, no h qualquer problema. Se
no entanto for utilizada em locais dependentes de outras constantes, sejam em
macros sejam no cdigo, ou que seja importante saber o valor da macro para saber
qual a sequncia de instrues, ou num caso extremo, o valor da macro necessrio
para saber se uma instruo vlida, evidentemente que o valor da macro em si no
pode ser alterado sem que o cdigo seja revisto. Perde portanto o sentido, uma vez
que prejudica a leitura do cdigo e no facilita qualquer posterior alterao. As
macros devem ser independentes entre si e de outras constantes
o Resoluo: Remova do cdigo as macros que so dependentes entre si, ou que
dificilmente podem mudar sem implicar outras alteraes no cdigo

O exemplo do Programa 7-1 para motivar a utilizao de vectores um exemplo da ocorrncia do


primeiro erro, variveis com nomes quase iguais, neste caso relacionados. Por vezes a evidncia
superior, sendo utilizadas vrias variveis do mesmo tipo e com o mesmo prefixo. As verses da
funo DiasDoMes anteriores apresentada no Programa 7-5 foram utilizadas para introduzir
diversos conceitos, mas so na verdade ocorrncias do erro da m utilizao do switch ou cadeia
if-else. Tm no entanto a atenuante de resultar em apenas 3 situaes, e no um valor distinto
por cada dia do ms. Sobre o erro das macros, vamos dar uma verso do Programa 7-9 com um
nmero exagerado de erros deste tipo para melhor ilustrar o custo da utilizao das macros.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

#include <stdio.h>
#define SIZE 10
#define INICIO 0
#define NOVALINHA "\n"
#define IMPRIMEVALOR "%3d "
#define OPERACAO1 i+j;
#define OPERACAO2 i*j;
#define OPERACAO3 matriz1[i][j]+matriz2[i][j];
#define ACESSO(a,b) matrizSoma[a][b]
#define ITERAR_i for(i=INICIO;i<SIZE;i++)
#define ITERAR_j for(j=INICIO;j<SIZE;j++)
#define ITERAR_ij ITERAR_i ITERAR_j
int main()
{
int matriz1[SIZE][SIZE];
int matriz2[SIZE][SIZE];
int matrizSoma[SIZE][SIZE];
int i,j;
/* inicializar matrizes */
ITERAR_ij
{
matriz1[i][j]=OPERACAO1
matriz2[i][j]=OPERACAO2
}
/* somar matrizes */
ITERAR_ij
matrizSoma[i][j]=OPERACAO3
/* mostrar resultado */
ITERAR_i

68

Parte II Funes, Vectores e Recurso


34
35
36
37
38
39
40 }

{
/* mudar de linha */
printf(NOVALINHA);
ITERAR_j
printf(IMPRIMEVALOR,ACESSO(i,j));
}

As macros esto definidas no incio do programa, e por ordem de gravidade de erro. A primeira,
SIZE 10, aconselhada, resulta em apenas vantagens dado que a constante ao ser alterada no
influencia em nada o bom funcionamento do programa. A segunda macro, INICIO 0, cai no erro
dado que no pode e no faz sentido ter um valor distinto do zero, o primeiro ndice de um vector
o zero, faz parte da linguagem de programao. Utilizar esta macro apenas d flexibilidade para
cometer um erro, e ao ler o cdigo no se percebe no local que o processamento desde o incio da
matriz.
As duas macros NOVALINHA e IMPRIMEVALOR, colocando todas as strings do programa em macros,
so at um procedimento normal para programas com diversas lnguas, em que as strings tm de
ser traduzidas e colocadas num local parte do cdigo, no utilizando obrigatoriamente macros. No
entanto as strings colocadas em macros no tm texto para traduzir, e a sua alterao pode levar a
que o ciclo final no funcione, e quem l os printfs no cdigo, no sabe o que estes fazem at
conhecer o valor das macros.
As macros OPERACAO1/2/3 so j uma agresso ao leitor, dado que acabam por ser uma espcie de
funes sem abstraco, que utilizam as variveis do local onde so chamadas. A macro
ACESSO(a,b), com argumentos, uma forma curta de aceder a uma varivel que j est declarada,
e cujo acesso to simples quanto possvel, pelo que se pode considerar uma reformatao da
linguagem de programao. Deve-se utilizar o facto de o leitor conhecer a sintaxe da linguagem C, e
no reformatar a linguagem para aceder neste caso a um elemento de matrizSoma de forma
alternativa. Finalmente, acaba-se com o pior que se pode fazer utilizando macros, as macros
ITERAR, que encapsulam os prprios ciclos. Para este cdigo ser lido ter-se-ia de andar com os
olhos para cima e para baixo para desvendar o que faz. Uma situao extrema pode-se com as
macros tornar uma outra linguagem de programao vlida, tendo o leitor que ao ler as macros
aprender a sintaxe dessa linguagem de forma a poder ler o programa.
Est introduzido o principal conceito em estruturas de dados, os vectores. Com base neste conceito
ser possvel abordar uma maior variedade de problemas. Tal como o conceito de varivel, a
determinao dos vectores necessrios para resolver um problema, aparentemente simples, das
partes mais complexas e importantes a decidir antes de comear a escrever um programa. Ir
aperceber-se desse problema quando for necessrio utilizar vectores auxiliares, que no
sejam dados de entrada e/ou sada do problema em questo.

8. Procedimentos

69

8. PROCEDIMENTOS
Como vimos no captulo de Funes, uma funo permite definir uma abstraco no clculo de um
valor. Quando se implementa a funo, apenas nos preocupamos como com os argumentos
recebidos se calcula o valor pretendido, abstraindo das possveis utilizaes da funo. Quando se
utiliza a funo, apenas nos preocupamos com a passagem dos valores correctos funo, de forma
a obter o valor pretendido calculado, abstraindo da forma como esta est implementada.
O conceito de "Procedimento" idntico ao da funo, mas na verdade no calcula um valor, mas
sim executa um determinado procedimento, no retornando nenhum valor. Esta pequena
diferena relativamente ao conceito funo, no conduz a grande distino da forma como so
implementados procedimentos ou funes, no merecendo na linguagem C uma distino, muito
embora exista em outras linguagens. A distino feita aqui a nvel de conceito.
Um procedimento, faz algo, logo o seu nome tem de estar associado a um verbo. Uma funo
calcula algo, logo o seu nome tem de estar associado grandeza calculada.
Um procedimento chamado como uma instruo isolada, uma funo chamada numa
expresso, nem que a expresso seja apenas a funo, para que o valor retornado seja utilizado ou
numa atribuio a uma varivel, ou num argumento de uma funo/procedimento.
Vamos ver um exemplo. Necessitamos de fazer uma funo para implementar o primeiro exemplo
dado no captulo Variveis, a troca do valor de duas variveis. Esta funo no deve retornar nada,
deve apenas trocar o valor das variveis. Qual dever ser o seu valor de retorno? Qual dever ser o
seu nome? A resposta a ambas as perguntas simples: no tem valor de retorno; o nome deve
conter o verbo da aco efectuada, ou seja "Trocar" ou se preferir "Troca". Trata-se de um
procedimento.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#include <stdio.h>
int Troca(int x, int y)
{
int aux=x;
x=y;
y=aux;
}
int main()
{
int x=123, y=321;
Troca(x,y);
printf("x: %d y: %d",x,y);
}
Programa 8-1 Tentativa de implementao de uma funo para trocar o valor de duas variveis

Este programa tem uma funo Troca, que um procedimento, pelo que se chamou numa
instruo isolada, o seu valor de retorno seria desperdiado caso existisse. Em todo o caso no
retorna nada, apenas tem o cdigo de troca de duas variveis, o x e o y.
Execuo do programa:
C:\>troca
x: 123 y: 321

70

Parte II Funes, Vectores e Recurso

A execuo do programa no correu como o previsto, a varivel x continuou com 123, e a


varivel y com 321. Porqu? Apenas se trocou os valores das variveis x e y da funo Troca, mas
os valores que estamos interessados trocar os valores das variveis x e y da funo main.
Pode-se da funo main chamar
Troca(2*x,10*y); e nessa
altura o que que trocava de
valor?
A
funo Troca no
conhece nem tem de conhecer as
expresses utilizadas para a
chamar, s sabe que os valores
passados esto nas variveis locais x e y. Apenas retornar um valor, e esse valor ser a sua
contribuio para o programa.
Temos de primeiro ter uma forma de dizer que a funo Troca no retorna um valor do tipo
inteiro, nem qualquer outro. Se omitir o tipo de retorno a linguagem C assume que inteiro, pelo
que necessrio utilizar um outro tipo disponvel, o tipo void. Este tipo no tipo de varivel
nenhum, ideal para os procedimentos em que no se pretende retornar nada.
Em segundo lugar temos de dar permisso funo Troca para alterar os valores das variveis x e
y da funo main. No no entanto necessrio um comando extra da linguagem C, na verdade isso
j foi feito no captulo Vectores, no exemplo int strlen(char str[]), em que passamos uma
string para um argumento. Foi passado na verdade o endereo do vector, e a funo conseguiu
aceder aos valores de todos os elementos do vector. Neste caso no temos vector, mas como um
vector um conjunto de elementos, um elemento tambm um vector.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#include <stdio.h>
void Troca(int x[], int y[])
{
int aux=x[0];
x[0]=y[0];
y[0]=aux;
}
int main()
{
int x[1]={123}, y[1]={321};
Troca(x,y);
printf("x: %d y: %d",x[0],y[0]);
}
Programa 8-2 Funo Troca que realmente troca o valor de duas variveis

Execuo do programa:
C:\>troca2
x: 321 y: 123

O programa funcionou
como
esperado.
No
entanto teve de haver
algum cuidado ao declarar
as variveis como vectores
de uma unidade na funo
main, para ter acesso ao

8. Procedimentos

71

endereo das variveis x e y. Esta situao resolve-se com o operador & da linguagem C,
que aplicado a uma varivel retorna o seu endereo, que precisamente o que necessitamos
para no ter de declarar um vector de uma varivel.
10 int main()
11 {
12
int x=123, y=321;
13
Troca(&x,&y);
14
printf("x: %d y: %d",x,y);
15 }

Falta ainda na funo Troca, uma forma mais simples de aceder ao primeiro elemento, at porque
no h mais elementos, apenas pretendido o acesso ao valor do primeiro elemento. A linguagem C
tem tambm o operador *, que aplicado a um endereo, retorna o valor nesse endereo:
3 void Troca(int x[], int y[])
4 {
5
int aux = *x;
6
*x = *y;
7
*y = aux;
8 }

Os argumentos tambm sugerem que se trata de um vector x e y, enquanto se pretende apenas o


endereo de duas variveis, ou se quiser um vector unitrio. J vimos que o int x[] equivalente
a int *x, e precisamente esta uma das situaes mais aconselhvel para a segunda verso, de
forma a dar indicaes ao leitor que se pretende apenas aceder ao valor no endereo e no a um
conjunto de valores a comear nesse endereo. No entanto no demais realar que para a
linguagem C esta diferena no tem significado. Vejamos ento a verso final.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#include <stdio.h>
void Troca(int *x, int *y)
{
int aux = *x;
*x = *y;
*y = aux;
}
int main()
{
int x=123, y=321;
Troca(&x,&y);
printf("x: %d y: %d",x,y);
}
Programa 8-3 Verso final da funo de troca de duas variveis

A este tipo de passagem de


parmetros designada de
passagem por referncia, uma
vez que se envia a referncia da
varivel, e no a prpria varivel,
para que o contedo da varivel
possa ser alterado.
Todos os procedimentos devem ter algum parmetro passado por variveis por referncia, caso
contrrio no h qualquer efeito da sua chamada. H no entanto duas situaes: a sua aco incide
sobre recursos externos, como imprimir uma mensagem no ecr; altera valores de variveis
globais.

72

Parte II Funes, Vectores e Recurso

A linguagem C permite que existam declaraes no no incio de funes, mas sim fora delas. As
variveis declaradas fora das funes so variveis globais, criadas no incio do programa,
e acessveis a todas as funes. Vejamos outra verso da troca de duas variveis.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include <stdio.h>
int x=123, y=321;
void Troca()
{
int aux=x;
x=y;
y=aux;
}
int main()
{
Troca();
printf("x: %d y: %d",x,y);
}
Programa 8-4 Exemplo de utilizao de variveis globais

Neste caso o procedimento Troca nem sequer necessita de argumentos, dado que vai trocar o valor
de x e y, e so exactamente essas variveis que so impressas no printf do main.
O primeiro passo no na primeira
linha da funo main, mas sim na
declarao das variveis globais. Estas
variveis esto fora da funo main, e
acessveis a todas as funes. O passo 2
chama a funo main. Nas execues
passo-a-passo sem variveis globais,
assume-se que a funo main acabou de ser chamada antes do primeiro passo.
Pode-se pensar que assim melhor, dado que nem sequer preciso passar argumentos. Nada mais
errado. Nesta verso do programa, a funo Troca ao perder os argumentos, perdeu generalidade,
apenas pode trocar o valor de x e y, quando anteriormente poderia trocar o valor de quaisquer
duas variveis inteiras. Por outro lado, como as variveis x e y esto disponveis em todo o
programa, tanto a utilizao como a atribuio dos valores destas variveis tm de considerar todas
as restantes linhas de cdigo. Pode uma zona do cdigo estar a fazer uma utilizao a estas
variveis, e outra zona de cdigo uma utilizao diferente. No portanto possvel que ao
implementar uma funo que utilize estas variveis, abstrair-se do resto do programa, a vantagem
da abstraco funcional. Nos outros exemplos, cada uma das variveis declaradas apenas eram
utilizadas/alteradas com instrues na funo onde foram declaradas, no sendo necessrio
considerar o resto do cdigo para utilizar/alterar cada varivel.
As variveis globais so desaconselhadas dado que quebram a abstraco funcional, mas por
vezes so necessrias por motivos prticos. Existe no entanto uma forma para em certas situaes
utilizar-se uma "falsa" varivel global, que na verdade uma varivel global mas apenas acessvel a
uma s funo, no quebrando desta forma a abstraco funcional. Trata-se de variveis estticas
(static), que podem ser declaradas numa funo, mas mantm o valor entre chamadas, que o
mesmo que uma varivel global.

8. Procedimentos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

73

#include <stdio.h>
int Troca(int *x, int *y)
{
static int trocas=0;
int aux = *x;
*x = *y;
*y = aux;
return ++trocas;
}
int main()
{
int x=123, y=321;
printf("Troca %d: x=%d, y=%d\n", Troca(&x,&y),x,y);
printf("Troca %d: x=%d, y=%d\n", Troca(&x,&y),x,y);
printf("Troca %d: x=%d, y=%d\n", Troca(&x,&y),x,y);
}
Programa 8-5 Exemplo de utilizao de variveis estticas (static)

Execuo do programa:
C:\>troca
Troca 1: x=321, y=123
Troca 2: x=123, y=321
Troca 3: x=321, y=123

Neste programa troca-se o valor das variveis x e y vrias vezes. A funo Troca retorna agora o
nmero de trocas que j efectuou. Tem uma varivel declarada com static, o que significa que
uma varivel esttica, mantendo o valor entre chamadas. Pode assim ser utilizada para contador,
sendo o valor dessa varivel o valor retornado. A funo printf chama a funo Troca de forma a
imprimir esse valor, e depois utiliza o valor de x e y, entretanto j alterados pela funo Troca. Ao
executar 3 vezes, podemos ver que o contador est a funcionar e que os valores das variveis vo
trocando.
A execuo passo-a-passo
trata as variveis estticas
da mesma forma que as
variveis globais, muito
embora estejam acessveis
apenas para as funes
onde esto declaradas. A
linha 5 nunca executada
quando chamada a
funo Troca, mas sim no
incio da execuo.
A utilizao de variveis
estticas no deve estar
ligada com a funcionalidade
pretendida. Contadores do
nmero de chamadas a
determinada funo um bom exemplo, mas se estiver a tentar utiliz-la como uma funcionalidade
pretendida, desaconselha-se.

74

Parte II Funes, Vectores e Recurso

No se pode acabar esta seco sem referir que o que se acabou de fazer com a passagem de
argumentos por referncia foi introduzir apontadores na linguagem C. As declaraes na
funo Troca com argumentos, podem estar na declarao das variveis locais de qualquer funo.
Podemos ter apontadores para qualquer varivel, e fazer operaes sobre apontadores, como
incrementar, passando o apontador para o endereo da varivel seguinte, ou somar, saltando-se
tantos endereos como o valor somado.
Vamos exemplificar implementando uma funo que recebe duas strings, e retorna um apontador
para a primeira ocorrncia de uma string na outra (strstr). Esta funo faz parte da biblioteca
string.h, e pode ser utilizada tal como a strlen j referida, s quais se juntam tambm as
funes strcpy e strcat de forma a se obter o conjunto de funes essenciais sobre strings. A
primeira, strcpy, copia o contedo de uma string para a outra, e a segunda, strcat, adiciona o
contedo de uma string na outra. Todas as funes da string.h assumem que foi declarado uma
string com espao suficiente para as operaes que executam, e que as strings terminam com zero.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#include <stdio.h>
char *strstr(char *find, char *str)
{
int i;
/* percorrer toda a string str */
while(*str!=0)
{
/* tentar fazer match a comear em str */
for(i=0; str[i]!=0 && find[i]!=0 && find[i]==str[i]; i++);
/* se chegou ao fim da string de procura, ento h match */
if(find[i]==0)
/* retornar o incio do match */
return str;
/* incrementar o incio da string */
str++;
}
return NULL;
}
int main()
{
char str[]="percorrer toda a string str";
char find[10];
char *resultado;
printf("Introduza a string de procura: ");
gets(find);
resultado=strstr(find,str);
printf("Resultado: %s\n",resultado);
}
Programa 8-6 Procura a primeira ocorrncia de uma string em outra (strstr)

Repare-se que se utiliza o facto de str ser um apontador para o incio da string, para no ciclo
principal servir desta prpria varivel como varivel iteradora. procurado no segundo ciclo um
match entre find e os primeiros caracteres de str. Se tal no for conseguido, incrementa-se o
incio da string (varivel str) e repete-se o ciclo.
Na funo main guarda-se o resultado da chamada funo numa varivel declarada como
apontador resultado. Poder-se-ia ter chamado a funo strstr logo na funo printf, mas
pretendeu-se ilustrar que se pode guardar um endereo sem problema, neste caso com a primeira
ocorrncia de find em str.

8. Procedimentos

75

Execuo do programa:
C:\>strstr
Introduza a string de procura: str
Resultado: string str
C:\>strstr
Introduza a string de procura: abc
Resultado: (null)

Como se pode ver na primeira execuo do programa, o resultado foi a impresso da primeira
ocorrncia at ao final da string, uma vez que uma string apenas acaba com o carcter zero. O
segundo caso est relacionado com o valor retornado da funo, em caso de no encontrar nada.
Retorna NULL, que o endereo vazio. Quando no se pretende atribuir nenhum valor a um
apontador, deve-se utilizar esta constante, que pertence linguagem C.
O programa tentou imprimir uma string a apontar para NULL na segunda execuo, o que no deve
ser feito, atendendo a que NULL o endereo vazio e portanto no tem caracteres. Por motivos
prticos, a string "(null)" aparecer como resultado desta operao na maior parte dos
compiladores actuais. Igualmente vlido embora no pertencente linguagem C, ser considerar o
valor de NULL como sendo zero, podendo-se fazer uso deste facto em expresses lgicas omitindo
operadores lgicos e considerando NULL como falso e todos os restantes endereos como
verdadeiro, mas desaconselha-se esse tipo de utilizao pelos motivos j explicados no
captulo Condicionais.
A execuo passo-a-passo bastante extensa, est aqui uma verso cortada, mas ainda assim podese verificar o funcionamento de um programa com apontadores. Nestes programas move-se o

76

Parte II Funes, Vectores e Recurso

endereo da varivel em anlise, em vez de utilizar um ndice para aceder a determinada varivel
num vector. Esta tcnica acaba por ser mais eficiente, uma vez que o acesso a uma posio
arbitrria de um vector mais lento que o acesso ao valor de uma varivel atravs do endereo,
mas pode resultar tambm em cdigo mais confuso.
Erros comuns mais directamente associados a este captulo:

Variveis globais
o Forma: Em vez de utilizar alguns dos argumentos nas funes, declara-se a varivel
globalmente em vez de na funo main, de forma a no s ser acessvel a todas as
funes, como no ser necessrio pass-la como argumentos nas funes
o Problema: Ao fazer isto, no s mascara as ms opes na diviso do cdigo por
funes, dado que no controla os argumentos da funo, como mais importante,
perde principal vantagem das funes: quando implementa/rev a funo, apenas
tem de considerar a funo e pode abstrair-se do resto do cdigo. Como h variveis
globais, ao implementar a funo tem que se considerar todos os restantes locais
que essas variveis so utilizadas (leitura / atribuio), para que o cdigo na funo
seja compatvel. Por outro lado, ao no se aperceber quando cria a funo do
nmero de argumentos que recebe e nmero de variveis que altera, pode estar a
criar funes que reduzem muito pouco a complexidade, sendo a sua utilidade
diminuta
o Resoluo: Utilize o seu cdigo com variveis globais para fazer uma reviso geral.
Deve remover todas as variveis globais e coloc-las na funo main, deixando
apenas as constantes globais que faam sentido estar definidas globalmente, e
macros. Depois deve ir refazendo os argumentos das funes, e eventualmente rever
as funes que necessita de forma a ter funes com poucos argumentos e com uma
funcionalidade clara e simples
Nomes sem significado
o Forma: Atendendo a que um bom nome gasta tempo e irrelevante para o bom
funcionamento do cdigo, qualquer nome serve
o Problema: O cdigo deve ser lido no s pelo compilador como por outras pessoas,
pelo que a m utilizao de nomes revela por um lado a m organizao mental de
quem o escreve, e por outro um desrespeito por quem l o cdigo. A troca de todos
identificadores por nomes abstractos e sem sentido, pode ser uma forma para
tornar o cdigo ilegvel, no caso de se pretender proteg-lo. Pode tambm ser uma
situao confundida com uma situao de fraude, resultante de uma tentativa de um
estudante alterar os identificadores de um cdigo obtido ilicitamente
o Resoluo: Verificar todos os identificadores de uma s letra, ou muito poucas
letras, bem como nomes abstractos, funes sem uma grandeza associada, e
procedimentos sem verbo. Se no consegue encontrar nomes claros para um
identificador, seja uma varivel, uma funo, o nome de uma estrutura, etc., pense
numa palavra em que explique para que a varivel serve, o que a
funo/procedimento faz, o que contm a estrutura, etc. Se no consegue explicar
isso, ento o identificador no existe
Funes muito grandes
o Forma: Tudo o que necessrio fazer, vai sendo acrescentado na funo, e esta fica
com cada vez uma maior dimenso. Esta situao ocorre devido ao problema ser
muito complexo

8. Procedimentos

77

Problema: Ao ter uma funo grande, vai ter problemas de complexidade. No s as


declaraes de variveis da funo ficam longe do cdigo onde so utilizadas, como
no consegue visualizar a funo de uma s vez, sendo muito complicado verificar o
cdigo. Esta uma situao clara de m utilizao da principal ferramenta da
programao, que lhe permite controlar a complexidade do cdigo: abstraco
funcional
o Resoluo: Deve dividir a funo em partes que faam sentido, de forma a ter
funes pequenas e com poucos argumentos. Dividir a funo s fatias m ideia,
dado que os argumentos necessrios para cada fatia podem ser muitos. Se houver
vrias fases, sim, colocar cada fase numa funo, caso contrrio deve verificar se os
ciclos interiores podem construir funes que no s utilizem poucos argumentos
como tambm tenham possibilidade de ser utilizadas em outras situaes. Se a
funo reunir estas condies, ter certamente um nome claro
Funes com muitos argumentos
o Forma: Aps criar a funo, ao adicionar como argumentos as variveis que esta
utiliza (leitura / atribuio), este valor revela-se elevado, mas o que necessrio
o Problema: Se o nmero de argumentos muito elevado, a abstraco que a funo
representa fraca, uma vez que para utilizar a funo, necessrio ter muita
informao. Para alm disso, se a funo for muito curta, pode dar mais trabalho a
arranjar os parmetros certos para chamar a funo, que colocar o seu cdigo, sendo
assim a funo mais prejudicial que til. No caso dos diversos argumentos estarem
relacionados, significa que no foram agregados numa estrutura
o Resoluo: Deve procurar alternativas na criao de funes. Dividir uma funo
existente a meio, provoca situaes destas. Para desagregar uma funo, deve
procurar o cdigo repetido, se no existir, o cdigo no interior dos ciclos
tipicamente um bom candidato a funo, de forma a manter em cada funo um ou
dois ciclos. No caso de as variveis estarem relacionadas, criar uma estrutura para
as agregar
o

O Programa 8-4 um exemplo de uma ocorrncia do erro das variveis globais. Um exemplo de
uma ocorrncia de nomes sem significado seria se em vez de utilizar Troca para o nome da funo
que troca o valor de duas variveis, utilizar o nome Processa. Esse nome nada diz sobre o que o
procedimento faz, pelo que o leitor fica sem saber se quem o escreveu sabe o que fez, uma vez que
no foi capaz de dar um nome condicente com a sua funo. Exemplos dos outros dois erros, no se
encontram neste texto, dado que so utilizados sempre exemplos o mais curto possvel para
introduzir cada conceito. O leitor provavelmente encontrar ocorrncias desses erros no seu
prprio cdigo, ao realizar os exerccios azuis e vermelhos.
Foram introduzidos alguns conceitos nesta seco, no s procedimentos, como
tambm passagem de valores por referncia, variveis globais e estticas, e apontadores.
Todos estes conceitos no tm uma grande novidade, so construdos com base nos conceitos j
dados anteriormente, nomeadamente em funes e vectores, mas so essenciais a uma boa
estruturao do cdigo. Cuidado especial deve ser dado no caso de se sentir tentado a recorrer com
frequncia a variveis globais, sendo esse um dos sintomas de que no est a aproveitar as totais
potencialidades da abstraco funcional. Num exerccio em que tal lhe acontea, analise com
redobrada ateno resolues alternativas.

78

Parte II Funes, Vectores e Recurso

9. RECURSO
A "Recurso" no mais que a aplicao da abstraco funcional, na prpria funo a
implementar. A abstraco funcional permite implementar uma funo com base em apenas os
seus argumentos, e ignorar tudo o resto. Ao mesmo tempo podemos utilizar funes sem querer
saber como esto implementadas. possvel utilizar a funo que se est a implementar?
Certamente que no se pode utilizar a funo que se est a implementar para implement-la,
excepto se utilizar argumentos distintos. Vamos supor que se tem casos particulares muito simples,
e para esses sabemos o resultado sem problema. No caso da funo factorial, esses casos so
quando o nmero 0 ou 1, o valor do factorial 1. Pode-se fazer a funo para esses casos, e no
caso genrico, utilizar o facto de N!= ( N 1)! N . Assim podemos utilizar a funo Factorial
para implementar a funo Factorial, mas utilizando argumentos mais baixos, e no limite ir ser
utilizado os casos particulares simples. Vamos implementar no exerccio seguinte esta funo tanto
no modo recursivo como no modo iterativo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#include <stdio.h>
int FactorialR(int n)
{
if(n<2)
return 1;
return FactorialR(n-1)*n;
}
int FactorialI(int n)
{
int i, resultado=1;
for(i=2;i<=n;i++)
resultado*=i;
return resultado;
}
int main()
{
int n=5;
printf("Factorial de %d: %d = %d", n, FactorialR(n), FactorialI(n));
}
Programa 9-1 Factorial, verso iterativa e recursiva

Execuo do programa:
C:\>factorial
Factorial de 5: 120 = 120

Repare-se na simplicidade da funo FactorialR, tratamento dos casos simples, e tratamento do


caso genrico recorrendo mesma funo mas com um argumento mais baixo, que acabar por
tender para o caso simples. Esta funo nem requer a criao de variveis locais, apenas utiliza o
argumento.
Ao contrrio, a funo FactorialI implementa o factorial todo, e retorna o valor. No entanto foi
necessrio um ciclo, e criar uma varivel iteradora para iterar de 2 a N, bem como criar uma
varivel para guardar o resultado. Ambas as funes retornam o mesmo resultado, como se pode
verificar na execuo do programa, mas a verso recursiva consideravelmente mais simples.

9. Recurso

79

Para compreender bem


a recurso, analise com
cuidado a execuo
passo-a-passo acima. As
diversas chamadas a
FactorialR no
se
confundem
em
momento algum. Cada
linha de cdigo diz
respeito
sempre

ltima funo que foi


chamada,
e
assim
permanecer at que a
funo
retorne,
passando a execuo
para a linha de onde a
funo foi chamada, ou
para a linha seguinte. Ao
contrrio da verso
recursiva, o FactorialI no se chama a si prprio, mas utiliza duas variveis i e resultado para
calcular o valor pretendido.
A recurso pode ser utilizada para substituir ciclos, tal como exemplificado no factorial.
Um exemplo clssico quando se introduz a recurso o problema das torres de Hanoi.

Figura 9-1 Problema das Torres de Hanoi, em madeira. Fonte: wikipdia

Tem-se 3 zonas onde se podem colocar discos numa pilha: esquerda, meio, direita. Os discos tm
dimenses distintas e esto inicialmente na pilha da esquerda ordenados por dimenso, o disco
maior em baixo, o mais pequeno em cima. Pode-se movimentar um disco de cada vez, do topo da
pilha, para uma outra pilha que esteja vazia ou tenha um disco maior, mas nunca para cima de um
disco menor. Pretende-se obter os movimentos necessrios para passar todos os discos da pilha da
esquerda para a pilha da direita.
Este problema aparentemente complexo, pode ser resolvido facilmente pela via recursiva, tratando
primeiro os casos especiais. Os casos especiais so aqui o movimento de 1 s disco, j que isso

80

Parte II Funes, Vectores e Recurso

simples, basta mover o disco da origem para o destino. Agora qual o caso genrico? Ser possvel
mover N discos da pilha A para a pilha B, sabendo como mover N-1 discos de uma pilha para outra?
A resposta sim, dado que o problema mover o disco maior. Se mover os discos acima, portanto
os N-1 discos, de A para uma pilha intermdia C, depois estamos em condies de mover 1 disco de
A para B e de seguida mover os N-1 discos que estavam em C para B.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#include <stdio.h>
void Hanoi(int de, int para, int medio, int discos)
{
static char pilhas[3][] = { "esquerda", "meio", "direita" };
if(discos==1)
printf("\nMover do(a) %s para %s", pilhas[de], pilhas[para]);
else if(discos>1)
{
Hanoi(de,medio,para,discos-1);
Hanoi(de,para,medio,1);
Hanoi(medio,para,de,discos-1);
}
}
int main()
{
int discos;
printf("Torres de Hanoi. Quantos discos quer movimentar? ");
scanf("%d",&discos);
Hanoi(0,2,1,discos);
}
Programa 9-2 Torres de Hanoi

Repare na simplicidade da expresso genrica. Mesmo o movimento de um s disco pode ser


reutilizado o cdigo j feito na condio acima, chamando a funo recursivamente. Como todas as
chamadas funo tm o argumento disco com um valor inferior ao recebido, este tipo de utilizao
lcito, uma vez que se est a diminuir a complexidade do problema at ao ponto ser um caso
especial, o movimento de um s disco.
Execuo do programa:
C:\>hanoi
Torres de Hanoi. Quantos discos quer movimentar? 2
Mover disco do(a) esquerda para meio
Mover disco do(a) esquerda para direita
Mover disco do(a) meio para direita
C:\>hanoi
Torres de Hanoi. Quantos discos quer movimentar? 4
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover
Mover

disco
disco
disco
disco
disco
disco
disco
disco
disco
disco
disco
disco
disco
disco
disco

do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)
do(a)

esquerda para meio


esquerda para direita
meio para direita
esquerda para meio
direita para esquerda
direita para meio
esquerda para meio
esquerda para direita
meio para direita
meio para esquerda
direita para esquerda
meio para direita
esquerda para meio
esquerda para direita
meio para direita

9. Recurso

81

Na execuo do programa pode-se ver que a soluo ao problema pretendido tem uma relao
exponencial relativamente ao nmero de discos. Para 2 discos h 3 movimentos, para 4 discos h
15 movimentos. O nmero de movimentos mnimo para N discos realmente 2 N 1 .
Execuo passo-a-passo:

Pode-se ver na execuo passo-a-passo que por cada chamada recursiva, necessrio alocar os
argumentos da funo. Se existirem muitas chamadas recursivas, e a funo utilizar muitas
variveis locais, poder haver problemas de memria, enquanto uma verso iterativa tem esse
problema controlado.
Repare na utilizao do vector esttico com o nome das pilhas na linha 5. Se fosse utilizado um
vector normal, este seria criado em cada chamada funo, a adicionar s 3 variveis locais, e
atendendo a que a funo chamada muitas vezes, seria um considervel desperdcio de memria.
Um outro exemplo clssico um exerccio que faz parte dos exerccios da Parte I, a funo
Fibonacci. A definio recursiva, pelo que se pode implementar facilmente atravs de recurso.
Reveja esse exerccio e implemente a verso recursiva.
Repare que a verso recursiva da funo Fibonacci mais lenta que a verso iterativa, o que deve
conseguir notar quando pedir nmeros elevados. Isso acontece porque o valor da funo nos
pontos mais baixos, repetidamente necessrio na verso recursiva, mesmo que j tenha sido
calculado anteriormente. Uma forma de resolver o problema, quando h hiptese da funo ser
chamada duas ou mais vezes com os mesmos argumentos, guardar o valor dos clculos num
vector esttico, e verificar se o valor pedido j foi ou no calculado. Essa soluo tem no entanto
custo na clareza do cdigo, o que pode no compensar em tornar a funo recursiva.
Nem sempre a recurso acresce custo computacional. Nos dois exemplos acima a diferena entre
uma verso iterativa e uma verso recursiva diminuta. No entanto, mesmo que exista aumento de
custo, por vezes a reduo da complexidade do cdigo compensadora. Caso duvide deste ponto,
implemente a verso iterativa das torres de Hanoi.

82

Parte II Funes, Vectores e Recurso

Erros comuns mais directamente associados a este captulo:

M utilizao da recurso
o Forma: Uma funo tem um conjunto de instrues que tm de ser executadas
vrias vezes. Uma forma de fazer isto no final da funo colocar um teste a
controlar o nmero de execues, e chamar a funo recursivamente
o Problema: Se a chamada recursiva est no final, e o cdigo tem de passar sempre
por esse condicional, ento pode remover a chamada substituindo por um ciclo. Ao
chamar a funo recursivamente, as variveis locais j criadas no vo mais ser
necessrias, mas por cada passo do ciclo ficar um conjunto de variveis locais
espera de serem destrudas. Se o ciclo for muito grande, o stack ir certamente
rebentar
o Resoluo: Substituir a chamada recursiva por um ciclo e respectivo teste de
paragem. Caso a chamada recursiva no seja no final da funo, ou exista mais que
uma chamada recursiva, ento sim, prefervel utilizar-se recurso

A funo FactorialR do Programa 9-1 um exemplo deste tipo de erro.


Nos exerccios da Parte II encontrar alguns que apenas se podem resolver de forma relativamente
simples, atravs da recurso. Tenha em mente que no deve utilizar recurso para o que no til.
Por exemplo, o caso do Factorial, embora mais simples, o ganho de simplicidade do cdigo no
relevante comparativamente verso iterativa. J nas torres de Hanoi o ganho muito relevante. A
funo Fibonacci um exemplo aparentemente em que se deve aplicar a recurso, mas resulta em
perda de eficincia.
A recurso uma ferramenta muito poderosa que tem de ser utilizada com muito cuidado. Tanto
pode reduzir um problema muito complexo num problema trivial, como pode criar problemas de
performance e mesmo de memria.

2) Exerccios

83

2) Exerccios
argumentos.c
Faa um programa que indique os argumentos recebidos na linha de comando.
Notas:

Utilize os parmetros da funo main, argc e argv

Execuo de exemplo:
C:\>argumentos abcd 1234
Argumento 0: argumentos
Argumento 1: abcd
Argumento 2: 1234

Pergunta: assumindo que o nome do programa argumentos, qual deve ser a linha de comando
para ter dois argumentos, o primeiro ABC_123 e o segundo 123_ABC?

inverte.c
Faa um programa que pea ao utilizador para inserir uma string, e utiliza uma funo que
recebe a string, e inverte a ordem dos caracteres da string.
Notas:

Utilize a funo gets(str), para ler uma string, e strlen(str) para obter o nmero de
caracteres na string, pertencente biblioteca string.h.
Considere que as strings inseridas pelo utilizador no ultrapassam os 255 caracteres.
Imprima os resultados intermdios.

Execuo de exemplo:
C:\>inverte
Inversao de um texto.
Texto: inverte
0: enverti
1: etverni
2: etrevni
Texto invertido: etrevni

Pergunta: Aplique ao programa a seguinte string, e coloque na resposta o texto invertido:


lcdsfkgjzoxilsd sdsdfd sdhjkafads dfwerwqad dsfs dfsdfsdkj

84

Parte II Funes, Vectores e Recurso

rand.c
Faa um programa que utiliza uma funo para gerar nmeros pseudo-aleatrios, de acordo
com a seguinte frmula:

x(n + 1) = mod(a x(n ) + b, m )


Em que a , b e m so constantes grandes e primos. Os nmeros pseudo-aleatrios so sempre
inicializados por um primeiro valor, a semente (seed), que ser o valor de x (0 ) na expresso acima.
Utilize o valor retornado por time(NULL); (nmero de segundos no instante em que a funo
chamada), de forma a inicializar a semente com o tempo, tendo assim resultados distintos em cada
execuo do programa. O programa deve gerar 10 nmeros pseudo-aleatrios, entre 0 e um valor
mximo, introduzido pelo utilizador.
Notas:

mod o resto da diviso, que em C o operador %


Deve incluir a biblioteca time.h para poder utilizar a funo time.

Se tem um nmero pseudo-aleatrio grande, e pretende um nmero de 0 a N-1, pode


utilizar o resto da diviso desse nmero por N para obter o valor pretendido.

Execuo de exemplo:
C:\>rand
Gerador de numeros aleatorios inteiros.
Valor maximo: 99
seed=1282148053: 33 52 2 2 26 44 66 29 68 24

Pergunta: se a , b e m forem respectivamente 231533, 82571 e 428573, e com seed=1


temporariamente, quais os trs primeiros nmeros gerados para valores entre 0 e 9? Escreva os
nmeros separados por espaos.

find.c
Faa um programa que utiliza uma funo que recebe um vector de inteiros, o seu tamanho, e
um elemento, e retorna a primeira posio em que o elemento encontrado no vector, ou -1 se o
elemento no estiver no vector. O programa deve procurar o nmero 2.
Notas:

Utilize a funo standard rand() para gerar valores aleatrios (e no a que fez no exerccio
anterior), e inicialize a semente chamando a funo srand(1), (ambas as funes
pertencem biblioteca stdlib.h) antes de inicializar o vector com elementos aleatrios de
0 a 999. Utilize um vector com 1000 elementos.
Mostre os 10 primeiros elementos do vector

2) Exerccios

85

Execuo de exemplo:
C:\>find
Vector: 41 467 334 500 169 724 478 358 962 464
Posicao de 2: xxx

Pergunta: Qual a primeira posio em que aparece o elemento 2?

maximo.c
Faa um programa que utiliza uma funo para retornar o valor mximo num vector de reais
(float), inicializado com valores gerados pela distribuio exponencial negativa:

log(Uniforme(0,1))
Notas:

A funo log est disponvel na biblioteca math.h


A funo "Uniforme" devolve valores reais entre 0 e 1, mas no existe nas bibliotecas. Gere
valores reais entre 0 e 1 (nunca gerando o zero), gerando um valor entre 1 e 10000 com a
funo rand (inicialize com srand(1)), aps o qual efectue a diviso real por 10000, para
assim ficar com valores desde 0,0001 at 1,0000.

Execuo de exemplo:
C:\>maximo
Vector: 5.47 0.17 0.46 0.43 0.09 0.56 1.91 0.07 0.36 0.81
0: 5.47
54: 5.57
268: 6.12
915: 7.xx
xx
Valor maximo: 7.xxxxx
xxxxx

Pergunta: Gerando um vector com 1000 elementos, utilize a funo desenvolvida para calcular o
maior valor no vector. Indique na resposta esse valor com preciso de 6 dgitos.

mdc.c
Faa um programa que utiliza uma funo recursiva para calcular o mximo divisor comum,
utilizando o seguinte algoritmo recursivo: mdc(x,y)=x se y=0, caso contrrio
mdc(x,y)=mdc(y,mod(x,y)), em que mod(x,y) o resto da diviso de x por y
Notas:

O resto da diviso em C o operador %

86

Parte II Funes, Vectores e Recurso

Execuo de exemplo:
C:\>mdc
Calculo do maximo divisor comum entre dois numeros.
Indique x e y:48 120
MDC: 24
A soma dos MDC dos pares de numeros de 1 a 100: xxxxx

Pergunta: percorra todos pares de nmeros distintos entre 1 e 100, no percorrendo o mesmo par
duas vezes, e some os mximos divisores comuns. Indique o resultado na resposta.

sort.c
Faa um programa que utiliza uma funo para ordenar um vector de inteiros, com o seguinte
algoritmo: percorre todos os pares sequencialmente, e se estiverem pela ordem inversa, troca os
elementos.
Notas:

Percorrer todos os pares apenas uma vez independente da ordem, no necessrio analisar
o par 10, 35, e o par 35, 10.

Exemplo passo-a-passo:
Iterao
0
1
2
3
4
5
6

v[0]
478
358
358
358
358
358
358

v[1]
358
478
478
478
478
464
464

v[2]
962
962
962
962
962
962
478

v[3]
464
464
464
464
464
478
962

Na tabela acima, cada linha uma iterao, sendo o estado do vector as 4 ltimas colunas dessa
linha. A bold esto os dois elementos em comparao no vector. Se estiverem pela ordem inversa,
trocam de lugar. Aps a ltima iterao, em que so comparados os elementos das posies 2 e 3, o
vector fica ordenado.
Execuo de exemplo:
C:\>sort
Vector antes de ordenado: 41 467 334 500 169 724 478 358 962 464
Vector depois de ordenado: 1 2 3 3 3 6 7 7 8 8
Quartil 25: xxx
Quartil 50: xxx
Quartil 75: xxx

Pergunta: utilizando a mesma inicializao de um vector de 1000 inteiros do exerccio find.c,


ordene o vector e indique o quartil de 25, 50 e 75% (valores nas posies 250, 500 e 750). Indique
os trs valores separados por um espao.

2) Exerccios

87

removedups.c
Faa um programa que utilize uma funo que remove os elementos duplicados num vector de
inteiros ordenados. Deve retornar o nmero de elementos finais no vector.
Notas:

Para remover um elemento no vector, todos os elementos seguintes tm de ser copiados


para as novas posies.

Execuo de exemplo:
C:\>removedups
Vector inicial: 41 467 334 500 169 724 478 358 962 464
Elementos que permanecem no vector: xxx
Vector final: 1 2 3 6 7 8 9 10 11 15

Pergunta: utilizando a mesma inicializao de um vector de 1000 inteiros do exerccio find.c, aps
orden-lo com cdigo do exerccio sort.c, execute esta funo e indique na resposta o nmero de
elementos que permanecem no vector.

somanumeros.c
Faa um programa que pea ao utilizador para introduzir uma string com nmeros reais
separados por espaos, e retorne a sua soma. Utilize preciso dupla e apresente o resultado em
notao cientfica com preciso 15.
Notas:

Pode utilizar a funo strtok da biblioteca string.h, que tem a seguinte utilizao:
char *pt=(char*)strtok(string," ");
while(pt!=NULL) {
processar pt
pt=(char*)strtok(NULL," ");
}

Pode utilizar a funo atof que recebe uma string e retorna o seu valor numrico. Deve
incluir a biblioteca stdlib.h, para utilizar esta funo.

Execuo de exemplo:
C:\>somanumeros
Introduza um conjunto de numeros reais separados por espacos.

1.2 3.4 5.6 7.8

1.20 (1.2)
4.60 (3.4)
10.20 (5.6)
18.00 (7.8)
A soma dos numeros e' 18.

Pergunta: Qual o resultado para a seguinte string: "1 1.12320023400234044 1e+5 1e-5 -4"?

88

Parte II Funes, Vectores e Recurso

trocos2.c
Faa um programa com a mesma funcionalidade que o exerccio 1) trocos.c, agora utilizando
uma funo que dado um montante, o devolve em moedas, mas em vez de utilizar cdigo repetido
para cada moeda, utilize um ciclo em que a informao de cada moeda est num vector
previamente inicializado. Calcule tambm o total de moedas necessrio, para os montantes 0.01;
0.03; 0.07; 0.15; ... i=i*2+0.01; ...; 20.
Notas:

Os vectores previamente inicializados, podem conter o valor das moedas e os textos a


apresentar.
Chame uma funo trocos, para saber quantas moedas so necessrias para cada um dos
montantes, e faa um ciclo a comear em 0.01, e com a frmula acima descrita

Execuo de exemplo:
C:\>trocos2
Introduza um montante em euros, podendo ter centimos: 3.49
2 euros: 1
1 euro: 1
20 centimos: 2
5 centimos: 1
2 centimos: 2
Total moedas: xx

Pergunta: Qual o total de moedas necessrio para os montantes acima descritos?

jogodados.c
Faa um programa que implemente um jogo de dados em que um jogador lana
sucessivamente um dado, at optar por parar ficando com o nmero de pontos somados. Se sarem
dois dados iguais consecutivos, o jogador fica com os pontos que tem, mas negativos. Faa um
programa para simular o jogo, mediante o nmero de lanamentos do jogador.
Notas:

Para responder pergunta em baixo, execute N jogos para cada nmero de lanamentos,
com N suficientemente grande de forma a obter um valor mdio relativamente estvel.
Para obter execues distintas, inicialize a semente com base no tempo, chamando no incio
do programa uma s vez: srand(time(NULL));

Exemplo de jogos:
Lanamentos: 4
jogo1: 3 1 6 5 +15
jogo2: 3 4 4 -11
...

2) Exerccios

89

Execuo de exemplo:
C:\>jogodados
Jogo do lancamento de dados.
Indique numero de lancamentos: 4
3 4 3 1 Pontos: 11
Valores medios dos pontos:
Com 1 lancamento(s): xxx
Com 2 lancamento(s): xxx
Com 3 lancamento(s): xxx
Com 4 lancamento(s): xxx
Com 5 lancamento(s): xxx
Com 6 lancamento(s): xxx
Com 7 lancamento(s): xxx
Com 8 lancamento(s): xxx
Com 9 lancamento(s): -5.06

Pergunta: Indique por ordem e separado por um espao, os nmeros de lanamentos cujo valor
esperado seja superior a 3.

baralhar.c
Faa um programa que utiliza uma funo que recebe um vector e a sua dimenso, e baralhe
os elementos nele contidos. Ateno que deve realizar o nmero mnimo de trocas, e a
probabilidade de cada elemento estar em qualquer posio deve ser igual.
Notas:

Comeando pelo primeiro elemento, tem de haver um valor aleatrio para decidir qual ser
o primeiro elemento. O segundo valor aleatrio decidir qual ser o segundo elemento (no
removendo o primeiro elemento que j foi escolhido aleatoriamente), e assim
sucessivamente para todos os restantes elementos.

Exemplificao:
Na tabela abaixo, em cada linha est uma iterao, bem como o valor das diversas posies do
vector, e o resultado de um valor aleatrio gerado em cada iterao para decidir qual o elemento
nessa iterao. O valor aleatrio pertence a um intervalo cada vez mais pequeno, at que o ltimo
valor um valor aleatrio 0 ou 1, para decidir a ordem das duas ltimas posies no vector.
Iterao
0
1
2
3

Valor Aleatrio
rand()%4 = 2
rand()%3 = 0
rand()%2 = 1

0
48
80
80
80

1
22
22
22
22

2
80
48
48
25

Execuo de exemplo:
C:\>baralhar
Vector identidade: 0 1 2 3 4 5 6 7 8 9
Vector baralhado: 41 486 348 581 249 804 550 568 186 689
Na posicao 250, 500, 750: xxx xxx xxx

3
25
25
25
48

90

Parte II Funes, Vectores e Recurso

Pergunta: crie um vector de 1000 elementos com valores de 0 a 999 seguidos, e chame a funo
para os baralhar (chame srand(1)). Indique na resposta os elementos nas posies 250, 500 e 750,
separados por espaos.

mergesort.c
Faa um programa que utiliza uma funo que ordena os elementos de um vector da seguinte
forma: divide o vector em duas metades, ordena cada metade separadamente, e depois junta ambas
as metades ordenadas.
Notas:

Ao juntar duas partes ordenadas, basta ver apenas os elementos no topo de cada metade, j
que ambas as metades esto ordenadas.
Como h duas operaes separadas, ordenar parte do vector e juntar duas partes de um
vector, mais fcil criar uma funo para cada uma destas operaes.
Para a pergunta em baixo, ao declarar um vector de 1.000.000 de elementos, utilize a
keyword static, ou uma varivel global, de forma a evitar que o vector fique no stack7 uma
vez que muito grande.

Execuo de exemplo:
C:\>mergesort
Ordenado de 0 a 124999
Ordenado de 125000 a 249999
Ordenado de 0 a 249999
Ordenado de 250000 a 374999
Ordenado de 375000 a 499999
Ordenado de 250000 a 499999
Ordenado de 0 a 499999
Ordenado de 500000 a 624999
Ordenado de 625000 a 749999
Ordenado de 500000 a 749999
Ordenado de 750000 a 874999
Ordenado de 875000 a 999999
Ordenado de 750000 a 999999
Ordenado de 500000 a 999999
Ordenado de 0 a 999999
Quantis: xxx xxx xxx

Pergunta: gerando agora um vector aleatrio com 1.000.000 de elementos, com valores de 0 a 999
(utilize rand, e chame srand(1)), utilize a funo de ordenao desenvolvida para ordenar o
vector e retorne os quantis de 25, 50 e 75% (valores nas posies 250000, 500000, e 750000),
separados por espaos.

O stack ser dado no captulo Memria

2) Exerccios

91

binarysearch.c
Faa um programa que utiliza uma funo que recebe um vector ordenado, tamanho e um
elemento, e retorna a posio em que o elemento se encontra (ou -1 se no estiver no vector),
efectuando uma procura binria: primeiro verifica a posio do meio, se for igual retorna, caso
contrrio selecciona apenas o segmento em que poder estar o elemento ( esquerda ou direita do
valor testado), e volta a testar a posio do meio desse segmento. O processo continua
recursivamente at que o segmento apenas tenha um elemento.
Notas:

Inclua o cdigo do exerccio mergesort.c de modo a obter o vector de elementos ordenados,


e coloque uma mensagem no incio da funo recursiva.

Execuo de exemplo:
C:\>binarysearch
Procurar 250 entre
Procurar 250 entre
...
Procurar 500 entre
Procurar 500 entre
...
Procurar 750 entre
Procurar 750 entre
...
Posicao de 250, 500

0 e 999999
0 e 499998
0 e 999999
500000 e 999999
0 e 999999
500000 e 999999
e 750: 251952 503905 755858

Pergunta: utilizando o mesmo vector ordenado do exerccio anterior, procure as posies dos
valores 250, 500 e 750. Indique o nmero de chamadas recursivas para cada uma das procuras,
separadas por espaos.

numeracaoromana.c
Faa um programa que utiliza duas funes, uma para converter um nmero em numerao
romana, e outro para a operao inversa. Relembra-se as correspondncias entre letras e nmeros:
M=1000; D=500; C=100; L=50; X=10; V=5; I=1. No sistema de numerao romano as letras devem
situar-se da ordem de maior valor para a de menor valor. No se deve escrever mais de trs I, X ou C
em qualquer nmero. Se estas letras se situam esquerda de um V, L ou D, subtrai-se o seu valor ao
das respectivas letras. Exemplo: IX, XC ou XL correspondem a 9, 90 e 40 respectivamente.
Notas:

Idealize o algoritmo voc mesmo. Caso no vislumbre nenhuma soluo, comece por
considerar o problema at ao nmero 10.

92

Parte II Funes, Vectores e Recurso

Execuo de exemplo:
C:\>numeracaoromana
Conversao de numeracao arabe para romana e vice-versa.
Numero arabe: 1234
234 M
134 MC
34 MCC
24 MCCX
14 MCCXX
4 MCCXXX
0 MCCXXXIV
Resultado: MCCXXXIV
Numero romano: MCCXXXIV
1000 CCXXXIV
1100 CXXXIV
1200 XXXIV
1210 XXIV
1220 XIV
1230 IV
1234
Resultado: 1234
Caracteres dos numeros de 1 a 999, em numeracao romana: xxxx

Pergunta: calcule o nmero de caracteres romanos necessrios para os nmeros de 1 a 999 (somar
o nmero de letras desses nmeros).

pokerdados.c
Faa um programa para o jogo do Poker de Dados. Joga-se com 5 dados, em que um jogador
lana os dados (cada dado tem um valor de 1 a 6), podendo relanar qualquer nmero de dados
uma segunda vez. Ganha o jogador que ficar com o melhor lanamento. Pretende-se que simule o
lanamento de um jogador neste jogo, podendo o jogador escolher os dados a relanar, e diga qual o
resultado. Os resultados so os seguintes: poker real = 5 iguais; poker quadruplo = 4 iguais; fullen =
trio + par; sequncia = 1 a 5 ou 2 a 6; trio = 3 iguais; duplo par = par + par; par = 2 iguais; nada =
no se verifica nenhum dos casos anteriores.
Notas:

Faa uma funo que para um conjunto de 5 dados, retorne o resultado do jogo.

Execuo de exemplo:
C:\>pokerdados
Valores dos dados lancados: 6 1 2 1 1
Frequencia dos valores: 3 1 0 0 0 1 (trio)
Retencao (1-reter, 0-descartar): 01011
Valores dos dados finais: 4 1 2 1 1
Frequencia dos valores: 3 1 0 1 0 0 (trio)
nada: xx
par: xxx
duplo par: xxx

Pergunta: para responder a esta pergunta chame srand(1), e faa 1000 lanamentos de dados, e
indique quantas vezes saiu: nada; par; duplo par. Coloque na resposta estes trs valores separados
por espaos.

2) Exerccios

93

strreplace.c
Faa um programa que utiliza uma funo que substitui numa string, todas as ocorrncias de
uma string A por uma string B, assumindo que h espao devidamente alocado (no h realocao
de strings).
Notas:

Se a string B conter A, a string em A em B no deve ser substituda recursivamente.

Execuo de exemplo:
C:\>strreplace
Substituir num texto, as ocorrencias de uma string A por uma string B.
Texto: texto a transformar troco o "o" por "os"
String A: o
String B: os
textos a transformar troco o "o" por "os"
textos a transformar trocos o "o" por "os"
textos a transformar trocos os "o" por "os"
Resultado (3 trocas): textos a transformar trocos os "o" por "os"

Nas strings introduzidas o e os adicionou-se um espao.


Pergunta: Coloque na resposta o resultado da substituio de "xju" por "uxjuc" em "sdxjukflj
sxjuxjudfgklj sdfli sdlfkxjuj erlijs sdfikxjxjuulsj xju xdvflkj wsefrlij wefsxjuld dfg".

ndamas.c
Faa um programa que pea um nmero N, e liste todas as possveis posies para colocar N
damas, num tabuleiro de Xadrez de lado N, sem que nenhuma dama ataque outra (mesma diagonal,
linha ou coluna). Faa um programa recursivo. Mostre apenas a primeira posio, e conte quantas
posies existem.
Notas:

Considere um valor mximo de 16 damas

Execuo de exemplo:
C:\>ndamas
Conta quantas posicoes existem, num tabuleiro de NxN, para colocacao de
N damas sem que estas se ataquem mutuamente.
Indique N (maximo 16): 5
+---------------+
| # . . . . |
| . . # . . |
| . . . . # |
| . # . . . |
| . . . # . |
+---------------+
Total: 10

94

Parte II Funes, Vectores e Recurso

Pergunta: quantas posies existem para 13 damas?

doisdados.c
Escreva um programa que lance dois dados um nmero elevado de vezes, e registe a
frequncia absoluta da soma dos dados. Faa tambm uma funo para apresentar um grfico de
barras com o histograma, que escale os valores a apresentar de forma a utilizar 16 linhas, utilizando
uma coluna por cada valor da srie, neste caso, um resultado de lanamento de dados, sendo o valor
o nmero de lanamentos.
Notas:

Faa uma funo separada e a mais genrica possvel para representar o grfico de barras
Como tem de imprimir o grfico na horizontal, tem de imprimir o grfico de barras para um
vector bidimensional antes de o imprimir

Execuo de exemplo:
C:\>doisdados
Simulacao de 10000 lancamentos de dois dados:
1677|
# xxxx
1565|
# xxxx
1453|
## xxxx
1342|
## xxxx
1230|
###xxxx
xxxx
1118|
###xxxx
xxxx
1006|
####xxxx
xxxx
894|
####xxxx
xxxx
783|
####xxxx
xxxx
671| #####xxxx
xxxx
559| #####xxxx
xxxx
447| ######xxxx
xxxx
335| ######xxxx
xxxx
224|#######xxxx
xxxx
112|#######xxxx
xxxx
0|###########
+----------23456789DOD
Valores: 250 546 780 1117 1468 1677 1330 xxxx xxx xxx xxx
Numero de caracteres # presentes no histograma: xx

Pergunta: faa o histograma utilizando 10.000 lanamentos, e chamando srand(1). Indique o


nmero de cardinais no histograma.

calendario.c
Faa um programa que imprima um calendrio mensal, para um ano e ms introduzidos pelo
utilizador, com o dia da semana nas colunas, e atendendo s seguintes regras e feriados nas notas.
Notas:

2) Exerccios

95

Determinao de ano bissexto: de 4 em 4 anos, um ano bissexto; de 100 em 100 anos no


ano bissexto; de 400 em 400 anos ano bissexto; prevalecem as ltimas regras sobre as
primeiras.
o Exemplo: O ano 2000 um ano bissexto, sendo 1 de Janeiro a um sbado
Dias dos meses: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
Pscoa8:
a = ano Mod 19
b = ano \ 100 (diviso inteira)
c = ano Mod 100
d=b\4
e = b Mod 4
f = (b + 8) \ 25
g = (b - f + 1) \ 3
h = (19 * a + b - d - g + 15) Mod 30
i=c\4
k = c Mod 4
l = (32 + 2 * e + 2 * i - h - k ) Mod 7
M = (a + 11 * h + 22 * l) \ 451
Ms = (h + l - 7 * M + 114) \ 31
Dia = ((h + l - 7 * M + 114) Mod 31) + 1
2-feira de Carnaval: Pscoa - 48 dias
3-feira de Carnaval: Pscoa - 47 dias
6-feira Santa: Pscoa - 2 dias
Corpo de Deus: Pscoa + 60 dias
Feriados fixos: ano novo 1/1; dia da liberdade 25/4; dia do trabalhador 1/5; dia de Portugal
10/6; implantao da Repblica 5/10; restaurao da independncia 1/12; Natal 25/12.
Faa vrias pequenas funes, uma por cada funcionalidade que necessite.

Execuo de exemplo:
C:\>calendario
Calendario mensal, com indicacao de feriados.
Indique ano mes:2010 10
Outubro de 2010:
#######################
# D S T Q Q S S #
#######################
#
1 2 #
# 3 4 F 6 7 8 9 #
#10 11 12 13 14 15 16 #
#17 18 19 20 21 22 23 #
#24 25 26 27 28 29 30 #
#31
#
#######################
Resposta: xxxx

Pergunta: some para o ms de Fevereiro, desde 2000 a 2099, o nmero de feriados, o nmero de
dias do ms, e o dia da semana para o dia 1 (Domingo vale 0, Sbado vale 6). Coloque o resultado na
resposta.

Fonte: http://pt.wikipedia.org/wiki/C%C3%A1lculo_da_P%C3%A1scoa

96

Parte II Funes, Vectores e Recurso

editdistance.c
Faa um programa que utiliza uma funo que recebe duas strings e retorna a distncia de
edio (mostrando informao passo a passo), de acordo com o seguinte algoritmo9:
m[i,j] = distance(s1[1..i], s2[1..j])
m[0,0] = 0
m[i,0] = i, i=1..|s1|
m[0,j] = j, j=1..|s2|
for i=1..|s1|
for j=1..|s2|
if s1[i]=s2[j] then
m[i,j] = m[i-1,j-1]
else
m[i,j] = min(
m[i-1,j-1] + 1, /* substituio */
m[i-1, j] + 1, /* remoo
*/
m[i, j-1] + 1 ) /* insero
*/

Notas:

Considere strings at um tamanho mximo, 256

Execuo de exemplo:
C:\>editdistance
Calculo da distancia de edicao entre duas strings.
Indique s1: Portugal
Indique s2: Brasil
Matriz de distancias:
B r a s i l
0 1 2 3 4 5 6
P 1 1 2 3 4 5 6
o 2 2 2 3 4 5 6
r 3 3 2 3 4 5 6
t 4 4 3 3 4 5 6
u 5 5 4 4 4 5 6
g 6 6 5 5 5 5 6
a 7 7 6 5 6 6 6
l 8 8 7 6 6 7 6
Operacoes:
Portugal (l) Portugal
Portugal [-a] Portugl
Portugl [g/i] Portuil
Portuil [u/s] Portsil
Portsil [t/a] Porasil
Porasil (r) Porasil
Porasil [-o] Prasil
Prasil [P/B] Brasil
Apagar: 2, Inserir: 0, Substituir: 4
Resultado: 6

Pergunta: Calcule as trs distncias de edio entre: "Sport Lisboa e Benfica", "Sporting
Clube de Portugal", e "Futebol Clube do Porto". Indique na resposta para cada uma das
distncias, separado por espaos, o nmero de substituies.

Fonte: http://en.wikipedia.org/wiki/Levenshtein_distance

PARTE III MEMRIA, ESTRUTURAS E


FICHEIROS
Na Parte II pouco foi dito sobre a gesto da memria, as variveis relacionadas entre si eram
declaradas em separado, nem to pouco se falou em ficheiros. Na Parte III vamos finalmente
abordar estas questes, reservando ainda um captulo para matria menos relevante mas
caracterstica da linguagem C, a que se chamou de Truques. Mais relevante e complementar
abstraco funcional a abstraco de dados, que introduzida nesta parte, sendo essencial
utilizar nas grandes aplicaes. A abstraco de dados a base da programao por objectos. No
final desta parte estar munido de todo o conhecimento que necessita de saber para construir
aplicaes em programao estruturada.

100

Parte III Memria, Estruturas e Ficheiros

10.

MEMRIA

At este momento o conhecimento sobre a memria num programa resume-se no seguinte: sempre
que necessrio, declaram-se variveis no incio das funes, que na forma de vectores podem ser
arbitrariamente grandes.
Nada mais haveria a dizer, no fossem os seguintes problemas:

Todo o espao necessrio a cada momento tem de ser conhecido antes de compilar o
programa (em tempo de compilao / compile time);
No pode uma funo criar memria para ser utilizada na funo que a chamou, toda a
memria que uma funo pode aceder, tem de ser declarada no incio da funo.

O primeiro problema deveras desagradvel quando no se sabe partida o tamanho de um


vector. Para o resolver, at aqui declarava-se uma macro com um tamanho por excesso, a utilizar na
declarao do vector de forma a facilitar a sua alterao se necessrio. No caso de o programa
necessitar um valor maior enquanto est a correr (em tempo de corrida / run time), tem-se de
informar o utilizador que o programa no tinha sido compilado a contar com memria suficiente
para satisfazer o seu pedido.
O valor por excesso pode levar a que exista um desperdcio de memria, inviabilizando vrias
aplicaes a correr em simultneo apenas porque tm uma reserva de memria feita em tempo de
compilao, e no quando for conhecido o valor necessrio em tempo de corrida.
Vamos ver uma verso generalizada do exemplo das semanas no captulo Vectores, em que se pede
para introduzir valores de dimenso varivel, obtendo-se no final a mdia e o desvio padro.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#include <stdio.h>
#include <math.h>
#define MAXVECTOR 10
int main()
{
float valor[MAXVECTOR];
float media=0,variancia=0;
int i,n;
printf("Quantos valores tem o vector: ");
scanf("%d",&n);
if(n<=0 || n>MAXVECTOR)
{
printf("Programa compilado para permitir no maximo %d elementos.\n",
MAXVECTOR);
return;
}
/* introduo de valores */
for(i=0;i<n;i++)
{
printf("Valor %d: ",i);
scanf("%f",&valor[i]);
}
/* calculos */
for(i=0;i<n;i++)
media+=valor[i];
media/=n;

10. Memria
32
33
34
35
36
37
38 }

101

for(i=0;i<n;i++)
variancia+=(media-valor[i])*(media-valor[i]);
variancia/=n;
printf("Media: %g\n",media);
printf("Desvio padrao: %g\n",sqrt(variancia));
Programa 10-1 Generalizao do Programa 7-2, calculando a mdia e o desvio padro

Execuo de exemplo:
C:\>media
Quantos valores tem o vector: 3
Valor 0: 123
Valor 1: 12
Valor 2: 10
Media: 48.3333
Desvio padrao: 52.8036
C:\>media
Quantos valores tem o vector: 11
Programa compilado para permitir no maximo 10 elementos.

Na primeira execuo, precisvamos 3 valores, mas foi utilizado um vector de 10, e na segunda
execuo, eram precisos 11 valores, mas o programa no estava compilado para poder retornar a
mdia e o desvio padro de um conjunto de 11 valores. Em todo o caso prefervel assim a fazer um
acesso fora do espao alocado.
O segundo problema de maior gravidade, uma vez que quanto muito uma funo pode
eventualmente utilizar memria j alocada nas funes que j a chamaram, acedendo memria de
outras funes por referncia, atravs de apontadores, mas nunca pode ela prpria criar memria
para que as restantes funes a utilizem, inviabilizando abstraces de funes que necessitem
criar memria.
Por exemplo, poderamos estar interessados em fazer isto:
/* retorna um vector de inteiros com dimenso n, com valores aleatrios */
? VectorAleatorio(int n);

Em vez disso tem que se criar o vector com a dimenso n, e passar como argumento junto com o
valor n.

102

Parte III Memria, Estruturas e Ficheiros

STACK
Para resolvermos os dois problemas enunciados, necessrio saber como que um programa
realmente cria o espao necessrio para as suas variveis. Para tal vamos olhar de novo para o
primeiro exemplo de recurso, no por causa do cdigo, mas pela execuo passo-a-passo.
Em cada um dos 26
passos, o programa
necessita de ter o cdigo
na memria, e tem a
linha em que est
actualmente, o que
permite
obter
a
instruo actual. H
tambm um espao para
"Resultado", aps o qual
h um nmero varivel
de colunas necessrias
para registar o valor das
variveis. Ora, ao espao
necessrio
para
escrever uma linha
nesta execuo passo-apasso,
corresponde
tambm espao em
memria necessrio pelo computador. H os registos internos fixos, um deles tem realmente a
instruo a executar e outros podem ser utilizados para valores temporrios, como os colocados na
coluna "Resultado", sendo estes registos dependentes da arquitectura do computador.
Algo que comum a todos os programas a correr em computadores actuais, o espao necessrio
de memria, mais vasto que registos, e de dimenso varivel, para poder guardar o valor das
variveis declaradas e as funes que so chamadas: stack (pilha). Este espao de memria tem o
seu nome dado que as variveis so colocadas em posies de memria consecutivas pela ordem
que so declaradas, tal como na execuo passo-a-passo se coloca uma nova varivel na prxima
coluna livre, mas por outro lado, quando a funo retorna, o espao para as variveis declaradas
nessa funo libertado, ficando disponvel para outras funes. Na execuo acima, pode-se ver
que a funo FactorialI, utilizou as mesmas colunas que eram utilizadas pela funo
FactorialR, que tinha corrido anteriormente, mas como j tinha terminado no necessitava mais
de memria.
H problemas de memria relacionados apenas com o stack que convm realar. Note-se que o
prprio stack um bloco de memria, e como tal foi alocado. Se o programa abusar do stack, pode
acontecer que no exista espao no stack para alocar mais variveis, tal como a folha de papel na
execuo passo-a-passo pode atingir a borda. Isto pode acontecer se alocar variveis muito grandes,
ou se a recurso for exagerada, provocando muita alocao de memria.
Vamos continuar no exemplo do factorial, para ter um problema de falta de espao no stack.

10. Memria
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

103

#include <stdio.h>
int FactorialR(int n)
{
if(n<2)
return 1;
return FactorialR(n-1)*n;
}
int FactorialI(int n)
{
int i, resultado=1;
for(i=2;i<=n;i++)
resultado*=i;
return resultado;
}
int main()
{
int n=1000;
printf("Factorial de %d: %d = %d", n, FactorialR(n), FactorialI(n));
}
Programa 10-2 Rplica do Programa 9-1

Execuo de exemplo para n=1000:


C:\>factorial
Factorial de 1000: 0 = 0

O valor zero normal, j que o valor do factorial largamente ultrapassa a dimenso representvel
por um nmero de 4 bytes, e assim que o resultado acumulado seja nulo, ser sempre nulo at ao
fim do produto. O importante aqui foi verificar que ambas as formas de clculo do factorial
correram bem e retornaram correctamente.
Execuo de exemplo para n=1000000:
C:\>factorial

O programa abortou. A verso recursiva estoirou o stack, se utilizar apenas a verso iterativa o
resultado fornecido. Repare-se que a verso recursiva para n=1000000 significa 1000000 de
chamadas, tendo que ser reservado para cada uma o apontador de onde a funo foi chamada, e o
espao para guardar o parmetro n. A verso iterativa chamada uma s vez, e aloca apenas espao
para as suas variveis locais uma s vez.
O stack no o local apropriado para guardar muita memria. Do exemplo, conclui-se que no se
deve contar com um nmero infindvel de chamadas recursivas de funes, porque em cada
chamada tem que se contar com o espao para declarar as variveis locais da funo, e o stack tem
limites.

104

Parte III Memria, Estruturas e Ficheiros

HEAP
Para resolver os problemas enunciados, temos de ter uma forma de alocar memria, e uma vez
alocada, quando j no for precisa tem que se poder libertar, em vez de se declarar variveis no
stack sendo estas libertas automaticamente quando a funo retorna. Existem precisamente duas
funes para este efeito, uma para alocar memria malloc e outra para libertar memria alocada
pelo malloc: free10.
void *malloc(int);
void free(void*);
malloc retorna um apontador para um tipo void, apontando para a memria alocada.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
void Media(int n)
{
float *valor;
float media=0,variancia=0;
int i;
valor=(float*)malloc(sizeof(float)*n);
if(valor!=NULL)
{
/* introduo de valores */
for(i=0;i<n;i++)
{
printf("Valor %d: ",i);
scanf("%f",&valor[i]);
}
/* calculos */
for(i=0;i<n;i++)
media+=valor[i];
media/=n;
for(i=0;i<n;i++)
variancia+=(media-valor[i])*(media-valor[i]);
variancia/=n;
printf("Media: %g\n",media);
printf("Desvio padrao: %g\n",sqrt(variancia));
free(valor);
} else
printf("Memoria insuficiente para alocar %d elementos.\n",
n);
}
int main()
{
int n;
printf("Quantos valores tem o vector: ");
scanf("%d",&n);
Media(n);
}
Programa 10-3 Calculo da mdia e desvio padro, alocando apenas a memria necessria

10

As funes malloc e free pretencem biblioteca stdlib.h

10. Memria

105

Execuo de exemplo:
C:\>media2
Quantos valores tem o vector: 3
Valor 0: 123
Valor 1: 12
Valor 2: 10
Media: 48.3333
Desvio padrao: 52.8036
C:\>media2
Quantos valores tem o vector: 11
Valor 0: 11
Valor 1: 2
Valor 2: 2
Valor 3: 3
Valor 4: 4
Valor 5: 5
Valor 6: 6
Valor 7: 7
Valor 8: 23
Valor 9: 1
Valor 10: 3
Media: 6.09091
Desvio padrao: 5.99173
C:\>media2
Quantos valores tem o vector: 1000000000
Memoria insuficiente para alocar 1000000000 elementos.

O espao alocado foi o necessrio para efectuar as operaes pretendidas.


Ao pedir um valor muito elevado para o vector, na ltima execuo, a funo malloc no consegue
reservar memria, e retorna NULL, dando erro, mas no dependente dos valores utilizados na
compilao, mas sim dos recursos disponveis na mquina quando o programa correu. O heap tem
limites, se no consegue alocar memria a funo malloc retorna NULL.
Na linha 10, o valor retornado pelo malloc do tipo void*, mas na verdade pretendido um tipo
float*. lcito efectuar-se a atribuio, sendo convertido o tipo void* para float*, mas a
instruo tem um cast para (float*) para que o compilador force a converso na expresso
mesmo antes da atribuio, e por outro lado ao ler-se o cdigo no se tenha dvidas, que a
converso pretendida e no se trata de um erro de atribuio entre variveis de tipos distintos. O
cast j poderia ter sido referido anteriormente, dado que se pode utilizar em qualquer parte de uma
expresso para a converter para o tipo pretendido, no entanto a sua importncia maior no
contexto dos apontadores. Por exemplo para fazer a diviso real entre duas variveis inteiras x e y,
pode-se utilizar a seguinte expresso: (float)x/y. Para fazer a diviso inteira e converter o
resultado para float, ser: (float)(x/y).
Vamos agora fazer um programa que vai lendo strings, e as junta numa s string:
1
2
3
4
5
6
7
8
9
10

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSTR 255
char *Concatenar(char *str, char *str2)
{
char *pt;
/* duplicar str2 se str nulo */

106
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

Parte III Memria, Estruturas e Ficheiros


if(str==NULL)
{
pt=(void *)malloc(strlen(str2)+1);
if(pt!=NULL)
strcpy(pt,str2);
} else {
pt=(void *)malloc(strlen(str)+strlen(str2)+1);
if(pt!=NULL)
{
strcpy(pt,str);
strcat(pt,str2);
free(str);
}
}
return pt;
}
int main()
{
char *texto=NULL, str[MAXSTR];
do {
gets(str);
texto=Concatenar(texto,str);
} while(strlen(str)>0);
printf("%s",texto);
free(texto);
}
Programa 10-4 Concatena as strings introduzidas, utilizando apenas a memria necessria

Execuo de exemplo:
C:\>strings

primeira linha
segunda linha
ultima linha

primeira linhasegunda linhaultima linha

A vantagem neste exemplo, permitir ir alocando memria conforme as necessidades. Se o


utilizador escrever pouco texto, o programa necessita de pouca memria, requisitando mais
memria medida que o utilizador escreve mais texto.
Repare-se que a funo Concatenar, no s liberta o espao que j no necessrio, como aloca o
novo espao, e retorna o apontador para que na funo main o apontador se mantenha actualizado.
Pode-se agora fazer abstraces funcionais, que aloquem memria, como por exemplo a criao de
um vector de inteiros aleatrios:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include <stdio.h>
#include <stdlib.h>
int *VectorAleatorio(int n, int base)
{
int *vector,i;
vector=(int*)malloc(sizeof(int)*n);
if(vector!=NULL)
{
for(i=0;i<n;i++)
vector[i]=rand()%base;
}
return vector;
}
void MostraVector(int *vector, int n)
{
int i;
for(i=0;i<n;i++)

10. Memria
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

107

printf("%d ",vector[i]);
}
int main()
{
int *vector,n,base;
srand(1);
printf("Dimensao: ");
scanf("%d",&n);
printf("Valor maximo: ");
scanf("%d",&base);
vector=VectorAleatorio(n,base);
if(vector!=NULL)
{
MostraVector(vector,n);
free(vector);
}
}
Programa 10-5 Gerao de um vector aleatrio

Execuo de exemplo:
C:\>vectoraleatorio
Dimensao: 10
Valor maximo: 10
1 7 4 0 9 4 8 8 2 4

O vector alocado na funo VectorAleatorio foi passado para a funo MostraVector, sem
problema. Pode-se na funo main abstrair dos detalhes no s da inicializao do vector, como
tambm da sua criao.
Toda esta flexibilidade no vem sem um preo. Temos agora uma nova dimenso nos erros
possveis com memria, ao ponto de outras linguagens de programao tomarem o seu espao
devido a terem formas automticas de evitar parte destes problemas.
Vamos considerar os seguintes erros:

Ler uma posio de memria no alocada;


Alterar uma posio de memria no alocada (e possivelmente em uso);
No libertar memria aps alocar;
Libertar memria de um endereo invlido:
o Endereo nulo;
o Endereo no alocado com o malloc (pode ser do stack, ou conter lixo);
o Endereo j libertado anteriormente.

O primeiro erro pode acontecer da seguinte maneira: pode-se por algum motivo no alocar
memria (por falta de memria ou por no chamar a funo malloc), e mesmo assim ler-se o
contedo:
27 ...
28 int main()
29 {
30
char *texto=NULL, str[MAXSTR];
31
do {
32
gets(str);
33
texto=Concatenar(texto,str);
34
} while(strlen(str)>0);
35
free(texto);

108

Parte III Memria, Estruturas e Ficheiros

36
37
38 }

texto=NULL;
printf("%s",texto);

Execuo de Exemplo:
C:\>strings2
primeira linha
segunda linha
terceira linha
(null)

Repare-se que neste caso, a string foi libertada e atribudo o valor NULL, e depois foi tentado aceder
string na posio de memria no alocada. O texto impresso "(null)". De onde vem este texto?
Como j foi referido no captulo Procedimentos, so estes realmente os caracteres que esto nos
primeiros bytes de memria, dado que este erro comum e desta forma o programador sabe que
est a imprimir um apontador para NULL.
Outra forma deste erro ocorrer pedir-se uma posio de memria acima do vector que foi alocado.
Vamos supor que no exemplo dos vectores aleatrios, a funo MostraVector tinha um nmero
desfasado relativamente ao ndice correcto.
15
16
17
18
19
20
21
22

...
void MostraVector(int *vector, int n)
{
int i;
for(i=1;i<=n;i++)
printf("%d ",vector[i]);
}
...

Execuo de exemplo:
C:\>vectoraleatorio2
Dimensao: 10
Valor maximo: 10
7 4 0 9 4 8 8 2 4 393364

O ltimo valor no foi gerado, foi lido de uma posio de memria no alocada, e estava o valor
393364 como poderia estar outro qualquer, depende da ltima utilizao desse endereo de
memria.
Este tipo de erro normalmente no leva ao crash da aplicao, excepto se o valor lido incorrecto for
o de um apontador, dado que nesse caso pode-se de seguida ler ou escrever outros valores a partir
desse apontador que est incorrecto.
O segundo erro tem normalmente efeitos mais devastadores que o primeiro, dado que pode ter
consequncias num local completamente distinto do local onde est o erro. O endereo pode ser no
espao em que est cdigo, o que tem consequncias imprevisveis. Vamos supor que ao alocar
espao para as strings, no se contava com o carcter terminador.

10. Memria
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

109

...
char *Concatenar(char *str, char *str2)
{
char *pt;
/* duplicar str2 se str nulo */
if(str==NULL)
{
pt=(void *)malloc(strlen(str2));
if(pt!=NULL)
strcpy(pt,str2);
} else {
pt=(void *)malloc(strlen(str)+strlen(str2));
if(pt!=NULL)
{
strcpy(pt,str);
strcat(pt,str2);
free(str);
}
}
return pt;
}
...

Execuo de exemplo:
C:\>strings3

primeira linha
segunda linha
terceira linha

primeira linhasegunda linhaterceira linha

Por acaso no houve problema, porque o espao de memria utilizado indevidamente no foi
utilizado para mais nada. Vamos agravar a situao, supondo que se escrevia para a posio de
memria NULL, o que pode acontecer se no se verificar se a operao de alocao de memria foi
bem sucedida.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

...
char *Concatenar(char *str, char *str2)
{
char *pt;
/* duplicar str2 se str nulo */
if(str==NULL)
{
pt=(void *)malloc(strlen(str2)+1);
if(pt!=NULL)
strcpy(pt,str2);
} else {
pt=(void *)malloc(strlen(str)+strlen(str2)+1);
if(pt!=NULL)
{
pt=NULL;
strcpy(pt,str);
strcat(pt,str2);
free(str);
}
}
return pt;
}
...

110

Parte III Memria, Estruturas e Ficheiros

Execuo de exemplo:
C:\>strings4

primeira linha
segunda linha

"An unhandle win32 exception occurred in strings4.exe [1488]"

A mensagem de erro varia conforme o sistema operativo, e a sua forma de lidar com estas situaes.
O programa no entanto que deveria ter o cuidado de evitar que esta situao ocorra.
O terceiro erro ocorre quando se cria memria em diversos locais, mas para cada bloco no
garantido que seja chamada a funo de libertao free. O apontador para o bloco pode at ficar
com outro bloco, ficando blocos de memria perdidos.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSTR 255
char *Concatenar(char *str, char *str2)
{
char *pt;
/* duplicar str2 se str nulo */
if(str==NULL)
{
pt=(void *)malloc(strlen(str2)+1);
if(pt!=NULL)
strcpy(pt,str2);
} else {
pt=(void *)malloc(strlen(str)+strlen(str2)+1);
if(pt!=NULL)
{
strcpy(pt,str);
strcat(pt,str2);
}
}
return pt;
}
int main()
{
char *texto=NULL, str[MAXSTR];
do {
gets(str);
texto=Concatenar(texto,str);
} while(strlen(str)>0);
printf("%s",texto);
}

Execuo de exemplo:
C:\>strings5

primeira linha
segunda linha
terceira linha

primeira linhasegunda linhaterceira linha

Nenhuma das alocaes libertada. Ficam blocos de memria alocados perdidos at que a aplicao
acabe. No h erro da aplicao. Se existirem muitos blocos de memria no libertos, a memria
acabar por se esgotar, ou pelo menos, a aplicao utiliza mais memria que necessita, e que
poderia ser utilizada em outras aplicaes. Nas aplicaes que correm durante muito tempo, por
exemplo em servidores, no pode haver nenhum bloco alocado no liberto, porque com o tempo de
corrida acabar sempre por esgotar a memria disponvel. Para que este problema no ocorra, tem

10. Memria

111

que se ter em mente quando se aloca memria, o local onde esse bloco ser liberto, nem que seja no
final do programa.
O quarto erro o que tem efeitos normalmente de crash da aplicao, mas o efeito varia conforme o
ambiente em que corre. Em todo o caso deve-se evitar que ocorram.
Libertao de o apontador para NULL:
1
2
3
4
5
6
7
8

#include <stdio.h>
#include <stdlib.h>
int main()
{
free(NULL);
printf("Libertacao ok");
}

Libertao de um apontador para um vector no stack:


1
2
3
4
5
6
7
8
9

#include <stdio.h>
#include <stdlib.h>
int main()
{
int vector[10];
free(vector);
printf("Libertacao ok");
}

Libertao dupla do mesmo bloco de memria:


1
2
3
4
5
6
7
8
9
10
11

#include <stdio.h>
#include <stdlib.h>
int main()
{
int *vector;
vector=(int*)malloc(sizeof(int)*10);
free(vector);
free(vector);
printf("Libertacao ok");
}

A ltima forma de erro normalmente a mais comum. Com medo de que um bloco no seja liberto,
por vezes h mais que um local onde o mesmo bloco liberto. Uma boa prtica aps libertar um
bloco de memria, atribuir o apontador para NULL ou outro valor. Como o anterior bloco de
memria j no vlido, e poder ser utilizado em outra funo, no faz sentido manter um
apontador a referenci-lo.
A linguagem C no faz testes de acessos fora dos vectores ou a posies no alocadas. Dada a sua
caracterstica de estar perto da mquina, no seria lcito retirar tempo de processamento numa
operao to comum como o acesso a uma posio de memria, pelo que o programador que deve
garantir que o seu programa no acede fora das zonas alocadas. Os sistemas operativos modernos
fazem no entanto essas verificaes ao nvel do programa, de forma a garantir que um programa
no acede a espao de memria que no lhe pertence, o que a acontecer poderia representar um
risco de segurana.

112

Parte III Memria, Estruturas e Ficheiros

Erros comuns mais directamente associados a este captulo:

Atribuies entre variveis de tipos distintos


o Forma: Principalmente quando se utiliza apontadores, por vezes necessrio fazer
atribuies entre apontadores de tipos distintos, ou entre apontadores e inteiros.
Isso no problema porque um apontador um nmero de 32 bits.
o Problema: Se atribui um apontador de um tipo a outro, este vai aceder ao endereo
de memria apontado como se fosse o segundo tipo, quando este foi alocado e tem
dados como se fosse o primeiro tipo. Pode ter consequncias imprevisveis, uma vez
que os dados ficam com o acesso incoerente. Em caso algum deve assumir o tipo do
apontador, uma vez que tal pode variar conforme o compilador/computador em que
o cdigo for utilizado
o Resoluo: Se for necessria atribuio entre apontadores de tipos distintos, ao
alocar memria por exemplo, ento deve utilizar no cdigo um cast para o tipo de
destino, de forma a clarificar que no se trata de um erro. Fora a situao de
alocao de memria, no precisa de qualquer outra atribuio entre tipos distintos.
Em situaes menos ortodoxas, pode utilizar o tipo union, para fundir dados de
mais de um tipo
No libertar memria aps alocar
o Forma: Atendendo a que no h erros por no libertar memria, e sendo a aplicao
de curta durao, justifica-se a no libertao da memria, j que quando a aplicao
terminar tudo libertado.
o Problema: A aplicao tem os endereos dos blocos de memria que alocou, j no
precisa deles, pode libert-los. Se no o fizer, no h garantia da altura em que esses
blocos sero libertos, nem se o custo computacional de os libertar o mesmo. O
argumento da pequena aplicao no deve ser justificao para qualquer erro, o
cdigo que funciona em pequenas aplicaes poder ser reutilizado em outras
aplicaes, e se for bem feito, no necessitar de alteraes
o Resoluo: Tenha sempre ateno ao alocar memria do local exacto onde esse
bloco ser certamente libertado
Alocar memria e no testar se a operao foi bem sucedida
o Forma: A alocao de memria uma operao comum, e se o programa no utiliza
grandes quantidades de memria, sempre bem sucedida, pelo que no vale a pena
efectuar a verificao j que complica desnecessariamente o cdigo.
o Problema: Mesmo que a aplicao utilize pouca memria, a alocao de memria
pode falhar se o sistema operativo tiver a memria esgotada por outras aplicaes,
ou por outras instncias da mesma aplicao. A no verificao pode provocar
acesso a zonas de memria proibidas com resultados imprevisveis
o Resoluo: Em todas as alocaes de memria, verifique se estas foram bem
sucedidas, e no caso de falharem, certifique-se que o programa tem um
procedimento vlido

11. Estruturas

11.

113

ESTRUTURAS

Cada varivel identificada por um nome. Quantas forem necessrias, quantas so criadas. No
entanto, por vezes h variveis que esto relacionadas entre si.
No exemplo do nmero de dias do ms, o ano e ms esto relacionadas uma vez que fazem parte de
uma data. No entanto a linguagem C no tem o tipo data, da ter-se de utilizar o cdigo utilizado no
captulo Funes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

#include <stdio.h>
int Bissexto(int ano)
{
return ano%400==0 || ano%4==0 && ano%100!=0;
}
int DiasDoMes(int mes, int ano)
{
if(mes==2)
{
/* teste de ano bissexto */
if(Bissexto(ano))
return 29;
else
return 28;
} else if(mes==1 || mes==3 || mes==5 || mes==7 ||
mes==8 || mes==10 || mes==12)
{
return 31;
} else
{
return 30;
}
}
int main()
{
int ano, mes, dias;
printf("Indique ano: ");
scanf("%d", &ano);
printf("Indique mes: ");
scanf("%d", &mes);
printf("%d",DiasDoMes(ano,mes));
}
Programa 11-1 Rplica do Programa 5-4

Se em vez de uma data, que tem trs valores numricos, pretendermos guardar por exemplo
informao sobre uma pessoa, com o nome, sexo, data de nascimento, morada, email e telefone, j
seriam muitas variveis relacionadas. Se pretender que o programa registe vrias pessoas, ento
tem-se de ter vrios vectores em paralelo para guardar esta informao. Evidentemente que esta
soluo torna-se facilmente impraticvel, tanto para passar a informao relativa a uma pessoa
para uma funo, como para guardar vrios registos de uma pessoa.

114

Parte III Memria, Estruturas e Ficheiros

As estruturas pretendem precisamente resolver este problema, permitindo agrupar variveis


relacionadas entre si.
struct
{
int ano, mes, dia;
} data;

A varivel data declarada no como sendo o tipo int, mas como sendo do tipo:
struct { int ano, mes, dia; }

O acesso varivel deve ser feito a cada campo da varivel, atravs do operador ".":
data.ano=2010;
data.mes=12;
data.dia=1;

Existe no apenas uma varivel data, mas sim trs variveis: data.ano, data.mes, data.dia11. No
entanto pode-se referenciar a varivel data, e copiar por exemplo o contedo das trs variveis,
para outra varivel do mesmo tipo:
/* copia todos os campos da varivel data para os campos
da varivel data2 */
data2=data;

A varivel data2 teria de ser declarada da mesma forma, tendo que se repetir os campos. Para
evitar esta tarefa, e eventuais erros nos nomes dos campos, pode-se dar nome estrutura e assim
declara-se apenas uma s vez a estrutura, fora de funes, e esta passa a estar disponvel para
declaraes de variveis desse tipo:
struct SData
{
int ano, mes, dia;
};
struct SData data, data2;

Podemos agora reescrever o exemplo do nmero de dias do ms:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
11

#include <stdio.h>
struct SData
{
int ano, mes, dia;
};
int Bissexto(int ano)
{
return ano%400==0 || ano%4==0 && ano%100!=0;
}
int DiasDoMes(struct SData data)
{
if(data.mes==2)
{
/* teste de ano bissexto */
if(Bissexto(data.ano))
return 29;

Inicializao da estrutura na declarao da varivel: struct {int ano,mes,dia;} data={2010,12,1}


={2010,12,1};
={2010,12,1}
colocam-se os valores de cada campo da estrutura pela mesma ordem com que esto declarados.

11. Estruturas
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

115

else
return 28;
} else if(data.mes==1 || data.mes==3 || data.mes==5 || data.mes==7 ||
data.mes==8 || data.mes==10 || data.mes==12)
{
return 31;
} else
{
return 30;
}
}
int main()
{
struct SData data;
int dias;
printf("Indique ano: ");
scanf("%d", &data.ano);
printf("Indique mes: ");
scanf("%d", &data.mes);
printf("%d",DiasDoMes(data));
}
Programa 11-2 Calculo dos dias de um ms/ano, utilizando estruturas

Uma estrutura pode estar dentro de outra. Vejamos o registo de uma pessoa, que utiliza a data de
nascimento:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#include <stdio.h>
#define MAXSTR 255
struct SData
{
int ano, mes, dia;
};
struct SPessoa
{
char *nome;
char sexo;
struct SData nascimento;
char *morada;
char *email;
long telefone;
};
struct SPessoa LerPessoa()
{
struct SPessoa pessoa;
char str[MAXSTR];
printf("Nome: ");
gets(str);
pessoa.nome=(char*)malloc(strlen(str)+1);
strcpy(pessoa.nome,str);
printf("Sexo: ");
scanf("%c",&pessoa.sexo);
printf("Data de nascimento (dd-mm-aaaa): ");
scanf("%d-%d-%d",
&pessoa.nascimento.dia,
&pessoa.nascimento.mes,
&pessoa.nascimento.ano);
printf("Morada: ");
gets(str);
gets(str);
pessoa.morada=(char*)malloc(strlen(str)+1);
strcpy(pessoa.morada,str);
printf("Email: ");
gets(str);
pessoa.email=(char*)malloc(strlen(str)+1);

116
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

Parte III Memria, Estruturas e Ficheiros


strcpy(pessoa.email,str);
printf("Telefone: ");
scanf("%ld",&pessoa.telefone);
return pessoa;
}
void MostraPessoa(struct SPessoa pessoa)
{
printf("\nNome: %s\nSexo: %c\nData de Nascimento: %d-%d-%d\n",
pessoa.nome, pessoa.sexo,
pessoa.nascimento.dia,
pessoa.nascimento.mes,
pessoa.nascimento.ano);
printf("Morada: %s\nEmail: %s\nTelefone: %d\n",
pessoa.morada,
pessoa.email,
pessoa.telefone);
}
void Libertar(struct SPessoa pessoa)
{
free(pessoa.nome);
free(pessoa.morada);
free(pessoa.email);
}
int main()
{
struct SPessoa pessoa;
pessoa=LerPessoa();
MostraPessoa(pessoa);
Libertar(pessoa);
}
Programa 11-3 Registo de uma estrutura com dados de diferentes tipos de pessoas

Execuo de exemplo:
C:\>pessoa
Nome: Joaquim Lima
Sexo: M
Data de nascimento (dd-mm-aaaa): 1-1-1980
Morada: Avenida da Liberdade n.1
Email: joaquim@gmail.com
Telefone: 123456789
Nome: Joaquim Lima
Sexo: M
Data de Nascimento: 1-1-1980
Morada: Avenida da Liberdade n.1
Email: joaquim@gmail.com
Telefone: 123456789

No necessrio passar as estruturas por referncia nos argumentos das funes, e pode-se
retornar uma estrutura sem problema, como se fosse do tipo inteiro. Todas as variveis da
estrutura so copiadas, tanto na passagem de parmetros como ao retornar da funo. Como h
apenas uma varivel pessoa, para poupar tempo de cpia conveniente passar a estrutura por
referncia, de forma a ser copiado apenas um apontador e no todos os campos da estrutura.
Para aceder a um campo de uma estrutura, utiliza-se o operador ".". Se em vez da estrutura
utilizarmos um apontador, tem que se aceder a um campo primeiro com o operador * para
identificar o contedo, e depois o ponto: (*pessoa).nome. Este tipo de anotao pode baixar a
legibilidade do cdigo, no caso de haver muitas estruturas passadas por referncia, como
esperado. Para evitar essa situao, a linguagem C tem um operador ->, equivalente: pessoa>nome

11. Estruturas

117

Pode-se estar interessado em declarar uma varivel de um tipo "inteiro", e no estar interessado
sequer em saber o tipo concreto do inteiro (long, short, int), e querer poder decidir mais tarde,
mas que tal no force a edio de todas as declaraes de variveis desse tipo.
Embora as estruturas permitam criar facilmente variveis de tipos complexos, o termo struct tem
de ser utilizado sempre que se declara uma varivel complexa, e seria interessante em vez de ter
struct SData variavel; se tivesse TData variavel;
A utilizao de macros poderia ser uma opo para resolver estes dois problemas embora de risco
muito elevado, dado que podem ser substitudos textos em locais imprprios. Os tipos das variveis
tm tendncia para serem curtos, dado que so normalmente conceitos simples e claros, pelo que o
risco de substituies indevidas aumenta.
O typedef serve precisamente para resolver este problema, sem o risco de trocas acidentais que a
utilizao de macros teria.
/* a partir deste momento, existe o tipo inteiro, que um int */
typedef int inteiro;

Notar que se existir a necessidade de em outro compilador/computador o inteiro ser do tipo long,
ou short, basta editar esta linha e todo o cdigo fica actualizado.
/* TData passa a ser um tipo struct SData */
typedef struct SData TData;

O efeito o pretendido. No entanto o identificador SData foi criado apenas para ser utilizado no
typedef. Tal no necessrio, pode-se simplesmente utilizar o typedef logo na declarao da
estrutura:
typedef struct
{
int ano, mes, dia;
} TData;

Vejamos o exemplo modificado com passagem de estruturas por referncia, operador ->, e
typedef:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#include <stdio.h>
#define MAXSTR 255
typedef struct
{
int ano, mes, dia;
} TData;
typedef struct
{
char *nome;
char sexo;
TData nascimento;
char *morada;
char *email;
long telefone;
} TPessoa;
void LerPessoa(TPessoa *pessoa)
{
char str[MAXSTR];

118
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

Parte III Memria, Estruturas e Ficheiros


printf("Nome: ");
gets(str);
pessoa->nome=(char*)malloc(strlen(str)+1);
strcpy(pessoa->nome,str);
printf("Sexo: ");
scanf("%c",&pessoa->sexo);
printf("Data de nascimento (dd-mm-aaaa): ");
scanf("%d-%d-%d",
&pessoa->nascimento.dia,
&pessoa->nascimento.mes,
&pessoa->nascimento.ano);
printf("Morada: ");
gets(str);
gets(str);
pessoa->morada=(char*)malloc(strlen(str)+1);
strcpy(pessoa->morada,str);
printf("Email: ");
gets(str);
pessoa->email=(char*)malloc(strlen(str)+1);
strcpy(pessoa->email,str);
printf("Telefone: ");
scanf("%ld",&pessoa->telefone);
}
void MostraPessoa(TPessoa *pessoa)
{
printf("\nNome: %s\nSexo: %c\nData de Nascimento: %d-%d-%d\n",
pessoa->nome, pessoa->sexo,
pessoa->nascimento.dia,
pessoa->nascimento.mes,
pessoa->nascimento.ano);
printf("Morada: %s\nEmail: %s\nTelefone: %d\n",
pessoa->morada,
pessoa->email,
pessoa->telefone);
}
void Libertar(TPessoa *pessoa)
{
free(pessoa->nome);
free(pessoa->morada);
free(pessoa->email);
}
int main()
{
TPessoa pessoa;
LerPessoa(&pessoa);
MostraPessoa(&pessoa);
Libertar(&pessoa);
}
Programa 11-4 Verso do registo de pessoas, mais legvel e eficiente

Esta verso mais legvel, e tambm mais eficiente.


O tipo TPessoa til apenas para a aplicao em desenvolvimento, e a existir
em outras aplicaes ser certamente diferente. Pelo contrrio, o tipo TData pode ser reutilizado
em vrias aplicaes. Vamos introduzir um tipo com um potencial de reutilizao superior
data: vector. Um vector normalmente est associado sua dimenso, a qual tem que ser
transportada num parmetro parte para dentro das funes, devendo ser utilizado esse valor para
percorrer todo o vector, ou testar se o ndice pedido est alocado ou no. Temos portanto um
potencial para definir um tipo com uma alta taxa de reutilizao.
typedef struct
{
int n;

11. Estruturas

119

int *valor;
} TVectorInt;

Vamos refazer o exemplo do vector aleatrio com base nesta estrutura:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int n;
int *valor;
} TVectorInt;
TVectorInt VectorAleatorio(int n, int base)
{
TVectorInt vector;
int i;
vector.valor=(void*)malloc(sizeof(int)*n);
if(vector.valor!=NULL)
{
vector.n=n;
for(i=0;i<n;i++)
vector.valor[i]=rand()%base;
} else
vector.n=0;
return vector;
}
void MostraVector(TVectorInt vector)
{
int i;
for(i=0;i<vector.n;i++)
printf("%d ",vector.valor[i]);
}
int main()
{
TVectorInt vector;
int n,base;
srand(1);
printf("Dimensao: ");
scanf("%d",&n);
printf("Valor maximo: ");
scanf("%d",&base);
vector=VectorAleatorio(n,base);
if(vector.valor!=NULL)
{
MostraVector(vector);
free(vector.valor);
}
}
Programa 11-5 Estrutura de um vector com os valores e a sua dimenso

Este exemplo foi melhorado no sentido em que se poupou a passagem para MostraVector de um
segundo argumento relacionado com o vector, a sua dimenso, que assim est mesmo associado ao
vector. Note-se que neste caso a estrutura passada por valor, uma vez que tem apenas dois
campos, nunca sendo o vector em si copiado.
Infelizmente as tarefas especficas desta estrutura continuam espalhadas pelo cdigo:

Alocao do vector
Acesso a uma posio do vector
Libertao do vector

120

Parte III Memria, Estruturas e Ficheiros

As estruturas ao permitirem o encapsulamento de dados so a base para a abstraco de dados,


tambm chamado de tipos abstractos de dados. Embora este conceito tenha ficado obsoleto com o
conceito de classe da programao orientada por objectos, tambm a sua base, pelo que a boa
utilizao de tipos abstractos de dados, ser o suporte para a compreenso de classes. Na
linguagem C esta a nica ferramenta de controle de complexidade dos dados.
Tal como a abstraco funcional permite que se utilize uma funo sem saber como est
implementada, e ao implementar a funo no necessitamos de saber como ser utilizada,
permitindo desta forma controlar a complexidade de um programa, e implementar um programa de
dimenso arbitrria, os tipos abstractos de dados permitem que se utilize dados sem saber como
esto realmente declarados, e que ao declarar um tipo abstracto de dados, no se esteja a preocupar
como o tipo ser utilizado.
Um tipo abstracto de dados consiste em definir no s uma estrutura, como tambm um conjunto
de funes para acesso estrutura, no devendo a estrutura ser acedida por qualquer outra funo
directamente. Quando for necessrio alterar a estrutura, o resto do programa ficar a funcionar,
no sendo necessrio rev-lo, e o mesmo se passa com o resto do programa, pode utilizar os
mtodos de acesso sem se preocupar como esto implementados.
TVectorInt VICriar(int);
int VITamanho(TVectorInt);
int VIValorO(TVectorInt,int);
void VIValorI(TVectorInt,int,int);
void VILibertar(TVectorInt*);

Estas funes so as necessrias para conter a complexidade de dados associada ao TVectorInt no


cdigo apresentado. Desde que se verifique a condio de que nenhuma outra funo acede
directamente estrutura de dados, e que estas funes de acesso no tm nada especfico da
aplicao, ser sempre um bom tipo abstracto de dados. Apenas desta forma este tipo abstracto de
dados poder ser reutilizado em outras aplicaes, e fornecer realmente uma abstraco de dados.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include <stdio.h>
#include <stdlib.h>
/* Tipo de dados abstracto: TVectorInt */
typedef struct
{
int n;
int *valor;
} TVectorInt;
TVectorInt VICriar(int n)
{
TVectorInt vector;
vector.valor=(void*)malloc(sizeof(int)*n);
if(vector.valor!=NULL)
vector.n=n;
else
vector.n=0;
return vector;
}
int VITamanho(TVectorInt vector)
{
return vector.n;
}
void VIValorI(TVectorInt vector, int i, int valor)
{
if(i>=0 && i<vector.n)

11. Estruturas
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

121

vector.valor[i]=valor;
}
int VIValorO(TVectorInt vector, int i)
{
if(i>=0 && i<vector.n)
return vector.valor[i];
return 0;
}
void VILibertar(TVectorInt *vector)
{
if(vector->valor!=NULL)
{
free(vector->valor);
vector->valor=NULL;
vector->n=0;
}
}
/* Programa */
TVectorInt VectorAleatorio(int n, int base)
{
TVectorInt vector;
int i;
vector=VICriar(n);
for(i=0;i<VITamanho(vector);i++)
VIValorI(vector,i,rand()%base);
return vector;
}
void MostraVector(TVectorInt vector)
{
int i;
for(i=0;i<VITamanho(vector);i++)
printf("%d ",VIValorO(vector,i));
}
int main()
{
TVectorInt vector;
int n,base;
srand(1);
printf("Dimensao: ");
scanf("%d",&n);
printf("Valor maximo: ");
scanf("%d",&base);
vector=VectorAleatorio(n,base);
MostraVector(vector);
VILibertar(&vector);
}
Programa 11-6 Tipo abstracto de dados TVectorInt

N a implementao das funes VIValorI e VIValorO de acesso ao vector foi colocado um teste de
validade, podendo assim num s local controlar se esse teste deve ou no existir. Esta opo no
seria possvel sem um tipo abstracto de dados. Na funo VILibertar foram tomadas medidas
para que o vector no seja libertado se no estiver alocado, ou se j foi libertado, sendo a nica
funo a passar a estrutura por referncia, uma vez que altera o valor dos seus campos. O programa
em si, ao ficar liberto das especificidades do tipo de dados abstracto, tambm simplificado.

122

Parte III Memria, Estruturas e Ficheiros

Este tipo pode no entanto ser melhorado, dado que para criar o vector tem de se saber o seu
tamanho, sendo os pedidos de acesso fora do espao inicialmente criado recusados. O
procedimento poderia ser distinto, por exemplo, no ser necessrio definir o tamanho do vector
inicialmente, e o vector ser realocado sempre que necessrio. Adicionava-se alguma complexidade
ao tipo abstracto de dados, mas o programa ficaria liberto da necessidade de especificar a dimenso
do vector.
Foi adicionado ao programa cdigo para medir o tempo utilizado na criao do vector, de forma a
podermos medir a sua performance.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Tipo de dados abstracto: TVectorInt */
typedef struct
{
int n;
int *valor;
} TVectorInt;
TVectorInt VICriar()
{
TVectorInt vector;
vector.valor=NULL;
vector.n=0;
return vector;
}
int VITamanho(TVectorInt *vector)
{
return vector->n;
}
void VIInternoRealocar(TVectorInt *vector, int i)
{
int k, *vectorAntigo;
vectorAntigo=vector->valor;
vector->valor=(int*)malloc(sizeof(int)*(i+1));
if(vector->valor!=NULL)
{
for(k=0;k<vector->n;k++)
vector->valor[k]=vectorAntigo[k];
vector->n=i+1;
} else
vector->n=0;
if(vectorAntigo!=NULL)
free(vectorAntigo);
}
void VIValorI(TVectorInt *vector, int i, int valor)
{
/* acesso fora dos parmetros, realocar */
if(i>=vector->n)
VIInternoRealocar(vector,i);
if(i>=0 && i<vector->n)
vector->valor[i]=valor;
}
int VIValorO(TVectorInt *vector, int i)
{
/* no colocar a realocao aqui, j que esta operao de
leitura e no de escrita */
if(i>=0 && i<vector->n)
return vector->valor[i];
return 0;

11. Estruturas
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

123

}
void VILibertar(TVectorInt *vector)
{
if(vector->valor!=NULL)
{
free(vector->valor);
vector->valor=NULL;
vector->n=0;
}
}
/* Programa */
TVectorInt VectorAleatorio(int n, int base)
{
TVectorInt vector;
int i;
vector=VICriar();
for(i=0;i<n;i++)
VIValorI(&vector,i,rand()%base);
return vector;
}
void MostraVector(TVectorInt *vector)
{
int i;
for(i=0;i<VITamanho(vector);i++)
printf("%d ",VIValorO(vector,i));
}
int main()
{
TVectorInt vector;
int n,base;
clock_t instante;
srand(1);
printf("Dimensao: ");
scanf("%d",&n);
printf("Valor maximo: ");
scanf("%d",&base);
instante=clock();
vector=VectorAleatorio(n,base);
if(n<100)
MostraVector(&vector);
VILibertar(&vector);
printf("Tempo (s): %.3f",(float)(clock()-instante)/CLOCKS_PER_SEC);
}
Programa 11-7 Verso de TVectorInt com apontadores, e controle do tempo

Todos os mtodos do tipo abstracto de dados foram passados para referncia, dado que
necessrio realocao em VIValorI, e para no estar metade dos mtodos com a estrutura por
valor e outra metade por referncia, colocou-se tudo em referncia.
Feito o tipo robusto, agora vamos optimiz-lo. Atendendo a que o tempo de realocao fora a que
todos os elementos sejam copiados para o novo vector, para adicionar elementos de 1 a 1, para um
vector de tamanho N, o nmero de cpias a realizar da ordem de N2. O primeiro elemento
copiado N vezes, o segundo elemento copiado N-1 vezes, etc.

124

Parte III Memria, Estruturas e Ficheiros

Vamos executar o cdigo com valores elevados, de forma a ter uma ideia do tempo utilizado pela via
experimental.
Execuo de exemplo:
C:\>vectoraleatorio5
Dimensao: 1000
Valor maximo: 100
Tempo (s): 0.016
C:\>vectoraleatorio5
Dimensao: 2000
Valor maximo: 100
Tempo (s): 0.032
C:\>vectoraleatorio5
vectoraleatorio5
Dimensao: 10000
Valor maximo: 100
Tempo (s): 0.390
C:\>vectoraleatorio5
Dimensao: 20000
Valor maximo: 100
Tempo (s): 1.343
C:\>vectoraleatorio5
Dimensao: 100000
Valor maximo: 100
Tempo (s): 31.375

clara a drstica mudana ao atingir valores mais elevados. Se com valores at 10000 os tempos
so desprezveis e aparentemente com crescimento linear, os valores acima de 20000 so bastante
significativos e crescimento quadrtico.
Uma forma de melhorar a performance, uma vez que se tivssemos alocado logo o vector com a
dimenso final, o nmero de cpias era apenas N, sempre que o vector no chega, alocar logo o
dobro do espao necessrio. Assim, o nmero de realocaes baixa consideravelmente, sendo num
vector de N elementos, cada elemento quanto muito atribudo uma vez e copiado outra vez, ou
seja, da ordem de N operaes. Para fazer isto, necessrio ter a dimenso alocada do vector, e a
dimenso lgica do vector. Como utilizamos tipos abstractos de dados, podemos fazer as alteraes
necessrias para que o programa fique com esta optimizao, alterando apenas funes do tipo
abstracto de dados.
Sendo este o primeiro teste da abstraco de dados, de total importncia que no seja necessrio
alterar o programa, caso contrrio confirmaramos que a abstraco de dados no estava
correctamente definida.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

/* Tipo de dados abstracto: TVectorInt */


typedef struct
{
int n,alocado;
int *valor;
} TVectorInt;
TVectorInt VICriar()
{
TVectorInt vector;
vector.valor=NULL;
vector.n=0;
vector.alocado=0;
return vector;
}
int VITamanho(TVectorInt *vector)

11. Estruturas

125

22 {
23
return vector->n;
24 }
25
26 void VIInternoRealocar(TVectorInt *vector, int i)
27 {
28
int k, *vectorAntigo;
29
vectorAntigo=vector->valor;
30
/* alocar o dobro do necessrio */
31
vector->valor=(int*)malloc(sizeof(int)*(i+1)*2);
32
if(vector->valor!=NULL)
33
{
34
for(k=0;k<vector->n;k++)
35
vector->valor[k]=vectorAntigo[k];
36
vector->alocado=(i+1)*2;
37
} else
38
vector->alocado=0;
39
if(vectorAntigo!=NULL)
40
free(vectorAntigo);
41 }
42
43 void VIValorI(TVectorInt *vector, int i, int valor)
44 {
45
/* acesso fora dos parmetros, realocar */
46
if(i>=vector->alocado)
47
VIInternoRealocar(vector,i);
48
49
if(i>=0 && i<vector->alocado)
50
{
51
vector->valor[i]=valor;
52
/* actualizar a dimenso lgica do vector */
53
if(i>=vector->n)
54
vector->n=i+1;
55
}
56 }
57
58 int VIValorO(TVectorInt *vector, int i)
59 {
60
/* no colocar a realocao aqui, j que esta operao de
61
leitura e no de escrita */
62
if(i>=0 && i<vector->n)
63
return vector->valor[i];
64
return 0;
65 }
66
67
68 void VILibertar(TVectorInt *vector)
69 {
70
if(vector->valor!=NULL)
71
{
72
free(vector->valor);
73
vector->valor=NULL;
74
vector->alocado=0;
75
vector->n=0;
76
}
77 }
Programa 11-8 Ajustes feitos no tipo abstracto de dados TVectorInt, para melhoria da eficincia

Foi adicionado um campo, e feitos os ajustes necessrios, apenas no tipo abstracto de dados. Vamos
agora confirmar pela via experimental a vantagem desta mudana.

126

Parte III Memria, Estruturas e Ficheiros

Execuo de exemplo:
C:\>vectoraleatorio6
Dimensao: 1000
Valor maximo: 100
Tempo (s): 0.000
C:\>vectoraleatorio6
Dimensao: 10000
Valor maximo: 100
Tempo (s): 0.016
C:\>vectoraleatorio6
Dimensao: 100000
Valor maximo: 100
Tempo (s): 0.015
C:\>vectoraleatorio6
Dimensao: 1000000
Valor maximo: 100
Tempo (s): 0.078

Nem o valor mais alto do teste anterior, 100000, tem um tempo que se considere digno de alguma
preciso. Foi necessrio aumentar o valor para 1000000 para ter quase uma dcima de segundo.
Face a estes resultados, at a utilizao da anterior implementao at 10000 valores deixa de ser
razovel, dado que a alternativa no gastar praticamente tempo nenhum. Esta soluo tem no
entanto o problema de desperdiar at 50% do espao alocado, o que pode no ser razovel em
algumas aplicaes.
Est introduzida a vantagem dos tipos abstractos de dados. No seria correcto no entanto terminar
esta seco, tendo j sido dada a alocao de memria, sem a introduo da estrutura de dados mais
famosa que utiliza estruturas: listas.
As listas pretendem guardar uma sequncia de elementos, tal como os vectores. No entanto, ao
contrrio destes, alocam um bloco de memria por cada elemento, no havendo necessidade de
realocaes, e por outro lado utilizam apenas pequenos blocos de memria, facilitando assim a
tarefa do gestor de memria. Cada elemento tem de ter no entanto um apontador para o prximo
elemento, pelo que esta estrutura tem de utilizar o tipo struct:
typedef struct SListaInt
{
int valor;
struct SListaInt *seguinte;
} TListaInt;

Repare-se que a definio da estrutura recursiva. No portanto possvel deixar de fazer um


paralelo entre listas (recursivo) e vectores (iterativo), da mesma forma que existe funes
recursivas (chamam-se a si prprias) e iterativas (processam os valores iterativamente).
Para identificar uma lista, basta ter um apontador para o primeiro elemento, sendo normalizado
no s que a lista vazia o apontador para NULL, como tambm o elemento seguinte ao ltimo
elemento o valor NULL, ou seja, h uma lista vazia aps o ltimo elemento.
Como utilizamos o tipo abstracto de dados, mesmo com esta mudana radical na estrutura definida,
podemos fazer a mudana envolvendo apenas o tipo abstracto de dados. Mas como as listas tm um
acesso natural distinto dos vectores, vamos definir um tipo de dados abstracto mais adequado para
a utilizao de listas.

11. Estruturas

127

TListaInt* LIAdicionar(TListaInt *lista, int valor);


TListaInt *LILibertar(TListaInt *lista)

O acesso feito nos prprios membros, tambm no programa, dado que o cdigo no programa
simplificado graas a esta estrutura como se ver.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Tipo de dados abstracto: TListaInt */
typedef struct SListaInt
{
int valor;
struct SListaInt *seguinte;
} TListaInt;
TListaInt* LIAdicionar(TListaInt *lista, int valor)
{
TListaInt *novo;
novo=(TListaInt*)malloc(sizeof(TListaInt));
if(novo!=NULL)
{
novo->valor=valor;
novo->seguinte=lista;
return novo;
}
return lista;
}
void LILibertar(TListaInt *lista)
{
TListaInt *aux;
while(lista!=NULL)
{
aux=lista->seguinte;
free(lista);
lista=aux;
}
}
/* Programa */
TListaInt *ListaAleatoria(int n, int base)
{
TListaInt *lista=NULL; /* nova lista */
int i;
for(i=0;i<n;i++)
lista=LIAdicionar(lista,rand()%base);
return lista;
}
void MostraLista(TListaInt *lista)
{
while(lista!=NULL)
{
printf("%d ", lista->valor);
lista=lista->seguinte;
}
}
int main()
{
TListaInt *lista;
int n,base;
clock_t instante;
srand(1);

128

Parte III Memria, Estruturas e Ficheiros

65
66
67
68
69
70
71
72
73
74
75
76
77 }

printf("Dimensao: ");
scanf("%d",&n);
printf("Valor maximo: ");
scanf("%d",&base);
instante=clock();
lista=ListaAleatoria(n,base);
if(n<100)
MostraLista(lista);
LILibertar(lista);
printf("Tempo (s): %.3f",(float)(clock()-instante)/CLOCKS_PER_SEC);
Programa 11-9 Tipo abstracto de dados TListaInt

Repare-se na simplicidade do ciclo while, em MostraLista. Apenas so passados


apontadores, e no mtodo LIAdicionar, como a lista alterada, tem de existir tambm uma
atribuio para o valor retornado, que representa o incio da lista, como se pode ver em
ListaAleatoria. Alternativamente poder-se-ia ter enviado o endereo da lista, mas este formato
considera-se mais simples e revelador que o topo da lista vai alterando.
Execuo de exemplo:
C:\>listaaleatoria
Dimensao: 10
Valor maximo: 10
4 2 8 8 4 9 0 4 7 1 Tempo (s): 0.000
C:\>listaaleatoria
Dimensao: 1000
Valor maximo: 100
Tempo (s): 0.000
C:\>listaaleatoria
listaaleatoria
Dimensao: 10000
Valor maximo: 100
Tempo (s): 0.000
C:\>listaaleatoria
Dimensao: 100000
Valor maximo: 100
Tempo (s): 0.094
C:\>listaaleatoria
Dimensao: 1000000
Valor maximo: 100
Tempo (s): 0.578

Os valores em termos de tempos so ligeiramente superiores melhor verso dos vectores, uma
vez que o nmero de alocaes de memria superior, mas o nmero de operaes idntico e no
desperdia memria excepto o espao de um apontador por cada elemento. As listas constituem
assim uma alternativa vlida aos vectores, resultando em cdigo mais simples em grande parte dos
casos.
O preo a pagar pela utilizao das listas, no mtodo de acesso aleatrio: LIValor(lista,i).
Para implementar um mtodo deste tipo, tem que se percorrer a lista desde o incio at obter o
valor na posio i, enquanto com vectores o acesso imediato.
A estrutura de dados de uma aplicao o conjunto de estruturas definidas que permitem
carregar os dados da aplicao em memria, e suportam as operaes necessrias. As opes
tomadas na definio da estrutura de dados so as mais determinantes para que a aplicao seja
facilmente implementada e mantida. No entanto, questes relacionadas com a estrutura de dados
no so aprofundadas neste texto, dado que estas questes so mais relevantes apenas para

11. Estruturas

129

aplicaes de mdia e grande dimenso. Aconselha-se apenas que antes de pensar no algoritmo que
lhe resolve o problema, pensar nos dados que tem, e como os coloca em memria, ou seja, definir a
estrutura de dados, e apenas depois nos algoritmos que necessita de implementar que utilizam a
estrutura de dados para produzir o resultado pretendido. normal que se sinta tentado a fazer o
inverso, dado que os problemas tratados no tm tratado com estruturas de dados complexas. Para
definir a estrutura de dados apenas necessita de conhecer o problema, para implementar um
algoritmo, necessita de ter tambm a estrutura de dados definida.

130

Parte III Memria, Estruturas e Ficheiros

12.

FICHEIROS

Um programa pode fazer processamentos fantsticos, mas no final termina e tudo o que aconteceu,
desaparece. Uma das grandes vantagens do computador, a possibilidade guardar ficheiros com
informao, ao longo do tempo, caso contrrio ficaria idntico a uma mquina de calcular. Os
ficheiros servem para guardar a informao de forma mais permanente, de modo a poder ser
utilizada mais tarde, pela mesma aplicao ou outra.
No entanto, ao executar um programa, todos os dados tm de ser introduzidos pelo teclado, e todos
os dados processados so reproduzidos no ecr. Seria interessante ter uma forma de indicar um
ficheiro de origem dos dados, e eventualmente um ficheiro de sada dos dados.
precisamente esse efeito que tm os operadores de linha de comando < e >:
C:\> prog < in.txt

O ficheiro in.txt ser colocado na entrada de dados de prog.


Vejamos o exemplo do nmero de dias do ms j conhecido.
Execuo de exemplo:
C:\>diasdomes
Indique ano: 2010
Indique mes: 2
28

No ficheiro in.txt coloca-se o texto introduzido:

Figura 12-1 Contedo do ficheiro in.txt

Execuo de exemplo:
C:\>diasdomes < in.txt
Indique ano: Indique mes: 28

Repare-se que o programa no ficou espera que se introduza o ano e o ms, estes valores foram
lidos do ficheiro in.txt, sendo o resultado final 28. Para o processo inverso, redireccionar o texto de
sada para um ficheiro, utiliza-se o operador >
C:\> prog > out.txt

Para o ficheiro out.txt ser enviado a sada de dados do programa.

12. Ficheiros

131

Execuo de exemplo:
C:\>diasdomes > out.txt
2010
2

A aplicao j no mostra o texto "Indique ano:" nem "Indique mes:" dado que todo o texto
que deveria de ir para o ecr est a ser colocado no ficheiro out.txt. No final o ficheiro out.txt fica
deste modo:

Figura 12-2 Contedo do ficheiro out.txt

Parte dos dados que so para especificar o que o utilizador deve introduzir, no fazem sentido neste
tipo de utilizaes, em que o utilizador sabe bem o que quer, ao ponto de colocar os dados de
entrada num ficheiro, portanto no est dependente do texto que a aplicao lhe mostra para
colocar os dados correctamente. Vamos mostrar um exemplo mais real, alterando o exemplo da
gerao dos nmeros aleatrios para no mostrar texto de identificao da informao esperada ao
utilizador, e mostrar todos os valores gerados independente do nmero de valores.
100
101
102
103
104
105
106
107
108
109
110
111
112
113

...
int main()
{
TVectorInt vector;
int n,base;
srand(1);
scanf("%d",&n);
scanf("%d",&base);
vector=VectorAleatorio(n,base);
MostraVector(&vector);
VILibertar(&vector);
}

Execuo de exemplo:
C:\>vectoraleatorio7

10
10

1 7 4 0 9 4 8 8 2 4
C:\>vectoraleatorio7 > out.txt

1000
100

Os 1000 valores aleatrios foram colocados no ficheiro out.txt. O ficheiro ficou com o seguinte
contedo:

132

Parte III Memria, Estruturas e Ficheiros

Figura 12-3 Novo contedo do ficheiro out.txt

Por vezes pretende-se juntar informao no ficheiro, e no criar um ficheiro novo. Para tal pode-se
utilizar o operador de linha de comando >>, que em vez de apagar o ficheiro antes de colocar a
informao do programa, adiciona a informao ao final do ficheiro:
Execuo de exemplo:
C:\>vectoraleatorio7 >> out.txt

10
10

Pode-se confirmar que os ltimos valores no ficheiro out.txt so menores que 10:

Figura 12-4 Ficheiro out.txt com algum texto adicionado pela ltima operao

Sobre redireccionamento dos dados de entrada e sada para ficheiros tudo. Pode no entanto ser
interessante enviar os dados de um programa, no para um ficheiro mas sim para outro programa.
Desta forma tem-se dados a passar directamente de um programa para outro. Isso possvel
atravs do operador de linha de comando |:
C:\> prog1 | prog2

12. Ficheiros

133

Os dados de sada de prog1, so os dados de entrada de prog2. Vamos a um exemplo concreto.


Suponhamos que pretendemos gerar valores aleatrios para testar o programa diasdomes, e
pretendemos utilizar o programa de gerao de vectores aleatrios. O seguinte comando satisfaz o
pretendido. A entrada de dados inicial est em in.txt, e a sada de dados final vai para out.txt:
C:\>vectoraleatorio7 < in.txt | diasdomes > out.txt

O contedo dos ficheiros o seguinte:

Figura 12-5 Contedo actual dos ficheiros in.txt e out.txt

Na entrada de dados foram pedidos dois valores aleatrios at 13, uma vez que o ms no pode ser
superior a 12. Poderia ter sido gerado um valor invlido para o ms (o valor 0), mas tal no
aconteceu, os valores gerados foram "2 7". Os valores gerados foram passados para o programa
diasdomes que finalmente colocou o resultado no ficheiro out.txt.
Pode-se encadear quantos programas forem necessrios, podendo inclusive estar desenvolvidos em
linguagens de programao distintas. Neste caso vectoraleatorio7 est a ser utilizado como um
gerador de casos de teste para diasdomes. Para colocar diasdomes sobre muitas situaes vlidas,
o ideal ser utilizar um programa especfico para gerar valores aleatrios na gama de valores que
diasdomes suporta. Assim podem ser efectuados muitos testes, que de outra forma, testados
manualmente, no s levaria muito tempo como provavelmente no seriam detectados tantos
problemas. Pode-se fazer tambm um programa para verificar o correcto output de diasdomes, e
dar um alerta no caso de detectar algum problema.
O redireccionamento da entrada e sada de dados, no resolve no entanto o problema de forma
completa. sem dvida uma ptima ferramenta, mas apenas se pode ter um ficheiro de entrada e
outro de sada. Pode haver necessidade de se querer utilizar o teclado, e mesmo assim obter o
contedo de um ficheiro, ou utilizar o ecr, e mesmo assim gravar num ficheiro, ou ainda abrir dois
ou mais ficheiros.
O ideal seria poder abrir os ficheiros que fossem necessrios, e aceder a eles como se estivesse a ler
do teclado e a imprimir para o ecr. precisamente para isso que servem as funes de ficheiros,
em modo de texto: fopen/fprintf/fscanf/fgets/feof
fopen/fprintf/fscanf/fgets/feof/fclose
/feof/fclose

134

Parte III Memria, Estruturas e Ficheiros

LER:
/*abertura em modo texto, para leitura, retorna NULL se no abrir */
FILE *f=fopen
fopen(ficheiro,"rt");
fopen
/* idntico a scanf, mas antecedendo um ficheiro aberto para leitura */
fscanf(f,...);
fscanf
/* idntico a gets, mas com indicao do tamanho mximo da string12 */
fgets(str,MAXSTR,f);
fgets
/* testa se foi atingido o final do ficheiro f */
feof(f);
feof
/* fecha o ficheiro aps j no ser necessrio */
fclose(f);
fclose

A funo fopen retorna um apontador para o tipo FILE, com a referncia a utilizar em todas as
funes sobre o ficheiro, sendo fechado por fclose. Pode-se fazer um paralelo entre
fopen/fclose e o malloc/free, enquanto estes ltimos alocam/libertam blocos de memria, os
primeiros abrem/fecham ficheiros.
Programa dos dias do ms reescrito para ler informao do ficheiro in.txt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include <stdio.h>
int main()
{
int ano, mes, dias;
FILE *f;
f=fopen("in.txt","rt");
if(f==NULL)
return;
fscanf(f,"%d", &ano);
fscanf(f,"%d", &mes);
fclose(f);
if(mes==2)
{
/* teste de ano bissexto */
if(ano%400==0 || ano%4==0 && ano%100!=0)
printf("29");
else
printf("28");
} else if(mes==1 || mes==3 || mes==5 || mes==7 ||
mes==8 || mes==10 || mes==12)
{
printf("31");
} else
{
printf("30");
}
}
Programa 12-1 Nmero de dias dado ms/ano, lendo dados a partir do ficheiro in.txt

Execuo de exemplo:
C:\>diasdomes2
28

12

A funo fgets retrona a string lida, a no ser que tenha existido um erro, e nesse caso retorna NULL

12. Ficheiros

135

GRAVAR:
/*abertura em modo texto, para leitura, retorna NULL se no abrir*/
FILE *f=fopen
fopen(ficheiro,"wt");
fopen
/* idntico a scanf, mas antecedendo um ficheiro aberto para leitura */
fprint(f,...);
fprint
fclose(f);
/* fecha o ficheiro aps j no ser necessrio*/
fclose

Pode-se gravar dados adicionando-os ao final do ficheiro, neste caso o modo de leitura seria "at".
Programa dos dias do ms reescrito para ler de in.txt e gravar para out.txt:
1 #include <stdio.h>
2
3 int main()
4 {
5
int ano, mes, dias;
6
FILE *f,*f2;
7
f=fopen("in.txt","rt");
8
f2=fopen("out.txt","wt");
9
if(f==NULL || f2==NULL)
10
return;
11
fscanf(f,"%d", &ano);
12
fscanf(f,"%d", &mes);
13
fclose(f);
14
15
if(mes==2)
16
{
17
/* teste de ano bissexto */
18
if(ano%400==0 || ano%4==0 && ano%100!=0)
19
fprintf(f2,"29");
20
else
21
fprintf(f2,"28");
22
} else if(mes==1 || mes==3 || mes==5 || mes==7 ||
23
mes==8 || mes==10 || mes==12)
24
{
25
fprintf(f2,"31");
26
} else
27
{
28
fprintf(f2,"30");
29
}
30
fclose(f2);
31 }
Programa 12-2 Nmero de dias do ms dado ms/ano lendo dados de in.txt e escrevendo para out.txt

Execuo de exemplo:
C:\>diasdomes3

Ficheiro out.txt:

Figura 12-6 Contedo do ficheiro out.txt aps execuo do Programa 12-2

Tudo o resto igual utilizao normal do printf/scanf/gets, to igual que na verdade mesmo
igual. Existe um ficheiro que est sempre aberto para a leitura: stdin. Existe outro ficheiro que est

136

Parte III Memria, Estruturas e Ficheiros

sempre aberto para escrita: stdout. Estes ficheiros no devem ser atribudos a nada, nem fechados
com fclose.
printf(...); igual a fprintf(stdout,...);
scanf(...); igual a fscanf(stdin,...);
gets(...); igual a fgets(...,MAXSTR,stdin);

Entre o gets e fgets h mais um argumento, para proteger a dimenso do vector alocada.
Estaria tudo dito relativamente aos ficheiros, no fosse agora haver outro tipo de utilizao: gravar
informao para depois ser lida e processada automaticamente. Nesta situao, no faz sentido os
textos de identificao do campo a introduzir, como passa a ser necessrio gravar e ler o ficheiro
num formato especfico, para que cada dado seja gravado/lido correctamente.
Vamos reutilizar o exemplo da ficha de uma pessoa para gravar numa lista todas as pessoas
introduzidas, de forma a ficarem disponveis na vez seguinte que se abrir a aplicao. O formato
simples: uma linha por cada campo pedido, da mesma forma que so introduzidos os dados.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

#include <stdio.h>
#define MAXSTR 255
/* Tipo de dados abstracto: TListaPessoa */
typedef struct
{
int ano, mes, dia;
} TData;
typedef struct SPessoa
{
char *nome;
char sexo;
TData nascimento;
char *morada;
char *email;
long telefone;
struct SPessoa *seguinte;
} TListaPessoa;
TListaPessoa* LPAdicionar(TListaPessoa *lista, TListaPessoa pessoa)
{
TListaPessoa *novo;
novo=(TListaPessoa*)malloc(sizeof(TListaPessoa));
if(novo!=NULL)
{
(*novo)=pessoa;
novo->seguinte=lista;
return novo;
}
return lista;
}
void LPLibertar(TListaPessoa *lista)
{
TListaPessoa *aux;
while(lista!=NULL)
{
aux=lista->seguinte;
free(lista->nome);
free(lista->morada);
free(lista->email);
free(lista);
lista=aux;
}

12. Ficheiros
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

}
/* Programa */
/* colocar fout=NULL para no imprimir texto dos campos a ler */
void LerPessoa(TListaPessoa *pessoa, FILE *fin, FILE *fout)
{
char str[MAXSTR];
if(fout!=NULL)
fprintf(fout,"Nome: ");
fgets(str,MAXSTR,fin);
pessoa->nome=(char*)malloc(strlen(str)+1);
strcpy(pessoa->nome,str);
if(fout!=NULL)
fprintf(fout,"Sexo: ");
fscanf(fin,"%c",&pessoa->sexo);
if(fout!=NULL)
fprintf(fout,"Data de nascimento (dd-mm-aaaa): ");
fscanf(fin,"%d-%d-%d",
&pessoa->nascimento.dia,
&pessoa->nascimento.mes,
&pessoa->nascimento.ano);
if(fout!=NULL)
fprintf(fout,"Morada: ");
fgets(str,MAXSTR,fin);
fgets(str,MAXSTR,fin);
pessoa->morada=(char*)malloc(strlen(str)+1);
strcpy(pessoa->morada,str);
if(fout!=NULL)
fprintf(fout,"Email: ");
fgets(str,MAXSTR,fin);
pessoa->email=(char*)malloc(strlen(str)+1);
strcpy(pessoa->email,str);
if(fout!=NULL)
fprintf(fout,"Telefone: ");
fscanf(fin,"%ld",&pessoa->telefone);
}
/* grava para fout, mas apenas mostra texto com o nome dos
campos se prompt=1 */
void MostraPessoa(TListaPessoa *pessoa, FILE *fout, int prompt)
{
if(prompt==0)
{
fprintf(fout,"%s%c\n%d-%d-%d\n%s%s%d",
pessoa->nome,
pessoa->sexo,
pessoa->nascimento.dia,
pessoa->nascimento.mes,
pessoa->nascimento.ano,
pessoa->morada,
pessoa->email,
pessoa->telefone);
} else {
fprintf(fout,"\nNome: %s\nSexo: %c\nData de Nascimento: %d-%d-%d\n",
pessoa->nome,
pessoa->sexo,
pessoa->nascimento.dia,
pessoa->nascimento.mes,
pessoa->nascimento.ano);
fprintf(fout,"Morada: %s\nEmail: %s\nTelefone: %d\n",
pessoa->morada,
pessoa->email,
pessoa->telefone);
}
}
int main()
{
FILE *f;
TListaPessoa *lista=NULL, *i, pessoa;

137

138

Parte III Memria, Estruturas e Ficheiros

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 }

/* ler as pessoas todas do ficheiro pessoas.txt */


f=fopen("pessoas.txt","rt");
if(f!=NULL)
{
while(!feof(f))
{
/* l uma pessoa do ficheiro */
LerPessoa(&pessoa, f, NULL);
lista=LPAdicionar(lista,pessoa);
}
fclose(f);
}
/* mostrar as pessoas lidas do ficheiro */
for(i=lista; i!=NULL; i=i->seguinte)
MostraPessoa(i,stdout,1);
/* l uma pessoa do teclado */
LerPessoa(&pessoa, stdin, stdout);
lista=LPAdicionar(lista,pessoa);
/* grava tudo para o ficheiro */
f=fopen("pessoas.txt","wt");
if(f!=NULL)
{
for(i=lista;i!=NULL;i=i->seguinte)
MostraPessoa(i,f,0);
fclose(f);
}
LPLibertar(lista);
Programa 12-3 Ficha de pessoa, com gravao e leitura do ficheiro

Realar na reutilizao dos mtodos para ler uma ficha e gravar, aos quais apenas se permitiu fazer
essa operao atravs de um ficheiro. Na funo main que se chama esses mtodos, quer com o
stdin/stdout, como com um ficheiro aberto para escrita e leitura. No modo de gravao para
ficheiro, muito importante que os campos sejam gravados exactamente da mesma forma que so
lidos, caso contrrio os diferentes valores podem no ir para os locais correctos.
Execuo de exemplo:
C:\>pessoa3
Nome: Heraclito de Efeso
Sexo: M
Data de nascimento (dd-mm-aaaa): 1-1--540
--540
Morada: Efeso
Email: heraclito@filosofia.com
Telefone: 111111111
C:\>pessoa3
Nome: Heraclito de Efeso
Sexo: M
Data de Nascimento: 1-1--540
Morada: Efeso
Email: heraclito@filosofia.com
Telefone: 111111111
Nome: Afonso Henriques
Sexo: M
Data de nascimento (dd-mm-aaaa): 2525-7-1109
Morada: Guimaraes
Email: afonso@conquistador.com
Telefone: 111111111
C:\>pessoa3
Nome: Heraclito de Efeso
Sexo: M
Data de Nascimento: 1-1--540
Morada: Efeso

12. Ficheiros

139

Email: heraclito@filosofia.com
Telefone: 111111111
Nome: Afonso Henriques
Sexo: M
Data de Nascimento: 25-7-1109
Morada: Guimaraes
Email: afonso@conquistador.com
Telefone: 111111111
Nome: Ludwig van Beethoven
Sexo: M
Data de nascimento (dd-mm-aaaa): 1616-1212-1770
Morada: Viena
Email: ludwig@musica.com
Telefone: 111111111

No final o ficheiro de texto pessoas.txt ficou com o seguinte contedo:

Figura 12-7 Ficheiro pessoas.txt com o contedo gravado/lido pelo Programa 12-3

Repare-se que o nome de uma pessoa est colado ao ltimo campo da pessoa anterior. Isto ocorreu
devido formatao dos dados de entrada/sada. O ficheiro pode ser editado, reflectindo-se essas
alteraes no programa, mas se o formato no for o correcto, os dados deixam de se ler
correctamente.
Os ficheiros gravados por estarem em modo de texto13 podem ser editados por outra aplicao, e
alterados facilmente. Utilizam no entanto muito espao, enquanto para guardar um nmero inteiro
so necessrios 4 bytes em memria, para o escrever em modo de texto sem contar com os
caracteres que antecedem o nmero, podem ser necessrios at 9 bytes.
O modo de texto assume que apenas caracteres imprimveis so utilizados, e aplica algumas
operaes (no Windows substitui um \n por um \r\n), existindo o conceito de linha de texto do
ficheiro. O modo binrio14 para programas ou ficheiros em outro formato no organizado por
linhas e texto, em que a existncia de um carcter com o cdigo de \n pura coincidncia, j que
no h linhas.
13 Os modos de texto mais utilizados: rt leitura; wt escrita num ficheiro novo; at escrita no final
do ficheiro
14 Os modos binrios mais utilizados: rb leitura; wb escrita num ficheiro novo; ab escrita no final
do ficheiro; r+b leitura e escrita em ficheiro existente; w+b leitura e escrita num ficheiro novo

140

Parte III Memria, Estruturas e Ficheiros

Como no existe linhas, o ficheiro gravado/lido com blocos de memria, sendo para isso que as
funes fread/fwrite15 existem (ler/gravar um bloco de memria). As funes fseek/ftell
permitem recolocar a posio do ficheiro e ler a posio actual no ficheiro, respectivamente. Com a
utilizao destas funes, em vez do fprintf/fscanf/fgets, pode-se facilmente gravar e ler
dados em modo binrio.
Para exemplificar, vamos acrescentar ao tipo abstracto de dados vector, a possibilidade de se
gravar/ler para um ficheiro em modo binrio. No exemplo do ficheiro aleatrio, assim que o
utilizador coloque um pedido, verifica se o ficheiro existe e nesse caso l os valores do ficheiro, caso
contrrio gera os valores e grava-os no ficheiro, para que da prxima vez no seja necessrio gerar
os valores.
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
15

...
void VILer(TVectorInt *vector, FILE *f)
{
int n;
vector->valor=NULL;
vector->alocado=0;
vector->n=0;
/* ler a dimenso do vector */
fread(&n,sizeof(int),1,f);
/* realocar para a dimenso exacta */
VIInternoRealocar(vector,n/2);
/* ler o vector inteiro */
if(vector->valor!=NULL)
{
fread(vector->valor,sizeof(int),n,f);
vector->n=n;
}
}
void VIGravar(TVectorInt *vector, FILE *f)
{
fwrite(&(vector->n),sizeof(int),1,f);
fwrite(vector->valor,sizeof(int),vector->n,f);
}
/* Programa */
...
int main()
{
TVectorInt vector;
FILE *f;
char str[MAXSTR];
int n,base;
srand(1);
scanf("%d",&n);
scanf("%d",&base);
/* verificar se existe um ficheiro com os dados gravados */
sprintf(str,"n%db%d.dat",n,base);
f=fopen(str,"rb");
if(f!=NULL)
{
VILer(&vector,f);
fclose(f);
} else {
vector=VectorAleatorio(n,base);
f=fopen(str,"wb");
if(f!=NULL)

Utilizao: fread/fwrite(tipo*, sizeof(tipo), nmero de blocos, f); ambas as funes


recebem um apontador a memria a ler/gravar no ficheiro, bem como o tamanho de cada bloco e nmero de
blocos.

12. Ficheiros
149
150
151
152
153
154
155
156 }
Programa

141

{
VIGravar(&vector,f);
fclose(f);
}
}
MostraVector(&vector);
VILibertar(&vector);
12-4 Actualizao do tipo abstracto TVectorInt para ler/gravar para ficheiro, em modo binrio

Execuo de exemplo:
C:\>vectoraleatorio8
vectoraleatorio8

10
10

1 7 4 0 9 4 8 8 2 4
C:\>vectoraleatorio8

10
100

41 67 34 0 69 24 78 58 62 64
C:\>vectoraleatorio8

10
100

41 67 34 0 69 24 78 58 62 64

A ltima execuo foi lida do ficheiro, e no gerada. Neste caso a operao de gerar um valor
aleatrio tem um custo computacional muito baixo, no vale a pena gravar para ficheiro. Se fosse
uma operao mais demorada, o programa tiraria vantagem de ter j no passado feito essa
computao, e agora seria respondido ao pedido de forma imediata. Se por algum motivo os
ficheiros fossem apagados, o programa voltaria a fazer os clculos. Este um tipo de estratgia
utilizado inclusive em algoritmos e com memria em vez de ficheiros, guardando resultado de
alguns clculos pesados em memria, para no caso de serem necessrios no futuro poupar-se o
tempo de os recalcular.
Notar que as funes VILer e VIGravar so muito curtas, uma vez que as funes fread e fwrite
esto adaptadas leitura de vectores de estruturas, sendo necessrio apenas alocar a memria
necessria, e chamar as funes. Os dois ficheiros que foram criados automaticamente na execuo
de exemplo, n10b10.dat e n10b100.dat ocupam 44 bytes cada, que precisamente o espao
necessrio para guardar em memria os 10 valores inteiros de 4 bytes, e um outro com o tamanho
do vector.
Se a informao for lida sequencialmente, as funes fseek16 e ftell17 no so necessrias, caso
contrrio pode-se utilizar estas funes para reposicionar o ponto de leitura ou escrita do ficheiro.
Este tipo de utilizao foi no entanto mais relevante no passado, em que havia pouca memria e os
ficheiros eram utilizados como se fossem blocos de memria, ficando o ficheiro sempre aberto, e o
algoritmo em vez de aceder a um valor em memria teria de aceder a um registo no ficheiro. um
processo muito mais lento que o acesso memria, mas necessrio em tempos em que a memria
principal era escassa, e actualmente pode ainda ser utilizado em casos muito particulares.

16

Utilizao para posicionamento no ficheiro: fseek(f,posicao,SEEK_SET); outras constantes podem ser


utilizadas, SEEK_CUR para a posio ser relativa posio corrente, e SEEK_END para a posio ser relativa ao
fim do ficheiro
17 Leitura da posio actual no ficheiro: posicao=fteel(f);

142

Parte III Memria, Estruturas e Ficheiros

No modo texto pode-se editar com editores externos, no modo binrio tal mais difcil, mas ocupa
menos espao e mais rpido de ler. Estas duas possibilidades so a base para a tomada de deciso,
que tem vindo cada vez mais a recair para o modo de texto em XML, formato este que no deixa de
ser texto, mas segue um conjunto de regras mnimas da organizao da informao em tags. A razo
desta opo simples: os dados muitas vezes duram mais que as aplicaes. No deve ser optado
pelo modo binrio como forma de segurana, dado que facilmente se faz um editor que leia o dados,
apenas encriptando possvel adicionar segurana.
Erros comuns mais directamente associados a este captulo:

Abrir um ficheiro e no chegar a fechar


o Forma: Por vezes abre-se o ficheiro, mas a operao de fechar no causa erros e
desnecessria dado que o ficheiro ser fechado quando o programa acabar
o Problema: O nmero de ficheiros abertos um recurso limitado pelo sistema
operativo. Se um ficheiro no mais preciso, deve ser imediatamente fechado de
forma a libertar recursos. Se esta prtica no for feita, pode acontecer que pedidos
de abertura de ficheiros por esta ou outra aplicao sejam recusados
o Resoluo: Verificar o ltimo local onde o ficheiro necessrio, e chamar a
correspondente funo fclose de forma a fechar o ficheiro
Abrir um ficheiro e no testar se foi bem aberto
o Forma: Quando se sabe que o ficheiro existe, no necessrio testar se a operao
de abertura do ficheiro foi bem sucedida, perda de tempo
o Problema: Pode acontecer mesmo quando h a certeza que o ficheiro existe, que a
operao de abertura de ficheiro seja mal sucedida, no caso de estarem demasiados
ficheiros abertos, ou por questes de permisses, ou ainda por estarem duas
aplicaes a tentar abrir o ficheiro em modo de escrita. As operaes seguintes, que
utilizam o apontador retornado para o ficheiro, ficam com um procedimento incerto
o Resoluo: Testar sempre se o ficheiro foi bem aberto, e apenas utilizar o
apontador para o ficheiro no caso de este ser diferente de NULL

Exemplo destes dois erros pode ser a simplificao do Programa 12-1 apagando as linhas 8 e 9, bem
como a linha 12.

13. Truques

13.

143

TRUQUES

Vamos acabar, no com o mais importante, mas sim com o que h de menos importante na
linguagem C, que so os operadores binrios (que manipulam bits), bem perto do assembly.
Falaremos tambm do operador ? e da estrutura union.
Antes dos operadores binrios, introduz-se o operador ?, que permite fazer um condicional dentro
de uma expresso:
(cond?exp1:exp2)

Este operador til em situaes que tanto cond, como exp1 e exp2 so pequenas, e levaria
criao de um condicional if quando uma pequena expresso servia. O exemplo clssico de boa
utilizao a seguinte:
x=(a>b?a:b); /* em x fica o mximo de a e b */
if(a>b)
x=a;
else
x=b;

A verso com o if claramente mais complexa. O operador ? pode no entanto ser mal utilizado
se cond, exp1 ou exp2 forem muito longos, ficando o cdigo de difcil leitura. O exemplo do clculo
dos dias do ms pode ser reduzido ao clculo de uma s expresso:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#include <stdio.h>
int main()
{
int ano, mes, dias;
printf("Indique ano: ");
scanf("%d", &ano);
printf("Indique mes: ");
scanf("%d", &mes);
dias=
(mes==2 ?
(ano%400==0 || ano%4==0 && ano%100!=0 ? 29 : 28) :
(mes==1 || mes==3 || mes==5 || mes==7 ||
mes==8 || mes==10 || mes==12 ? 31 : 30));
printf("%d",dias);
}
Programa 13-1 Verso condensada do nmero de dias de um dado ms/ano

A funcionalidade deste programa igual aos restantes. No entanto as expresses lgicas utilizadas
so muito grandes, pelo que esta verso perde legibilidade. Mesmo na expresso lgica, este
operador pode ser utilizado. Ainda no exemplo dos dias do ms, pode-se substituir as diversas
igualdades para teste de ms de 31 dias por um operador ?

144
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Parte III Memria, Estruturas e Ficheiros


#include <stdio.h>
int main()
{
int ano, mes, dias;
printf("Indique ano: ");
scanf("%d", &ano);
printf("Indique mes: ");
scanf("%d", &mes);
dias=
(mes==2 ?
(ano%400==0 || ano%4==0 && ano%100!=0 ? 29 : 28) :
((mes%2==1?mes<=7:mes>=8) ? 31 : 30));
printf("%d",dias);
}
Programa 13-2 Verso ainda mais condensada do nmero de dias de um dado ms/ano

Neste caso tem-se compactao da expresso, mas a legibilidade muito reduzida, e a utilizar temse de acompanhar o cdigo de um bom comentrio explicativo.
Este operador apenas est nesta seco, dado que quem est a aprender conveniente no ver este
tipo de controlo de fluxo, porque na verdade o controle de fluxo dentro de uma expresso, que
para o alto nvel da linguagem C, representa uma s instruo. No h controlo de fluxo sem vrias
instrues. Na verdade no h contradio, dado que a implementao de uma expresso de C em
assembly resulta em mltiplas instrues.
Uma particularidade da linguagem C, utilizado por vezes em conjuno com o operador ?, a
possibilidade de utilizar atribuies como expresses. Isto , como uma atribuio retorna o
valor atribudo, pode ser utilizado dentro de uma expresso. A situao mais natural para fazer uso
deste conhecimento a atribuio do mesmo valor a vrias variveis, por exemplo:
x=y=z=0;

Neste exemplo a expresso resultante de z=0 foi utilizada para a atribuio a y, que por sua vez foi
utilizada como expresso para a atribuio a x. No entanto pode ser utilizado em situaes mais
imaginativas, sem benefcio para a legibilidade do cdigo:
a=(b>(c=0)?b=-b:++c);

A expresso anterior abstracta, dado que para m utilizao do conhecimento no existem bons
exemplos. A nica varivel que tem de ter um valor antes da instruo acima a varivel b. Por
exemplo, se esta for 10, os valores das variveis ficam a=-10; b=-10; c=0;. Caso seja -10, os
valores ficam a=1; b=-10; c=1;. A sequncia de operaes a seguinte:
1. Avaliado b>(c=0)
a. Atribudo c=0
b. Retornado c
2. Se verdadeiro, avaliado b=-b
a. Atribudo b=-b
b. Retornado b
3. Se falso, avaliado ++c
a. Incrementado c
b. Retornado c
4. Atribudo a a o valor retornado em 2 ou 3

13. Truques

145

A mais-valia dos operadores binrios (que manipulam os bits) disponibilizar em linguagem de


alto nvel, operaes de custo muito baixo em assembly e que existem em todos os computadores
modernos, que podem ser teis, para alm dos habituais truques envolvendo operaes com
potncias de 2, para a implementao eficiente de algumas operaes normalmente envolvendo
tipos de dados abstractos. Todas as operaes envolvendo tipos de dados, so tipicamente muito
utilizadas, pelo que devem ser optimizadas o melhor possvel.
O operador >> (e correspondente >>=, bem como as verses inversas << e <<=) so operadores
de deslocao de bits dentro da varivel. Os bits encostados perdem-se.
Deslocao:
a = a >> b; ( a>>=b; )
a = a << b; ( a<<=b; )

Tal como num nmero em base decimal, esta operao significa deslocar os dgitos de um nmero
para a esquerda ou direita. Na base decimal, o resultado multiplicar ou dividir por 10, por cada
deslocao. Em base binria, o resultado multiplicar ou dividir por 2. Como esta operao mais
rpida que qualquer operao aritmtica, comum multiplicar/dividir por potncias de 2
utilizando deslocamento de bits.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#include <stdio.h>
#include <time.h>
#define CICLOS 100000000
int main()
{
int valor=12345,i;
clock_t instante;
instante=clock();
for(i=0;i<CICLOS;i++);
printf("\nTempo de %d ciclos vazios: %.3f",
CICLOS, (float)(clock()-instante)/CLOCKS_PER_SEC);
instante=clock();
for(i=0;i<CICLOS;i++)
{
valor*=2;
valor/=2;
}
printf("\nTempo de %d multiplicacoes e divisoes por 2: %.3f",
CICLOS, (float)(clock()-instante)/CLOCKS_PER_SEC);
instante=clock();
for(i=0;i<CICLOS;i++)
{
valor<<=1;
valor>>=1;
}
printf("\nTempo de %d operacoes de deslocacao: %.3f",
CICLOS, (float)(clock()-instante)/CLOCKS_PER_SEC);
}
Programa 13-3 Instruo de shift vs multiplicao/diviso por 2

Execuo de exemplo:
C:\>deslocacao
Tempo de 100000000 ciclos vazios: 0.454
Tempo de 100000000 multiplicacoes e divisoes por 2: 2.093
Tempo de 100000000 operacoes de deslocacao: 0.688

146

Parte III Memria, Estruturas e Ficheiros

Como se pode observar, a vantagem tentadora. O ciclo vazio feito inicialmente, uma vez que
estas operaes tm um custo muito baixo, e o custo de operao do ciclo relevante. Pode-se
estimar o tempo gasto realmente nas operaes como sendo 0,234 e 1,639 segundos, pelo que a
operao de deslocao de um bit cerca de 7 vezes mais rpida que a operao de
multiplicao/diviso por 2.
As operaes binrias lgicas implementam cada funo lgica mas bit a bit. Estas operaes tm
tal como as operaes de deslocamento, um custo muito baixo de execuo.
Operaes lgicas binrias:
a=a|b; (a|=b;)
a=a&b; (a&=b;)
a=a^b; (a^=b;)
~a complemento

or
and
xor
(negao binria)

Um nmero em base decimal, o dgito menos significativo tambm o resto da diviso do nmero
por 10, e da mesma forma, os dgitos menos significativos em binrio, so o resto da diviso por a
correspondente potncia de 2. Com os operadores binrios, o resto da diviso por 2 obtm-se
simplesmente: a&1. Notar que apenas o dgito mais baixo poder ficar no nulo. O resto da diviso
com 4 seria: a&3. Esta operao naturalmente mais rpida que a operao normal a%2, e a%4.
Vamos mostrar agora os bits num nmero real, de forma a exemplificar alguma manipulao de
bits. Vamos ler um real para um inteiro, e mostrar no ecr todos os seus bits.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include <stdio.h>
#include <time.h>
int main()
{
unsigned int valor, bitAlto;
int i;
printf("Valor real:");
scanf("%g",&valor);
bitAlto=1<<31;
for(i=0;i<32;i++)
{
printf("%c",(valor&bitAlto?'1':'0'));
valor<<=1;
}
}
Programa 13-4 Representao binria de um nmero real

Execuo de exemplo:
C:\>bits
Valor real:1
00111111100000000000000000000000
C:\>bits
Valor real:-1
10111111100000000000000000000000
C:\>bits
Valor real:2
01000000000000000000000000000000
C:\>bits
Valor real:1.5
00111111110000000000000000000000

13. Truques

147

C:\>bits
Valor real:0.1
00111101110011001100110011001101
C:>bits
Valor real:0.125
00111110000000000000000000000000
C:\>bits
Valor real:1.5
00111111110000000000000000000000

No exemplo colocou-se numa varivel o bit mais alto, de forma a detectar o seu valor, e foi-se
fazendo operaes de deslocao at estarem todos os bits processados. Pode-se assim ver como
composto o nmero do tipo real, com o primeiro bit sendo o sinal, seguido de 8 a comporem o

1, MANTISSA2 2
expoente, e os restantes a mantissa, seguindo a frmula: ( 1)
.
No entanto o tipo real tem mais particularidades, nomeadamente situaes especiais de infinito, e
nmero no vlido, pelo que no se aconselha fazer a manipulao binria de reais, este exemplo
apenas pretende ilustrar optimizaes possveis.
SINAL

EXPOENTE255

Tivemos que fazer um artifcio para ver o real como inteiro, foi indicar ao scanf um apontador para
inteiro sem sinal, quando o scanf esperava um real. A linguagem C tem uma estrutura union que
permite uma mesma varivel comportar-se como sendo de diferentes tipos, evitando assim o
artifcio:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

#include <stdio.h>
#include <time.h>
int main()
{
unsigned int bitAlto;
int i;
union {
unsigned int inteiro;
float real;
} valor;
printf("Valor real:");
scanf("%g",&valor.real);
bitAlto=1<<31;
for(i=0;i<32;i++)
{
printf("%c",(valor.inteiro&bitAlto?'1':'0'));
valor.inteiro<<=1;
}
}
Programa 13-5 Verso do Programa 13-4 utilizando a estrutura union

A estrutura union idntica a struct, mas a sua utilidade muito inferior. Apenas em raras
excepes poder esta estrutura ser til.
O conhecimento da representao binria dos nmeros reais, poderia ser til para pequenas
operaes, como saber se o nmero negativo bastar comparar o bit mais alto, ou para
multiplicar/dividir por 2, bastar somar/subtrair a zona do expoente, o que corresponde a fazer
uma soma/subtraco inteira por o nmero 1 devidamente deslocado. Vamos medir o ganho
potencial desta ltima operao:

148

Parte III Memria, Estruturas e Ficheiros

1 #include <stdio.h>
2 #include <time.h>
3
4 #define CICLOS 100000000
5
6 int main()
7 {
8
union {
9
unsigned int inteiro;
10
float real;
11
} valor;
12
int i,incremento;
13
clock_t instante;
14
15
valor.real=1.0;
16
incremento=1<<23;
17
18
printf("\nReal: %f",valor.real);
19
valor.inteiro+=incremento;
20
printf("\nReal *2: %f",valor.real);
21
valor.inteiro-=incremento;
22
printf("\nReal /2: %f",valor.real);
23
24
instante=clock();
25
for(i=0;i<CICLOS;i++);
26
printf("\nTempo de %d ciclos vazios: %.3f",
27
CICLOS, (float)(clock()-instante)/CLOCKS_PER_SEC);
28
29
instante=clock();
30
for(i=0;i<CICLOS;i++)
31
{
32
valor.real*=2;
33
valor.real/=2;
34
}
35
printf("\nTempo de %d operacoes *=2 e /=2 float: %.3f",
36
CICLOS, (float)(clock()-instante)/CLOCKS_PER_SEC);
37
instante=clock();
38
for(i=0;i<CICLOS;i++)
39
{
40
valor.inteiro+=incremento;
41
valor.inteiro-=incremento;
42
}
43
printf("\nTempo de %d operacoes += e -= int: %.3f",
44
CICLOS, (float)(clock()-instante)/CLOCKS_PER_SEC);
45 }
Programa 13-6 Utilizao do formato do nmero real para aumentar a eficincia de algumas operaes

Execuo de exemplo:
C:\>mult
Real: 1.000000
Real *2: 2.000000
Real /2: 1.000000
Tempo de 100000000 ciclos vazios: 0.453
Tempo de 100000000 operacoes *=2 e /=2 float: 1.266
Tempo de 100000000 operacoes += e -= int: 0.718

H aqui um ganho potencial de 3 vezes os produtos efectuados na verso inteira.


Este tipo de utilizao poder no entanto dar mais trabalho que recompensa, na medida que as
condies optimizveis que fogem ao procedimento normal no so muito frequentes, e podem ser
executadas um nmero diminuto de nmero de vezes no algoritmo no compensando nem o
trabalho nem a perda de legibilidade que essa utilizao implica.

13. Truques

149

Uma boa utilizao dos operadores binrios para colocar numa s varivel inteira, vrias
variveis booleanas. Exemplifica-se com caractersticas de produtos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

#include <stdio.h>
#define
#define
#define
#define

NACIONAL 1
BARATO 2
QUALIDADE 4
LEVE 8

void MostrarProduto(int produto)


{
if(produto&NACIONAL)
printf("Nacional; ");
if(produto&BARATO)
printf("Barato; ");
if(produto&QUALIDADE)
printf("Qualidade; ");
if(produto&LEVE)
printf("Leve; ");
}
main()
{
int produto[4],i;
/* 0: produto barato e de qualidade */
produto[0]=BARATO|QUALIDADE;
/* 1: produto nacional e leve */
produto[1]=NACIONAL|LEVE;
/* 2: igual ao produto 0, mas no barato */
produto[2]=produto[0]&(~BARATO);
/* 3: todas as qualidades do 1 e 2,
excepto o ser nacional que inverso */
produto[3]=(produto[1]|produto[2])^NACIONAL;
/* mostrar as caractersticas dos produtos */
for(i=0;i<4;i++)
{
printf("\nProduto %d: ");
MostrarProduto(produto[i]);
}
}
Programa 13-7 Exemplo de utilizao de vrias variveis booleanas numa s varivei inteira

Execuo de exemplo:
C:\>flags
Produto
Produto
Produto
Produto

0:
1:
2:
3:

Barato; Qualidade;
Nacional; Leve;
Qualidade;
Qualidade; Leve;

No exemplo colocou-se macros para cada varivel booleana, e em cada uma est uma potncia de 2
distinta. Ao atribuir um valor basta fazer uma disjuno com a macro, como feito no exemplo nos
produtos 0 e 1. Para remover um valor, basta fazer uma conjuno com o complementar da
varivel, como feito no produto 2. No produto 3 troca-se um valor utilizando o XOR binrio. Na
funo MostrarProduto pode-se ver como se l facilmente os valores, fazendo a conjuno com a
macro respectiva.

150

Parte III Memria, Estruturas e Ficheiros

Erros comuns mais directamente associados a este captulo:

Comentrios inexistentes em partes complexas


o Forma: O cdigo deve ser lido por quem perceba no s da linguagem de
programao C, como de algoritmos, e que seja suficientemente inteligente para o
compreender
o Problema: Quem l cdigo C deve naturalmente possuir todas essas caractersticas,
mas tal no motivo para no explicar as partes complexas. Os comentrios podem
afectar a legibilidade do cdigo, e quem est a ler cdigo em que tem dificuldade em
compreender, no ir apreciar a forma rebuscada e hbil de quem o escreveu, mas
sim a simplicidade e clareza com que o fez, ou o explica. Se no conseguir
compreender algo, ser certamente por culpa de quem escreveu que no sabia o que
fazia e l acabou por obter uma soluo funcional em modo de tentativa e erro, de
baixa qualidade
o Resoluo: Nas zonas menos claras, comentar explicando todos os truques /
habilidades aplicadas. Se ficar algum por explicar, ser confundido certamente com
cdigo que nem quem o fez sabe realmente porque funciona, e mais vale refazer
novamente se houver algum problema

Exemplo de ocorrncias deste erro encontrar certamente ao rever o seu cdigo antigo. um bom
exerccio rever o seu prprio cdigo, para poder com alguma distncia da altura em que o escreveu,
reflectir sobre o que necessita realmente de ter em comentrio no cdigo para o ajudar a
compreend-lo quando j no se lembrar dele.

3) Exerccios

151

3) Exerccios
adiciona.c
Faa uma estrutura para uma lista de inteiros de ligao simples (apontador para o elemento
seguinte da lista). Implemente as funes para adicionar um elemento lista, remover o elemento
no topo da lista, e para calcular o nmero de elementos na lista.
Notas:

No se esquea de remover todos os elementos na lista, antes de sair do programa. Tudo o


que alocar deve libertar.

Execuo de exemplo:
C:\>adiciona
Numero de elementos: 1000
Elementos: xxx 116 80 686 693 309 598 650 629 676

Pergunta: adicione 1000 nmeros aleatrios entre 0 e 999 a uma lista, e calcule o nmero de
elementos na lista, bem como o valor do primeiro elemento na lista. Indique na resposta o primeiro
elemento na lista.

more.c
Faa um programa que receba um ficheiro de texto em argumento, e o mostre no ecr,
utilizando os primeiros 4 caracteres para numerar a linha. Se uma linha tiver mais de 75 caracteres,
a mesma mostrada em vrias linhas, mas o nmero de linha apresentado sempre a do ficheiro.
Considere que o comprimento mximo de uma linha de 1024 caracteres.
Notas:

Abra um ficheiro em modo de texto, e no se preocupe agora com a alocao de memria


Verifique se o ficheiro foi bem aberto, e se foi dado um argumento com o nome do ficheiro

Execuo de exemplo:
C:\>more more.c
1:#include <stdio.h>
2:
3:#define MAXSTR 1024
4:
5:int main(int argc, char **argv)
6:{
7:
FILE *f;
8:
char str[MAXSTR], *pt, c;
9:
int count = 0;
10:

152

Parte III Memria, Estruturas e Ficheiros


11:
12:
13:
14:
15:
16:
...

/* verificar se foi dado um argumento */


if(argc <= 1)
{
printf("Utilizao: more <ficheiro>\n");
return;
}

Pergunta: copie o texto desde "more.c" at s notas acabando em "nome do ficheiro", para um
ficheiro de texto (abra no Notepad), e grave. Assegure-se que o ficheiro de texto no Notepad tem
apenas 5 linhas (no utilizar o Word wrap), cada linha no comea com espaos (se tiver apagueos), no existindo bullets. Abra de seguida o ficheiro com a aplicao more, e indique na resposta
separado por espaos: nmero de linhas (do ficheiro); nmero de linhas (do ficheiro) cortadas;
palavras cortadas na mudana de linha por ordem (e separadas por um espao, no incluindo
vrgulas ou pontos se existirem).

insere.c
Sobre o cdigo desenvolvido no exerccio adiciona.c, faa uma funo para inserir um
elemento numa lista de inteiros, aps um elemento menor e antes de um elemento maior ou igual
(insere de forma ordenada). Faa ainda uma outra funo para apagar os elementos de uma lista de
inteiros, que sejam iguais a um determinado valor.
Notas:

Pode chamar as operaes atmicas desenvolvidas de adio, remoo e nmero de


elementos, para implementar a insero ordenada e remoo dos elementos com um
determinado valor

Execuo de exemplo:
C:\>insere
Elementos apos inserir 1000 elementos: 1 2 3 3 3 6 7 7 8 8
Numero de elementos na lista apos apagar: xxx
Elementos apos apagar: 1 3 3 3 7 7 9 11 11 15

Pergunta: insira ordenadamente na lista 1000 elementos aleatrios de valores entre 0 e 999,
chamando srand(1), e apague todos os nmeros na lista que sejam pares. Indique como resposta o
nmero de elementos da lista.

3) Exerccios

153

insertsort.c
Utilizando as funes desenvolvidas dos exerccios anteriores, nomeadamente a insero de
um elemento por ordem, faa uma funo para ordenar uma lista atravs do algoritmo InsertSort:
inserir cada elemento ordenadamente numa lista nova.
Notas:

Convm ter em ateno para no deixar blocos alocados no libertados. Veja com ateno se
todos os blocos alocados no seu cdigo so libertados no final.

Execuo de exemplo:
C:\>insertsort
Lista nao ordenada: 249 116 80 686 693 309 598 650 629 676
Lista: 249
Lista: 116 249
Lista: 80 116 249
Lista: 80 116 249 686
Lista: 80 116 249 686 693
Lista: 80 116 249 309 686 693
Lista: 80 116 249 309 598 686 693
Lista: 80 116 249 309 598 650 686 693
Lista: 80 116 249 309 598 629 650 686 693
Lista: 80 116 249 309 598 629 650 676 686 693
Lista ordenada: x x x x x x 7 7 8 8

Pergunta: construa uma lista com elementos aleatrios entre 0 e 999 (chame srand(1)), e ordene
no final a lista. Indique na resposta os trs primeiros elementos da lista, separados por espaos.

enesimo.c
Sobre o cdigo desenvolvido no exerccio insertsort.c, faa uma funo para retornar o
ensimo elemento numa lista de inteiros.
Notas:

Ao reutilizar todo o exerccio insertsort.c, apenas tem de implementar a nova funo.

Execuo de exemplo:
C:\>enesimo
elementos 250, 500 e 750: xxx xxx xxx

Pergunta: utilize o vector do exerccio insertsort.c, e indique como resposta os elementos nas
posies 250, 500 e 750 separados por espaos.

154

Parte III Memria, Estruturas e Ficheiros

palavras.c
Faa um programa que recebe o nome de um ficheiro, e retorna o nmero de palavras
distintas do ficheiro, que tenham apenas letras separadas por espaos, tabs e fins-de-linha.
Notas:

Pode utilizar a funo strtok

Execuo de exemplo:
C:\>palavras palavras.c
[1] #define MAXSTR 1024
[10] /* funo que dada uma string retorna o nmero de palavras */
[1] int Palavras(char *str)
[1] char strBackup[MAXSTR];
...

Pergunta: faa um ficheiro com o enunciado (desde "palavras.c:" at "fins-de-linha."), e indique


quantas palavras so retornadas.

cifracesar.c
Faa um programa que recebe um ficheiro de texto, e uma chave K, e mostra o ficheiro
codificado com a cifra de Jlio Csar. Esta cifra substitui cada carcter por K caracteres frente.
Aplique a cifra apenas s letras e dgitos, dando a volta tanto nas letras como nos dgitos, isto , 0
com um K de 11, fica 1 (10 dgitos mais 1). As letras maisculas e minsculas no se misturam, do
a volta separadamente.
Notas:

Identifique a frmula que permite obter o dgito certo, dando a volta.

Execuo de exemplo:
C:\>cifracesar cifracesar.c 13
#vapyhqr <fgqvb.u>
#vapyhqr <pglcr.u>
#vapyhqr <fgevat.u>
#qrsvar ZNKFGE 588
ibvq PvsenQrPrfne(pune *fge, vag punir)
{
vag v, punirq;
...
Pergunta: coloque num ficheiro o texto "lroajlnbja 32101" codificada pela cifra de Csar com o
cdigo "21090". Indique na resposta o seu contedo original (utilizar o valor simtrico para

descodificar).

3) Exerccios

155

more.c
Faa agora o exerccio more, mas para modo binrio. Coloque 16 caracteres por linha, na zona
da direita coloque os caracteres imprimveis, na zona da esquerda coloque o nmero do byte do
primeiro carcter na linha, e na zona central 16*3=48 caracteres com o valor hexadecimal de cada
carcter.
Notas:

isprint uma funo da biblioteca ctype.h, que pode ser utilizada para saber se um

carcter imprimvel
Para imprimir a verso hexadecimal de um nmero, a string de formatao do printf %x

Execuo de exemplo:
C:\>more more.c
0|23
16|68
32|79
48|65
64|0a
80|67
96|0d
112|3b
128|0d
...

69
3e
70
20
69
63
0a
0d
0a

6e
0d
65
4d
6e
2c
7b
0a
20

63
0a
2e
41
74
20
0d
20
20

6c
23
68
58
20
63
0a
20
20

75
69
3e
53
6d
68
20
20
20

64
6e
0d
54
61
61
20
20
69

65
63
0a
52
69
72
20
63
6e

20
6c
0d
20
6e
20
20
68
74

3c
75
0a
31
28
2a
46
61
20

73
64
23
30
69
2a
49
72
69

74
65
64
32
6e
61
4c
20
2c

64
20
65
34
74
72
45
2a
20

69
3c
66
0d
20
67
20
70
6a

6f
63
69
0a
61
76
2a
74
2c

2e
74
6e
0d
72
29
66
3b
20

|#include <stdio.
|h> #include <ct
|ype.h>
#defin
|e MAXSTR 1024
| int main(int ar
|gc, char **argv)
| {
FILE *f
|;
char *pt;
|
int i, j,

Pergunta: faa um ficheiro com o enunciado do problema (desde "more.c" at "carcter."), e veja o
ficheiro com o utilitrio desenvolvido (certifique-se que o ficheiro tem 2 linhas, e cada linha no
comea com espaos). Indique na resposta, separado por um espao, a posio no ficheiro das
palavras: "more.c"; "Coloque"; "16*3=48"

basededados.c
Faa um programa que guarde num ficheiro os registos de contactos, com nome, email,
telefone, cidade e descrio. O programa permite manter os registos entre execues, utilizando um
ficheiro binrio, e permite listar registos, adicionar/ver/editar qualquer registo.
Notas:

Assuma que as strings tm um valor mximo de 255 caracteres

156

Parte III Memria, Estruturas e Ficheiros

Execuo de exemplo:
C:\>basededados
#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo: 2
Nome: Jos coelho
Telefone: 123 456 789
Cidade: Lisboa
Descrio: ...

#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo: 1

#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo: 1

########
# Lista:
########
# 0 | Jos Coelho
########

########
# Lista:
########
# 0 | Jos Coelho
# 1 | Afonso Henriques
########

#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo: 1

#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo:

########
# Lista:
########
# 0 | Jos coelho
########

C:\>basededados

#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo: 4
ID: 0
Nome: Jos Coelho
Telefone: 123 456 789
Cidade: Lisboa
Descrio: ...

#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo: 2
Nome: Afonso Henriques
Telefone: 123 456 789
Cidade: Guimares
Descrio: ...

#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo: 3
ID: 0
#############
# Nome | Jos Coelho
# Telefone | 123 456 789
# Cidade | Lisboa
# Descrio | ...
#############
#########################
# Menu: #
#########################
# 1 | LISTAR registos #
# 2 | ADICIONAR registo #
# 3 | VER registo #
# 4 | EDITAR registo #
#########################
Opo:
C:\>

Pergunta: qual foi o modo de abertura do ficheiro da base de dados, que utilizou para gravar um
registo aps ser editado?

mergesort.c
Faa uma funo para ordenar uma lista de inteiros, com o algoritmo do MergeSort: ordenar
cada metade da lista (no obrigatoriamente a primeira metade, apenas metade dos elementos),
recursivamente, e no final juntar ambas as metades ordenadas, numa s lista ordenada.
Notas:

Basta reutilizar as funes de adio e remoo de elementos numa lista, dos exerccios
anteriores sobre listas de inteiros.
Ateno ao enunciado, esta funo MergeSort em listas mais simples que a
correspondente verso em vectores

3) Exerccios

157

Execuo de exemplo:
C:\>mergesort
Elemento na posio 1000: x
Elemento na posio 10000: xx

Pergunta: Adicione 100.000 de elementos, gerados aleatoriamente e com valores entre 0 e 999
(chame srand(1);), e ordene atravs desta funo. Indique como resposta o elemento na posio
1000, seguido do elemento na posio 10.000.

sort.c
Adapte a lista de inteiros para uma lista de strings, e carregue um ficheiro de texto que recebe
como argumento do programa. Deve carregar cada linha do ficheiro em cada elemento da lista de
strings. Adapte agora o MergeSort de listas de inteiros para listas de strings, e imprima o ficheiro
por ordem alfabtica das linhas.
Notas:

Utilize strcmp que compara duas strings, devolvendo 0 se as strings so iguais, e valor
positivo se uma maior que a outra na ordem alfabtica, e negativo no caso contrrio.

Execuo de exemplo:
C:\>sort sort.c
lista = Adiciona(lista, lista1->str);
lista = Adiciona(lista, lista2->str);
lista = Adiciona(lista, str);
lista = Remove(lista);
lista1 = Remove(lista1);
lista2 = Adiciona(lista2, lista->str);
lista2 = Remove(lista2);
/* alocar espao para a string, e copiar a string */
...

Pergunta: execute a aplicao dando como entrada o ficheiro de texto que tem mais mo: cdigo
fonte C. Qual o primeiro carcter na ltima linha de praticamente todos os ficheiros com cdigo
fonte C?

find.c
Faa um programa que recebe um ficheiro e uma string de procura, com wilcards (* significa
zero ou mais caracteres, e ? significa um carcter qualquer), e retorne todas as linhas de match
positivas. Considere que um match ocorre apenas dentro de uma s linha.
Notas:

Faa a funo Find, que recebe uma string, e um texto de procura, e retorna a primeira
ocorrncia desse texto na string. A funo deve aceitar wilcards e poder ser chamada
recursivamente. Se no conseguir os wilcards, faa primeiro sem wilcards, e depois adicione
o ?, e finalmente o *
Pode utilizar como ponto de partida o exerccio 3) more.c

158

Parte III Memria, Estruturas e Ficheiros

Execuo de exemplo:
C:\>find find.c if(*==*)
16:
if(find[ifind] == 0)
19:
if(find[ifind] == '?')
24:
else if(find[ifind] == '*')
33:
else if(find[ifind] == str[istr])
64:
if(f == NULL)
86:
if(pt == str)

Pergunta: copie a resoluo proposta do exerccio 3) more.c (primeiro exerccio) para um ficheiro
de texto, e execute as seguintes procuras sobre esse ficheiro por ordem: "!"; "<*>";
"(*,*,*)"; "[?]". Indique na resposta os nmeros das linhas retornadas, por ordem e separados
por espaos.

histograma.c
Faa um programa que recebe um ficheiro, e faa um histograma dos caracteres alfanumricos
no ficheiro. As maisculas e minsculas devem ser considerados caracteres distintos.
Notas:

Pode reutilizar a funo desenvolvida no exerccio 2) doisdados.c

Execuo de exemplo:
C:\>histograma histograma.c
130|
#
121|
#
113|
#
104|
#
#
95|
#
#
87|
#
#
#
78|
#
#
#
69|
#
#
#
61|
#
#
# #
52|
# # # #
## ###
43|
# # ## #
## ###
35|
# # ## #
## ###
26|
# # ## ##
## ###
17|#
# #######
### ####
9|### #
#
#
##
# ######## ##### ####
0|##############################################################
++----+----+----+----+----+----+----+----+----+----+----+----+0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Numero de cardinais no histograma: 164

Pergunta: aproveitando o ficheiro 3) more.c, utilizado no exerccio anterior, faa o seu histograma
e indique na resposta: a frequncia absoluta mais alta; letra (ou nmero) com a frequncia mais
alta; trs letras minsculas com frequncia nulas (ordem alfabtica). Coloque os trs valores
separados por espaos.

3) Exerccios

159

indicador.c
Faa um programa que leia um ficheiro de cdigo em linguagem C, e processe todos os blocos
globais, sendo um bloco global todos os pares de chavetas que no esto dentro de nenhum outro
par de chavetas. O programa deve retornar o seguinte indicador de complexidade, bem como os
seus parciais:

I=

(Conds + 2Ciclos + 1)

+ Instruesi

i =1... N

Notas:

Condsi o nmero de condicionais no bloco global i (if/case)

Ciclosi o nmero de ciclos no bloco global i (for/while)

Instruesi o nmero de instrues no bloco global i (contabilizar os ponto e vrgula).

O programa deve indicar os parciais por cada bloco global, indicando o nmero de
condicionais, ciclos, comandos e o parcial at ao momento aps o processamento desse
bloco.
Tenha em ateno aos comentrios, strings e caracteres que no so necessrios para o
clculo deste indicador

Execuo de exemplo:
C:\>indicador indicador.c
conds
ciclos comandos
0
0
2
1
0
6
1
0
5
0
1
4
0
3
5
4
3
17
2
2
10
4
6
24
3
3
18
Indicador: 717

parcial
3
13
22
35
89
227
286
599
717

Pergunta: aproveitando o ficheiro 3) more.c, utilizado nos exerccios anteriores, indique na


resoluo todos os parciais (4 nmeros por cada bloco), separados por espaos.

PARTE IV ANEXOS

14. Compilador e Editor de C

14.

161

COMPILADOR E EDITOR DE C

Neste anexo esto as instrues para a instalao do compilador e editor aconselhado. Pode
naturalmente utilizar qualquer outro. No entanto, de evitar a utilizao de um ambiente de
desenvolvimento com o compilador, editor e debugger integrado, como o Visual Studio, Dev-C++, ou
o Eclipse. Ter oportunidade de o fazer quando tiver experincia de programao, actualmente um
ambiente de desenvolvimento pode ser um factor de distraco, e em nada contribui para a
aquisio das competncias: capacidade de implementao de pequenos programas. Aconselha-se
um pequeno compilador de linha de comando, e opcionalmente um editor com algumas facilidades
de edio e visualizao de cdigo C.

TCC: TINY C COMPILER


URL: http://bellard.org/tcc/
Instalao:
1.
2.
3.
4.

Descarregar (Windows binary distribution) do site;


Descompactar os ficheiros para "C:\Program Files";
Colocar no path a directoria "C:\Program Files\tcc";
Escrever e executar o programa "Ola Mundo!".

Colocar no path a directoria "C:\


"C:\Program Files\
Files\tcc":
tcc"

Control Panel System Advanced


Environment Variables:

Na dialog das variveis de ambiente, seleccione a


varivel Path (cuidado para no apagar o seu
contedo):

162

Parte IV Anexos
Ao editar, deve adicionar no final a directoria
"C:\Program Files\tcc". Ateno que as directorias
tm de estar separadas por um ponto e vrgula.

Exemplo de utilizao:

Abrir uma linha de comandos:


Iniciar Todos os Programas Acessrios Linha de Comandos

Na linha de comandos v para a directoria onde pretende ter os exerccios. Para mudar de
directoria utilize o comando cd (change directory), seguido da directoria (neste exemplo, para no
indicar qualquer directoria, utiliza-se a directoria raiz, mas deve criar uma directoria para os
exerccios):
C:\...>cd
cd \ ENTER

Editar e criar um ficheiro de texto com o Notepad:


C:\>notepad ola.c ENTER

Ao abrir o Notepad, como o ficheiro "ola.c" no


existe, h uma mensagem a dizer que este no existe e
se o quer criar. Responda que sim.
Compilar o ficheiro criado:
C:\>tcc
tcc ola.c ENTER

Erros possveis:

'tcc' is not recognized as an internal or external command, operable program or


batch file.
o provavelmente a varivel path no tem a directoria do tcc correctamente
configurada. Para confirmar isso, execute o comando "C:\>path
path"
path e verifique
se a directoria do TCC est no path.
tcc: file 'ola.c' not found
o provavelmente no editou e gravou um ficheiro ola.c com o Notepad, na
mesma directoria onde est. No se esquea de copiar exactamente o que est
no exemplo, e ao sair do Notepad gravar
ola.c:1: ';' expected
o provavelmente o ficheiro gravado no est igual ao exemplo. Copie
exactamente o texto no exemplo letra por letra e grave ao sair do Notepad.

Se no houve erros, no devolvido texto nenhum e aparece novamente a prompt "C:\>". Nesse
caso executar o programa criado:
C:\>ola
ola ENTER
Ola Mundo!
C:>

Nota: criado um executvel com o mesmo nome que o ficheiro fonte, mas com extenso exe.
Nesta UC crie programas utilizando apenas um s ficheiro em linguagem C, neste caso o ficheiro
ola.c.

14. Compilador e Editor de C

163

NOTEPAD++
URL: http://notepad-plus-plus.org/

Este editor utiliza cores conforme a sintaxe do C (suporta tambm outras linguagens), de forma a
evitar alguns erros sintcticos que porventura se podem cometer por engano no Notepad, e que
seriam detectados apenas quando se fosse compilar.

Associe o Notepad++ aos ficheiros com extenso C, de forma a poder abrir o ficheiro no Notepad++
escrevendo apenas o nome do ficheiro na linha de comando, ou clicando no ficheiro no Windows.
Clique com o boto direito no ficheiro, e abra seleccionando outra aplicao (seleccione o
Notepad++, se no estiver listado, utilize o boto "Browse"). No se esquea de seleccionar a caixa
de seleco em baixo.

Para abrir o ficheiro ola.c com o Notepad++, basta agora escrever o nome do ficheiro:
C:\>ola.c ENTER

164

Parte IV Anexos

15.

NO CONSIGO PERCEBER O RESULTADO DO PROGRAMA

Ainda no fiz os exerccios verdes, e no consigo perceber o que est a acontecer no programa, e
porque no tem o resultado que espero:
1. Faa a execuo passo-a-passo do seu programa. No h nada como o papel e lpis para
compreender um conceito. Ir ver que o programa no faz o que pretende, e assim reflectir
sobre a mudana que necessita efectuar no cdigo para ter o comportamento esperado.
2. No h segundo ponto, se no conseguir fazer a execuo passo-a-passo, reveja os captulos,
ou pea ajuda a terceiros.
J fiz exerccios azuis, e tenho um programa que tem um comportamento e/ou resultado
incompreensvel, aparenta mesmo ter vontade prpria:
1. Utilizar os parmetros mais simples possvel, se funcionar complicar ligeiramente os
parmetros at obter uma situao de erro;
2. Imprimir no ecr os valores atribudos a variveis, na zona suspeita;
3. Comentar cdigo que insuspeito, e verificar se o erro se mantm, continuando at obter
o programa mnimo que d o erro;
4. Tentar formalizar a questo para que uma pessoa externa ao projecto possa analisar o
problema.
Ao utilizar parmetros mais simples e verificar que funciona bem, pode identificar a zona do cdigo
onde no h problema, ficando apenas parte do cdigo suspeito. Ao imprimir os valores das
variveis, pode detectar o local e a altura em que os valores deixam de estar correctos. Se o
programa funcionar comentando o cdigo insuspeito, pode-se confirmar que o problema reside na
zona suspeita. Finalmente, ao tentar formalizar a questo, removendo portanto tudo o que
especfico do problema e colocando o cdigo no abstracto de forma a se colocar a questo a outra
pessoa, pode-se identificar o problema mesmo antes de consultar a pessoa externa ao projecto.
Pode acontecer que seja mais simples a estratgia inversa no ponto 3, ou seja, comentar o cdigo
suspeito. Se no for simples manter o programa a funcionar comentando o cdigo insuspeito, pode
ser mais fcil manter o programa a funcionar comentando o cdigo suspeito, e verificar se o
problema se mantm. Se desaparecer, pode-se reduzir a zona do cdigo suspeito a comentar, de
forma a localizar o problema.
Situao de desespero: estou s voltas com isto, mesmo incompreensvel!
No h nada incompreensvel, o computador no tem vontade prpria, nem o problema que est a
lidar complicado demais. Apenas devido ao cansao no consegue ver pormenores que so
simples mas lhe passam ao lado e lhe permitiriam descobrir a causa do problema. O melhor parar
e continuar numa outra altura, aps uma noite de descanso. O mais provvel ser que ao retornar
ao problema j tenha a encontrado resposta para o problema sem saber como.

15. No consigo perceber o resultado do programa

165

EXEMPLO DE CDIGO INCOMPREENSIVEL


Este exemplo foi reportado por um estudante18, sendo a parte incompreensvel o facto de existirem
valores finais distintos, com e sem o printf na linha 60. Tal no deveria acontecer porque o
printf no altera valores de variveis.
Ao cdigo apresentado foi cortado o bloco inicial do main que no est relacionada com o problema,
e comentrios:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
18

#include <stdio.h>
int mdc(int x, int y)
{
int i, aux;
int n = 0;
int resto;
int mdc1 = 1;
int v1[1000];
resto = y % x;
if(resto == 0)
{
mdc1 = x;
}
else if(resto > 0)
{
x--;
for(i = 1; i < x; i++)
{
resto = y % i;
if(resto == 0)
{
v1[n] = i;
mdc1 = i;
n++;
}
}
n--;
x++;
aux = 0;
for(i = 1; i <= n; i++)
{
resto = x % v1[i];
if(resto == 0)
{
mdc1 = v1[i];
aux++;
}
}
}
if(aux == 0)
mdc1 = 1;
return mdc1;
}

void main()
{
int x, y, i, j;
int mdcr = 0;
int mdc1 = 0;
int soma = 0;
for(i = 1; i <= 100; i++)
{

Espao central, UC Programao, 2010/2011

166
57
58
59
60
61
62
63
64
65
66 }

Parte IV Anexos
for(j = i + 1; j <= 100; j++)
{
soma += mdc(i, j);
/* printf(" (%d,%d) mdc =%d\n", i, j, mdc(i, j)); */
}
printf("A soma intermedia: %d\n", soma);
}
printf("A soma dos MDC dos pares de numeros de 1 a 100: %d\n", soma);

Programa 15-1 Cdigo incompreensvel

Execuo de exemplo:
C:\>exemplo1
A soma intermedia:
A soma intermedia:
A soma intermedia:
A soma intermedia:
A soma intermedia:
A soma intermedia:
...
A soma intermedia:
A soma intermedia:
A soma intermedia:
A soma dos MDC dos

99
197
294
414
509
667
10147
10148
10148
pares de numeros de 1 a 100: 10148

Este valor est incorrecto, mas descomentando o printf, o valor fica correcto. Sabendo que este
tipo de funcionamento originado por acesso a valores de memria fora dos locais declarados,
pode-se analisar com ateno o cdigo, de forma a verificar todos os locais em que tal ocorre:

Utilizao de uma varivel no inicializada:


o Todas as variveis declaradas no inicializadas, tm um comando de atribuio
antes da sua utilizao
o Os elementos do vector, antes de serem utilizados so sempre inicializados
Escrita de uma varivel fora da declarao:
o O nico cdigo suspeito o vector v1, que poderia inadvertidamente aceder fora do
espao declarado, nomeadamente para valores negativos: aps uma inspeco
atenta, verifica-se que o vector inicializado at n, e o ciclo seguinte apenas utiliza
valores at n, nunca podendo ser negativos

Se quiser pare de ler aqui e tente encontrar o problema com o cdigo acima.
Falhando a inspeco ao cdigo, parte-se para a sua alterao, nomeadamente colocando
parmetros mais pequenos, cortando cdigo irrelevante, e colocando printfs com valores das
variveis, para se poder reduzir o cdigo suspeito. o que feito no cdigo seguinte, em que o valor
100 nos ciclos foi reduzido para 6, o printf com argumentos, foram retirados os argumentos, e
adicionados dois printfs na funo mdc, de forma a saber em que valores da funo os valores
retornados so distintos:
1 #include <stdio.h>
2 int mdc(int x, int y)
3 {
4
int i, aux;
5
int n = 0;
6
int resto;
7
int mdc1 = 1;
8
int v1[1000];
9
printf(" (%d,%d)", x, y);
10
resto = y % x;
11
if(resto == 0)

15. No consigo perceber o resultado do programa


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

{
mdc1 = x;
}
else if(resto > 0)
{
x--;
for(i = 1; i < x; i++)
{
resto = y % i;
if(resto == 0)
{
v1[n] = i;
mdc1 = i;
n++;
}
}
n--;
x++;
aux = 0;
for(i = 1; i <= n; i++)
{
resto = x % v1[i];
if(resto == 0)
{
mdc1 = v1[i];
aux++;
}
}
}
if(aux == 0)
mdc1 = 1;
printf("=%d", mdc1);
return mdc1;
}
void main()
{
int x, y, i, j;
int mdcr = 0;
int mdc1 = 0;
int soma = 0;
for(i = 1; i < 6; i++)
{
for(j = i + 1; j <= 6; j++)
{
printf(" ");
soma += mdc(i, j);
}
printf("A soma intermedia: %d\n", soma);
}
printf("A soma dos MDC dos pares de numeros de 1 a 100: %d\n", soma);
}

Programa 15-2 Programa incompreensvel, preparado para debug

Execuo de exemplo com printf na linha 59:


C:\>exemplo2
(1,2)=1 (1,3)=1 (1,4)=1 (1,5)=1 (1,6)=1A soma intermedia: 5
(2,3)=1 (2,4)=2 (2,5)=1 (2,6)=2A soma intermedia: 11
(3,4)=1 (3,5)=1 (3,6)=3A soma intermedia: 16
(4,5)=1 (4,6)=2A soma intermedia: 19
(5,6)=1A soma intermedia: 20
A soma dos MDC dos pares de numeros de 1 a 100: 20

167

168

Parte IV Anexos

Execuo de exemplo sem printf na linha 59:


C:\>exemplo2
(1,2)=1 (1,3)=1 (1,4)=1 (1,5)=1 (1,6)=1A soma intermedia: 5
(2,3)=1 (2,4)=1 (2,5)=1 (2,6)=1A soma intermedia: 9
(3,4)=1 (3,5)=1 (3,6)=1A soma intermedia: 12
(4,5)=1 (4,6)=2A soma intermedia: 15
(5,6)=1A soma intermedia: 16
A soma dos MDC dos pares de numeros de 1 a 100: 16

Pode-se agora ver o que no era possvel com 100 valores, despistando tambm a situao da
funo mdc ser chamada duas vezes no ciclo, a segunda no printf. As diferenas esto nos pares
(2,4), (3,6), (2,6), que so 1 em vez de serem 2, 3 e 2.
Esta informao limita grandemente o cdigo suspeito, sendo agora a inspeco ao cdigo centrada
na execuo da funo mdc com esses valores. Torna-se claro qual o problema: quando o resto da
diviso nula, a varivel aux no est inicializada, sendo no entanto utilizada. Se o seu valor
desconhecido for nulo, o resultado da funo incorrecto. A inicializao da varivel aux para um
valor no nulo, suficiente para resolver este problema.

16. Funes standard mais utilizadas

16.

169

FUNES STANDARD MAIS UTILIZADAS

Exemplos de chamadas:

printf(texto %d %g %s %c, varInt, varDouble, varStr, varChar);


Imprime no ecr uma string formatada, em que substitudo o %d pela varivel inteira seguinte na lista, o %g pela
varivel real na lista, o %s pela varivel string na lista, o %c pela varivel carcter na lista.

scanf(%d, &varInt); gets(str);


scanf a funo inversa do printf, l um inteiro e coloca o seu resultado em varInt, cujo endereo fornecido. A
funo gets l uma string para str.

Prottipos:

int atoi(char *str); float atof(char *str);


Converte uma string num nmero inteiro/real respectivamente

int strlen(char *str);


Retorna o nmero de caracteres da string str

strcpy(char *dest, char *str); [strcat]

char *strstr(char *str, char *find);


char *strchr(char *str, char find);

Copia str para dest, ou junta str no final de dest, respectivamente

Retorna a primeira ocorrncia de find em str, ou NULL se no existe. Na verso strchr find um carcter.

char *strtok(char *string, char *sep);


char *strtok(NULL, char *sep);
Retorna um apontador para uma token, delimitada por sep. A segunda chamada retorna a token seguinte, na mesma
string, podendo-se continuar a chamar a funo at que retorne NULL, o que significa que a string inicial no tem mais
tokens para serem processadas.

sprintf(char *str, ); sscanf(char *str,);

int strcmp(char *str1, char *str2);

Estas funes tm o mesmo funcionamento de printf/scanf, mas os dados so colocados (ou lidos) em str.
Retorna 0 se str1 igual a str2, retornando um valor negativo/positivo se uma string maior/menor que a outra

int isalpha(int c); [isdigit,isalnum,islower,isupper,isprint]


Retorna true se c uma letra / dgito numrico / letra ou dgito / minscula / maiscula / imprimivel.

void *malloc(size_t); free(void *pt);


malloc retorna um apontador para um bloco de memria de determinada dimenso, ou NULL se no h memria
suficiente, e a funo free liberta o espao de memria apontado por pt e alocado por malloc

FILE *fopen(char *fich, char *mode); fclose(FILE *f);


fopen abre o ficheiro com nome fich, no modo mode (rt leitura em modo texto, wt escrita em modo texto),
e fclose fecha um ficheiro aberto por fopen

fprintf(f,); fscanf(f,); fgets(char *str, int maxstr, FILE *f);


idnticos ao printf/scanf mas direccionados para o ficheiro, e fgets uma verso do gets mas com limite
mximo da string indicado em maxstr.

int feof(FILE *f);


feof retorna true se o ficheiro f est no fim, e false c.c.

fseek(f,posicao,SEEK_SET); fwrite/fread(registo,sizeof(estrutura),1,f);
funes de leitura binria (abrir em modo rb e wb). fseek posiciona o ficheiro numa dada posio,
fwrite/fread escrevem/lem um bloco do tipo estrutura para o endereo de memria registo.

int rand(); srand(int seed);


rand retorna um nmero pseudo aleatrio e srand inicializar a sequncia pseudo aleatria

time_t time(NULL); clock_t clock();


time retorna um nmero segundos que passaram desde uma determinada data, e clock o nmero de instantes (h
CLOCKS_PER_SEC instantes por segundo)

double sin(double x); [cos,log,log10,sqrt]


double pow(double x,double y);
Funes matemticas mais usuais, com argumentos e valores retornados a double

170

Parte IV Anexos

17.

ERROS COMUNS

Neste anexo compilam-se os erros mais comuns encontrados em actividades de avaliao, de forma a auxiliar
a prpria pessoa possa rever o seu cdigo, bem como o avaliador a corrigir trabalhos, que assim suficiente
referir que para o bloco de cdigo X aplica-se o erro Y, no sendo necessrio reproduzir toda a argumentao
em volta do erro. Cada erro identificado com um nome, a forma em que ocorre do ponto de vista de quem
comete o erro, o problema que o erro implica, e a resoluo a adoptar por quem tenha o cdigo com o erro.
No problema indicada a gravidade do erro, um de trs nveis: baixa, moderada, elevada. Este indicador de
gravidade foi feito tendo em vista a descontar, num critrio em que estes erros sejam contabilizados, 33%
pela existncia de um erro de gravidade elevada, 25% pela existncia de um erro de gravidade moderada, e
10% pela existncia de um erro de gravidade baixa. Apenas os 3 erros mais graves devem ser contabilizados,
para que quem tenha cometido apenas erros de gravidade baixa, sofra um desconto mximo de 30%.

Nomes sem significado

Variveis com
nomes quase
iguais

Variveis Globais

Funes com
parmetros no nome

Erro

Forma

Problema

Resoluo

Para distinguir uma


constante importante
na
funo,
basta
colocar o valor do
parmetro no nome da
funo, por exemplo
funcao12, para a funo
que retorne o resto da
diviso por 12. Desta
forma evita-se utilizar
um argumento.
Em vez de utilizar
alguns dos argumentos
nas funes, declara-se
a varivel globalmente
em vez de na funo
main, de forma a no
s ser acessvel a todas
as funes, como no
ser necessrio pass-la
como argumentos nas
funes

Se distingue uma constante importante no nome da


funo, ento deve colocar a constante como
argumento da funo, ficando a funo a funcionar
para qualquer constante. Caso no o faa, corre o
risco de ao lado de funcao12, ser necessrio a
funcao4, funcao10, etc., ficando com instrues
parecidas. No poupa sequer na escrita dado que
coloca no nome da funo a constante que utiliza na
funo.
Gravidade: elevada

Se tem casos destes no seu cdigo, deve fazer uma


troca simples: substituir todas as constantes
dependentes de 12 pelo argumento, ou uma
expresso dependente do argumento.

Ao fazer isto, no s mascara as ms opes na


diviso do cdigo por funes, dado que no
controla os argumentos da funo, como mais
importante, perde principal vantagem das funes:
quando implementa/rev a funo, apenas tem de
considerar a funo e pode abstrair-se do resto do
cdigo. Como h variveis globais, ao implementar a
funo tem que se considerar todos os restantes
locais que essas variveis so utilizadas (leitura /
atribuio), para que o cdigo na funo seja
compatvel. Por outro lado, ao no se aperceber
quando cria a funo do nmero de argumentos que
recebe e nmero de variveis que altera, pode estar
a criar funes que reduzem muito pouco a
complexidade, sendo a sua utilidade diminuta.
Gravidade: elevada
Esta situao uma indicao clara que necessita de
um vector nome[4]. Se no utilizar o vector no
possvel depois fazer alguns ciclos a iterar pelas
variveis, e iro aparecer forosamente instrues
idnticas repetidas por cada varivel.
Gravidade: elevada

Utilize o seu cdigo com variveis globais para


fazer uma reviso geral. Deve remover todas as
variveis globais e coloc-las na funo main,
deixando apenas as constantes globais que faam
sentido estar definidas globalmente, e macros.
Depois deve ir refazendo os argumentos das
funes, e eventualmente rever as funes que
necessita de forma a ter funes com poucos
argumentos e com uma funcionalidade clara e
simples.

O cdigo deve ser lido no s pelo compilador como


por outras pessoas, pelo que a m utilizao de
nomes revela por um lado a m organizao mental
de quem o escreve, e por outro um desrespeito por
quem l o cdigo. A troca de todos identificadores
por nomes abstractos e sem sentido, pode ser uma
forma para tornar o cdigo ilegvel, no caso de se
pretender proteg-lo. Pode tambm ser uma
situao confundida com uma situao de fraude,
resultante de uma tentativa de um estudante alterar
os identificadores de um cdigo obtido ilicitamente.
Gravidade: elevada

Verificar todos os identificadores de uma s letra,


ou muito poucas letras, bem como nomes
abstractos, funes sem uma grandeza associada,
e procedimentos sem verbo. Se no consegue
encontrar nomes claros para um identificador,
seja uma varivel, uma funo, o nome de uma
estrutura, etc., pense numa palavra em que
explique para que a varivel serve, o que a
funo/procedimento faz, o que contm a
estrutura, etc. Se no consegue explicar isso, ento
o identificador no existe.

Algumas
variveis
necessrias tm na
verdade
o
mesmo
nome, pelo que se
utiliza
os
nomes
nomeA, nomeB, nomeC,
nomeD, ou nome1,
nome2, nome3, nome4
Atendendo a que um
bom nome gasta tempo
e irrelevante para o
bom funcionamento do
cdigo, qualquer nome
serve.

Deve nesta situao procurar alternativas de fazer


cdigo em que esta situao lhe tenha ocorrido.
No utilize cdigo de outra pessoa, apenas o seu
cdigo com este tipo de erro lhe poder ser til.

17. Erros comuns

Indentao varivel

Funes com muitos


argumentos

Funes muito grandes

Utilizao do goto

Erro

171

Forma

Problema

Resoluo

Numa dada instruo,


sabe-se a condio
pretendida para voltar
para cima ou saltar
para baixo. suficiente
colocar um label no
local
desejado,
e
colocar o goto dentro
do condicional. Este
tipo de instruo pode
ser motivado pelo uso
de fluxogramas.

Perde-se nada mais nada menos que a estrutura das


instrues. A utilizao ou no desta instruo, que
define se a linguagem estruturada, ou no
estruturada (por exemplo o Assembly). Se de uma
linha de cdigo se poder saltar para qualquer outra
linha de cdigo, ao analisar/escrever cada linha de
cdigo, tem que se considerar no apenas as linhas
que a antecedem dentro do bloco actual, como todas
as linhas de cdigo, dado que de qualquer parte do
programa pode haver um salto para esse local. Esta
situao ainda mais grave que a utilizao de
variveis globais, dado que para compreender 1
linha de cdigo, no apenas os dados que
necessitam de ser considerados, como tambm
todas as instrues no programa. A complexidade do
programa nesta situao cresce de forma quadrtica
com o nmero de linhas. Mesmo sem goto, no caso
de variveis globais, a complexidade do programa
crescer de forma linear, enquanto sem variveis
globais a complexidade igual maior funo, pelo
que cresce menos que o nmero de linhas de cdigo.
Gravidade: elevada
Ao ter uma funo grande, vai ter problemas de
complexidade. No s as declaraes de variveis da
funo ficam longe do cdigo onde so utilizadas,
como no consegue visualizar a funo de uma s
vez, sendo muito complicado verificar o cdigo. Esta
uma situao clara de m utilizao da principal
ferramenta da programao, que lhe permite
controlar a complexidade do cdigo: abstraco
funcional.
Gravidade: moderada

Utilizar as estruturas de ciclos disponveis, bem


como condicionais, e funes. Se o salto para
trs dentro da mesma funo certamente um
ciclo o que pretendido, mas se para a frente,
provavelmente um condicional. Se um salto
para uma zona muito distante, ento
provavelmente uma funo que falta. No caso de
utilizar fluxogramas, deve considerar primeiro
utilizar o seu tempo de arranque vendo execues
passo-a-passo de forma a compreender o que
realmente um programa, sem segredos, e resolver
exerccios.

Tudo
o
que

necessrio fazer, vai


sendo acrescentado na
funo, e esta fica com
cada vez uma maior
dimenso.
Esta
situao ocorre devido
ao problema ser muito
complexo.

Aps criar a funo, ao


adicionar
como
argumentos
as
variveis que esta
utiliza
(leitura
/
atribuio), este valor
revela-se elevado, mas
o que necessrio.

Como a indentao ao
gosto do programador,
cada qual coloca a
indentao como lhe d
mais jeito ao escrever.

Se o nmero de argumentos muito elevado, a


abstraco que a funo representa fraca, uma vez que
para utilizar a funo, necessrio ter muita
informao. Para alm disso, se a funo for muito
curta, pode dar mais trabalho a arranjar os parmetros
certos para chamar a funo, que colocar o seu cdigo,
sendo assim a funo mais prejudicial que til. No caso
dos diversos argumentos estarem relacionados, significa
que no foram agregados numa estrutura.
Gravidade: moderada
A leitura fica prejudicada se a indentao no for
constante. Por um lado no fcil identificar se h
ou no algum erro do programador em abrir/fechar
chavetas, dado que a indentao pode ser sua opo
e no esquecimento de abrir/fechar chavetas. Por
outro lado, no possvel a visualizao rpida das
instrues num determinado nvel, dado que o nvel
varivel, sendo necessrio para compreender algo
de o cdigo analis-lo por completo. Com o cdigo
indentado, pode-se ver o correcto funcionamento de
um ciclo externo, por exemplo, sem ligar ao cdigo
interno do ciclo, e assim sucessivamente.
Gravidade: moderada

Deve dividir a funo em partes que faam


sentido, de forma a ter funes pequenas e com
poucos argumentos. Dividir a funo s fatias m
ideia, dado que os argumentos necessrios para
cada fatia podem ser muitos. Se houver vrias
fases, sim, colocar cada fase numa funo, caso
contrrio deve verificar se os ciclos interiores
podem construir funes que no s utilizem
poucos argumentos como tambm tenham
possibilidade de ser utilizadas em outras
situaes. Se a funo reunir estas condies, ter
certamente um nome claro.
Deve procurar alternativas na criao de funes.
Dividir uma funo existente a meio, provoca
situaes destas. Para desagregar uma funo, deve
procurar o cdigo repetido, se no existir, o cdigo
no interior dos ciclos tipicamente um bom
candidato a funo, de forma a manter em cada
funo um ou dois ciclos. No caso de as variveis
estarem relacionadas, criar uma estrutura para as
agregar.

Indente o cdigo com 2 a 8 espaos, mas com o


mesmo valor ao longo de todo o cdigo. Se utilizar
tabs, deve indicar o nmero de espaos
equivalente, no cabealho do cdigo fonte, caso
contrrio considera-se que o cdigo est mal
indentado. Se pretender subir a indentao com a
abertura de uma chaveta, pode faz-lo mas tem de
utilizar sempre chavetas, caso contrrio ficam
instrues que esto no mesmo nvel mas no
cdigo ficam e indentaoes distintas. Qualquer
que seja as opes, no pode existir instrues no
mesmo alinhamento que pertenam a nveis
distintos (ou vice-versa). A posio das chavetas
indiferente, mas tem que se apresentar um estilo
coerente ao longo do cdigo.

172

M utilizao da
Recurso

Comentrios
M utilizao do
Instrues parecidas
explicam a linguagem switch ou cadeia Utilizao de system
no seguidas
C
if-else

Instrues parecidas
seguidas

Erro

Parte IV Anexos
Forma

Problema

Resoluo

Quando necessrio
uma instruo que
parecida
com
a
anterior,
basta
seleccion-la e fazer
uso de uma das
principais
vantagens
dos
documentos
digitais: copy/paste.

O cdigo ficar muito pesado de ler, podendo


tambm prejudicar a escrita. sinal que est a falhar
um ciclo, em que as pequenas mudanas entre
instrues, em vez de serem editadas e alteradas,
essa alterao colocada dependente da varivel
iteradora, fazendo numa s instruo a chamada a
todas as instrues parecidas, debaixo de um ciclo. O
cdigo no s fica mais simples de ler, como se for
necessria outra instruo basta alterar a expresso
lgica de paragem do ciclo, em vez de um copy/paste
e edio das alteraes.
Gravidade: moderada
O copy/paste vai dificultar a leitura, e tambm a
escrita tem de ser com muito cuidado, para poder
editar as diferenas. Se esta situao ocorrer, uma
indicao clara que necessria uma funo com as
instrues que so parecidas e devem ser
reutilizadas. Nos argumentos da funo deve ir
informao suficiente para que a funo possa
implementar as diferenas.
Gravidade: moderada

Se encontra situaes destas no seu cdigo, estude


alternativas para utilizar ciclos.

Quer seja a mudar o cdigo de pgina, quer seja a


limpar o texto da consola, ou a copiar um ficheiro de
um lado para o outro, qualquer comando que
escreva, dependente do sistema operativo. Perde
portabilidade e o seu programa em vez de fazer uma
funo concreta, no mais que um script que
chama outros programas, e para isso todos os
sistemas operativos tm uma linguagem de scripting
mais adequada. Se quiser utilizar cdigo de outros
programas, deve integr-los.
Gravidade: moderada
Muitas vezes ao utilizar no switch um ndice (ou
cadeia if-else), as instrues em cada caso ficam
muito parecidas, ficando com cdigo longo e de
custo de manuteno elevado. Se essa situao
ocorrer deve rever o seu cdigo, dado que est
provavelmente a falhar uma alternativa mais
potente, que a utilizao de vectores.
Gravidade: moderada
Esta situao um claro excesso de comentrios,
que prejudica a legibilidade. Quem l cdigo C sabe
escrever cdigo C, pelo que qualquer comentrio do
tipo atribuir o valor X varivel Y, desrespeitoso
para quem l, fazendo apenas perder tempo. Ao
comentar instrues da linguagem e deixar de fora
comentrios mais relevantes, pode indicar que o
cdigo foi reformulado por quem na verdade no
sabe o que este realmente faz.
Gravidade: moderada
Se a chamada recursiva est no final, e o cdigo tem
de passar sempre por esse condicional, ento pode
remover a chamada substituindo por um ciclo. Ao
chamar a funo recursivamente, as variveis locais
j criadas no vo mais ser necessrias, mas por
cada passo do ciclo ficar um conjunto de variveis
locais espera de serem destrudas. Se o ciclo for
muito grande, o stack ir certamente rebentar.
Gravidade: moderada

Remova todas as chamadas a system, e faa o


que pretendido fazer no programa com o seu
prprio cdigo.

Quando necessrio
uma ou mais instrues
que so parecidas com
outras que j escritas
noutra parte do cdigo,
basta seleccionar e
fazer uso de uma das
principais
vantagens
dos
documentos
digitais: copy/paste.
Para
implementar
algumas funes, que
existem no sistema
operativo,
pode-se
utilizar
a
funo
system, pelo que h
que aproveitar.

De forma a executar
instrues dependente
do valor de um ndice,
utilizar um switch
colocando as diversas
instrues para cada
caso do ndice.
De forma a explicar
tudo o que se passa no
programa, e no ser
penalizado, por vezes
desce-se to baixo que
se explica inclusive
linguagem C.

Uma funo tem um


conjunto de instrues
que
tm
de
ser
executadas
vrias
vezes. Uma forma de
fazer isto no final da
funo colocar um teste
a controlar o nmero
de execues, e chamar
a
funo
recursivamente.

Se aconteceu no seu cdigo, deve estudar quais as


diferentes alternativas para utilizao de funes
que tem, de forma a garantir que no lhe escapam
hiptese antes de optar.

Criar um vector com as constantes necessrias


para chamar numa s instruo todas as outras
dependentes do ndice.

Escreva como se estivesse a escrever a si prprio,


supondo que no se lembrava de nada daqui a uns
6 meses.

Substituir a chamada recursiva por um ciclo e


respectivo teste de paragem. Caso a chamada
recursiva no seja no final da funo, ou exista
mais que uma chamada recursiva, ento sim,
prefervel utilizar-se recurso.

17. Erros comuns

Variveis
desnecessrias

Funes
especficas

Alocar memria e no
testar se a operao foi
bem sucedida

Linhas de cdigo
nunca executadas

Declaraes ou atribuies a
variveis nunca utilizadas

Atribuies entre
variveis de tipos
distintos

Erro

173

Forma

Problema

Resoluo

Principalmente quando
se utiliza apontadores,
por vezes necessrio
fazer atribuies entre
apontadores de tipos
distintos, ou entre
apontadores e inteiros.
Isso no problema
porque um apontador
um nmero de 32 bits.
Por vezes declaram-se
variveis, ou fazem-se
atribuies a variveis,
um pouco para ver se o
programa
passa
a
funcionar. No entanto,
aps muito corte e
costura, por vezes
ficam atribuies a
variveis
que
na
verdade nunca so
utilizadas, embora tal
no afecte o bom
funcionamento
do
programa.
Por vezes em situaes
de stress tentam-se
vrias alternativas, e
fica algum cdigo que
na verdade nunca tem
hiptese
de
ser
executado, mas que
tambm no atrapalha.

Se atribui um apontador de um tipo a outro, este vai


aceder ao endereo de memria apontado como se
fosse o segundo tipo, quando este foi alocado e tem
dados como se fosse o primeiro tipo. Pode ter
consequncias imprevisveis, uma vez que os dados
ficam com o acesso incoerente. Em caso algum deve
assumir o tipo do apontador, uma vez que tal pode
variar conforme o compilador/computador em que
o cdigo for utilizado.
Gravidade: moderada
Se h uma varivel declarada que no utilizada,
essa varivel pode ser removida e o cdigo fica igual.
Se h uma atribuio a uma varivel que depois no
utilizada em nenhuma expresso, ento a
atribuio
desnecessria.
Ter variveis,
expresses, atribuies a mais, uma situao
indesejvel, dado que compromete a leitura do
cdigo que realmente funciona.
Gravidade: moderada

Se for necessria atribuio entre apontadores de


tipos distintos, ao alocar memria por exemplo,
ento deve utilizar no cdigo um cast para o tipo
de destino, de forma a clarificar que no se trata
de um erro. Fora a situao de alocao de
memria, no precisa de qualquer outra
atribuio entre tipos distintos. Em situaes
menos ortodoxas, pode utilizar o tipo union, para
fundir dados de mais de um tipo.

Se h cdigo a mais, este deve ser removido sobre


pena de perda de legibilidade e manuteno. Esta
situao ampliada quando h cdigo de diversas
provenincias, em que ao integr-lo ningum se
atreve a reeditar cdigo que funciona, para
seleccionar a parte do cdigo que deve ser
integrado.
Gravidade: moderada

A alocao de memria

uma
operao
comum,
e
se
o
programa no utiliza
grandes quantidades
de memria, sempre
bem sucedida, pelo que
no vale a pena
efectuar a verificao j
que
complica
desnecessariamente o
cdigo.
Para uma situao
necessria uma funo
concreta, pelo que
essa funo que
definida, utilizando as
constantes necessrias
dentro dessa funo.
Ao criar uma varivel
por cada necessidade
de registo de valores,
pode acontecer haver
variveis sempre com o
mesmo valor, mas tal
no impede que o
programa
funcione
correctamente.

Mesmo que a aplicao utilize pouca memria, a


alocao de memria pode falhar se o sistema
operativo tiver a memria esgotada por outras
aplicaes, ou por outras instncias da mesma
aplicao. A no verificao pode provocar acesso a
zonas de memria proibidas com resultados
imprevisveis.
Gravidade: moderada

Identificar as zonas de cdigo que de certeza que


no so executadas de modo algum, e apag-las.
Tanto pode ser feito por inspeco ao cdigo
como atravs de testes, submetendo o cdigo a
diversos casos de teste, desde que previamente se
tenha colocado informao de debug em cada
bloco de cdigo. Os blocos de cdigo em que no
tiver sido produzida nenhuma informao de
debug, so provavelmente os blocos de cdigo que
nunca so executados.
Em todas as alocaes de memria, verifique se
estas foram bem sucedidas, e no caso de falharem,
certifique-se que o programa tem um
procedimento vlido.

Verificar se h variveis declaradas que no so


utilizadas, e apag-las. Para cada atribuio,
seguir o fluxo do cdigo at uma possvel
utilizao. Se no existir, apagar a atribuio,
reinicializando o processo de verificao. No final
o cdigo real poder ser muito mais reduzido, do
que o gerado em situaes de stress em modo de
tentativa/erro.

Se as funes so muito especficas, no podem ser


reutilizadas, tanto no mesmo programa, como em
outros programas. Quanto mais genrica a funo,
maior o seu grau de reutilizao, sendo mais
simples de manter e detectar algum problema.
Gravidade: baixa

Reveja todas as constantes que tem numa funo


especfica, e passe as constantes que sejam
passveis de serem mudadas para argumentos da
funo, de forma a aumentar as possibilidades da
funo ser til sem alteraes em outras
situaes.

Ter muitas variveis com a mesma funo tem o


mesmo problema que uma atribuio, uma leitura.
Fica o cdigo mais complexo e difcil de ler sem
qualquer ganho. Pode acontecer que este problema
se reflicta no apenas por ter duas variveis sempre
com os mesmos valores, mas terem valores com uma
relao simples (o simtrico, por exemplo).
Gravidade: baixa

Deve identificar as variveis com atribuies e


utilizaes perto uma da outra, eventualmente os
nomes, e verificar se pode a cada momento
utilizar o valor de uma varivel em vez da outra.
Por vezes no simples a identificao/remoo
destas situaes quando h muitas variveis.
prefervel aplicar primeiramente a remoo de
declaraes/atribuies desnecessrias, e aplicar
a resoluo do erro uma atribuio, uma leitura,
antes deste erro.

Parte IV Anexos
Forma

Problema

Resoluo

As variveis devem ser


declaradas o mais perto
possvel do local onde
so utilizadas, e como
alguns compiladores de
C
permitem
a
declarao fora do
incio das funes, h
que aproveitar.

verdadeira a frase, mas no para quem se inicie na


programao, nem para a linguagem C. A
generalidade dos compiladores de C permite
declarao das variveis em qualquer parte do
cdigo, mas nem todos, pelo que perde
compatibilidade. Mais relevante a m influncia
que tal situao ter, facilitando a existncia de
funes grandes. Com as variveis declaradas em
cada bloco, no ir sofrer os efeitos de funes
grandes to intensamente, o que o pode levar a no
sentir a necessidade de criar funes.
Gravidade: baixa
Se h instrues relacionadas, estas devem ser
colocadas na mesma funo, se tal se justificar.
Colocar duas ou mais instrues na mesma linha vai
prejudicar a legibilidade e no s. O compilador
quando encontra algum problema refere a linha do
cdigo, e existindo mais que uma instruo nessa
linha, a informao menos precisa. Deixa de ser
possvel tambm seguir um nvel de indentao para
passar por todas as instrues nesse nvel, tem que
se fazer uma leitura atenta por todo o cdigo do
bloco.
Gravidade: baixa
O real teste de paragem fica diludo nas instrues
que alteram esta varivel, bem como condicionais
que utilizem o break, e ter algo em vez de num local
em vrios, torna sempre mais complicada a leitura e
manuteno de cdigo. Esta situao ocorre porque
falhou na identificao do real teste de paragem.
pior se necessitar de acompanhar esta varivel com
uma outra varivel que altera de valor em cada ciclo
(uma varivel iteradora).
Gravidade: baixa
A leitura severamente penalizada se no existir
uma norma clara seguida para os identificadores no
cdigo, na separao de palavras e utilizao de
abreviaturas. Um identificador com um mau nome
pode ser trocado o seu significado sem que o leitor
se aperceba, a no ser que tenha muita ateno,
resultando em horas perdidas procura de um bug
que
facilmente
encontraria
com
nomes
normalizados. As abreviaturas a utilizar, devem
incidir apenas para relacionar funes, por exemplo,
e nunca os nomes devem ser apenas abreviaturas ou
abstractos.
Gravidade: baixa
A utilizao de macros tem um custo, que incorre
nas linhas de cdigo onde utilizada: no
conhecido o valor real da macro. Se esta for utilizada
em situaes em que o fluxo de informao seja
claro, como um valor inteiro limite sendo os ciclos
desde uma constante at esse limite, no h
qualquer problema. Se no entanto for utilizada em
locais dependentes de outras constantes, sejam em
macros sejam no cdigo, ou que seja importante
saber o valor da macro para saber qual a sequncia
de instrues, ou num caso extremo, o valor da
macro necessrio para saber se uma instruo
vlida, evidentemente que o valor da macro em si
no pode ser alterado sem que o cdigo seja revisto.
Perde portanto o sentido, uma vez que prejudica a
leitura do cdigo e no facilita qualquer posterior
alterao. As macros devem ser independentes entre
si e de outras constantes.
Gravidade: baixa

Todas as declaraes fora do incio das funes


passam para o incio das funes. Reveja os nomes
das variveis se necessrio.

Macros em todas as constantes

Nomes no normalizados

Ciclos contendo apenas


uma varivel lgica no
teste de paragem

Duas instrues na
mesma linha

Erro
Declarao de variveis
fora do incio de funes

174

Quando as instrues
so pequenas e esto
relacionadas, mais
simples
coloc-las
todas na mesma linha.
Como uma questo de
estilo, no afecta o
funcionamento.

Esta estratgia consiste


em utilizar nos ciclos
(normalmente while),
uma varivel no teste
de paragem, atribuindo
o valor verdadeiro e
dentro do ciclo, quando
for necessrio sair dele,
fazer uma atribuio
varivel a falso.
Os nomes para as
variveis,
funes,
estruturas no afectam
o bom funcionamento
do cdigo, pelo que no
vale a pena perder
muito tempo. Ora ficam
de uma maneira, ora
ficam de outra.

Tudo o que constante


tem direito a uma
macro, de forma a
poder ser facilmente
alterada

Em todas as linhas com mais que uma instruo,


colocar uma instruo por linha.

Se optar com frequncia para esta situao, deve


rever o seu cdigo procurando alternativas.

Refazer os identificadores para utilizar uma s


norma de separao de nomes/abreviaturas. Se
houver cdigo de diversas provenincias,
aproveitar para rever e reformular de forma a
permitir a sua manuteno.

Remova do cdigo as macros que so dependentes


entre si, ou que dificilmente podem mudar sem
implicar outras alteraes no cdigo.

175

Forma

Problema

Resoluo

Para explicar bem o


identificador, este tem
que ter trs e quatro
palavras, ou mesmo
mais.

Com os identificadores muito longos, o cdigo tornase menos legvel, e provavelmente haver mais
instrues a ocuparem mais de uma linha de cdigo.
Gravidade: baixa

O cdigo deve ser lido


por quem perceba no
s da linguagem de
programao C, como
de algoritmos, e que
seja
suficientemente
inteligente
para
o
compreender.

Quem l cdigo C deve naturalmente possuir todas


essas caractersticas, mas tal no motivo para no
explicar as partes complexas. Os comentrios podem
afectar a legibilidade do cdigo, e quem est a ler
cdigo em que tem dificuldade em compreender, no
ir apreciar a forma rebuscada e hbil de quem o
escreveu, mas sim a simplicidade e clareza com que
o fez, ou o explica. Se no conseguir compreender
algo, ser certamente por culpa de quem escreveu
que no sabia o que fazia e l acabou por obter uma
soluo funcional em modo de tentativa e erro, de
baixa qualidade.
Gravidade: baixa
Esta situao pode indicar a existncia de uma
varivel desnecessria, e quantas mais variveis
desnecessrias o cdigo tiver, mais complexo fica,
para
no
falar
que
ocupam
memria
desnecessariamente. apenas justificvel no caso de
a expresso ser muito complexa, de forma a
clarificar o cdigo.
Gravidade: baixa

Deve procurar o essencial da varivel / funo /


estrutura, de forma a dar o nome sobre o
essencial, e comentar se necessrio na declarao
as particularidades que no so cobertas pelo
nome do identificador. Utilize no mximo 3
palavras, separadas por maisculas ou por _.
Nas zonas menos claras, comentar explicando
todos os truques / habilidades aplicadas. Se ficar
algum por explicar, ser confundido certamente
com cdigo que nem quem o fez sabe realmente
porque funciona, e mais vale refazer novamente
se houver algum problema.

Abrir um ficheiro e
Abrir um
No libertar
Uma atribuio, uma
no testar se foi bem ficheiro e no
memria aps alocar
leitura
aberto
chegar a fechar

Erro
Comentrios inexistentes Nomes muito
em partes complexas
longos

17. Erros comuns

Uma
varivel

atribuda
com
um
valor, para
depois
utiliz-lo na instruo
seguinte, no sendo
mais necessrio o valor
da
varivel.
Por
exemplo, para reunir os
valores necessrios
chamada
de
uma
funo.
Atendendo a que no
h erros por no
libertar memria, e
sendo a aplicao de
curta durao, justificase a no libertao da
memria,
j
que
quando a aplicao
terminar
tudo

libertado.
Por vezes abre-se o
ficheiro,
mas
a
operao de fechar no
causa
erros
e

desnecessria dado que


o ficheiro ser fechado
quando o programa
acabar.
Quando se sabe que o
ficheiro existe, no
necessrio testar se a
operao de abertura
do ficheiro foi bem
sucedida, perda de
tempo.

Ao fazer a atribuio do valor varivel, utiliza


uma expresso. Essa varivel depois utilizada de
seguida tambm uma s vez, pelo que mais vale
colocar a prpria expresso no local onde a
varivel utilizada.

A aplicao tem os endereos dos blocos de memria


que alocou, j no precisa deles, pode libert-los. Se
no o fizer, no h garantia da altura em que esses
blocos sero libertos, nem se o custo computacional
de os libertar o mesmo. O argumento da pequena
aplicao no deve ser justificao para qualquer
erro, o cdigo que funciona em pequenas aplicaes
poder ser reutilizado em outras aplicaes, e se for
bem feito, no necessitar de alteraes.
Gravidade: baixa
O nmero de ficheiros abertos um recurso limitado
pelo sistema operativo. Se um ficheiro no mais
preciso, deve ser imediatamente fechado de forma a
libertar recursos. Se esta prtica no for feita, pode
acontecer que pedidos de abertura de ficheiros por
esta ou outra aplicao sejam recusados.
Gravidade: baixa

Tenha sempre ateno ao alocar memria do local


exacto onde esse bloco ser certamente libertado.

Pode acontecer mesmo quando h a certeza que o


ficheiro existe, que a operao de abertura de
ficheiro seja mal sucedida, no caso de estarem
demasiados ficheiros abertos, ou por questes de
permisses, ou ainda por estarem duas aplicaes a
tentar abrir o ficheiro em modo de escrita. As
operaes seguintes, que utilizam o apontador
retornado para o ficheiro, ficam com um
procedimento incerto.
Gravidade: baixa

Testar sempre se o ficheiro foi bem aberto, e


apenas utilizar o apontador para o ficheiro no
caso de este ser diferente de NULL.

Verificar o ltimo local onde o ficheiro


necessrio, e chamar a correspondente funo
fclose de forma a fechar o ficheiro.

176

Parte IV Anexos

18.

EXERCCIOS: DICAS, RESPOSTAS E RESOLUES

Neste anexo so dadas dicas aos exerccios, forma para se validar a resoluo, e resolues.

DICAS EXTRA
Exerccio / problema
1) soma.c
no compreendo a
frmula

Explicao / sugesto / dica


N

Na frmula

i o

smbolo

significa que se deve somar a expresso

i =1

seguinte, tendo em conta que existe uma varivel que vai variar entre dois valores
(valor indicado em baixo, at ao indicado em cima). A varivel utilizada neste caso
a varivel i, desde o valor 1 at ao valor N. Se N=4, a frmula acima
4

equivalente a

i = 1 + 2 + 3 + 4
i =1

1) produto.c
no compreendo a
frmula

Na frmula

N!= i o smbolo
i =1

o equivalente ao

mas para

produtos. Neste caso, se N=4 a expresso acima fica

4!= i = 1 2 3 4
i =1

1) arranjos.c
no compreendo a
frmula

frmula

A( N , R ) =

N!
=
i
(N R)! i=
N R+1

utiliza

apenas

elementos

introduzidos nos exerccios anteriores. Neste caso, para N=4 e R=2 a expresso
fica:

1) arranjos.c
d-me 5079110400,
que o valor correcto
mas no est nas
respostas possveis
1) arranjos.c
D-me valores
estranhos, calculo N! e
(N-R)!, e depois divido
um valor pelo o outro

A(4,2) =

4
4
4!
4!
= = i = i = 3 4
(4 2)! 2! i=42+1 i=3

O valor correcto de combinaes 20, 8 a 8 realmente esse. No entanto essa no


a resposta correcta ao exerccio, que deve ser resolvido com inteiros de 4 bytes.
Troque todas as variveis inteiras para inteiros de 4 bytes e execute novamente o
programa.
Calculando ambos os factoriais est a fazer no s mais contas, como tambm
ultrapassa o limite dos inteiros mais cedo, atendendo que o factorial cresce muito
rapidamente. Se tiver um valor de R pequeno, pode conseguir calcular arranjos
mesmo com um N grande, em que no consegue obter o factorial de N, mas
consegue obter arranjos de N, R a R. Utilizar um tipo mais potente, resolve o
problema do limite para valores pequenos, mas teria o problema mesma para
muitos valores que poderiam ser calculados pela segunda frmula. Ateno que
todos os tipos inteiros devem ter 4 bytes, atendendo a que os exerccios esto
equilibrados para esse tipo de dados.

18. Exerccios: Dicas, Respostas e Resolues


1) fibonacci.c
no compreendo a
frmula

n
,n 2

F (n ) =
F (n 1) + F (n 2) , n > 2
Esta frmula tem uma definio recursiva. Para o valor 1 e 2, retorna 1 e 2
respectivamente (primeiro condicional). Para 3 ou superior, retorna a soma dos
dois valores imediatamente anteriores.
Exemplos:
F 3 = F 2 + F 1 = 2 +1 = 3

1) combinacoes.c
no compreendo a
frmula

177

( ) ( ) ()
F (4) = F (3) + F (2) = 3 + 2 = 5
F (5) = F (4) + F (3) = 5 + 3 = 8
F (6) = F (5) + F (4) = 8 + 5 = 13
C (N , R ) =

N
N!
= i
(N R )!R! i= N R+1

i
i =1

Veja primeiramente as explicaes para 1) arranjos.c. Para N=5, R=3, a expresso


acima fica:

3 4 5
1 2 3
1) combinacoes.c
obtenho o valor
incorrecto
1) euler.c
no compreendo a
frmula

Veja neste pequeno exemplo, como fica a nota sobre a multiplicao e diviso em
simultneo a cada passo.
Verifique se no estar a utilizar a frmula incorrecta, por exemplo:
N

i=R

i =1

pretendido o clculo do valor da constante de euler atravs da frmula:


K

1
n=0 n!

e=

Por exemplo, se K=9, a expresso pretendida :

1 1 1
1
+ + + ... +
0! 1! 2!
9!
Utilizando apenas as 4 operaes bsicas fica:

1 1 1
1
+ +
+ ... +
1 1 1 2
1 2 3 ... 9
1) trocos.c
no percebo a
pergunta

Tem de contar o nmero de moedas retornadas. Se fosse pedido o montante do


exemplo, isto , 1.79, o valor a colocar na resposta seria 6 (1 moeda de 1, 50,
20 e 5 cntimos e 2 moedas de 2 cntimos).

178
1) trocos.c
com o montante 0.02
no d o valor
correcto

Parte IV Anexos
Pode estar a utilizar em nmeros reais igualdades. No deve faz-lo, dado que a
representao dos reais tem preciso limitada. Veja-se o programa apresentado
no frum19:
1
2
3
4
5
6
7
8
9
10
11
12

#include <stdio.h>
int main()
{
float montante;
printf("Indique um montante igual a 0.02: ");
scanf("%f", & montante);
if(montante == 0.02)
printf("montante = 0.02\n");
else
printf("montante diferente de 0.02\n");
printf("montante-0.02= %.16g\n", montante-0.02);
}

Execuo do programa:
C:\>erro
Indique um montante igual a 0.02: 0.02
montante diferente de 0.02
montante-0.02= -4.470348362317633e-010
montante == 0.02 no deve ser feito, quanto

O teste
desigualdades:

muito deve-se efectuar duas

montante>=0.01999999 && montante<=0.02000001

1) primo.c
no percebo a
pergunta
1) pi.c
obtenho valores muito
diferentes de
1) pi.c
obtenho valores muito
perto de
2) rand.c
por vezes tenho
valores negativos

19

No caso do exerccio dos trocos, para ter em ateno o arredondamento, tem de


se adicionar 0.5 valores da unidade que se pretende arredondar. Assim, se
pretende cntimos, antes de se efectuar os testes de desigualdades, deve-se
somar 0.005 (uma vez que a unidade o 0.01). Em alternativa pode-se converter
o valor real para inteiros, portanto cntimos, mas na converso tem que se ter em
ateno com o arredondamento, caso contrrio o problema mantm-se, h o risco
de 0.02*100 passar a 1 em inteiro.
Deve somar apenas os nmeros divisores mnimos, no somando nada para o
caso dos nmeros primos. No deve somar todos os nmeros testados. No caso do
exemplo do enunciado, a resposta seria 3, dado que do nmero 99 o divisor
mnimo o nmero 3, e o nmero 97 primo. Ateno que um dos nmeros na
pergunta, apenas divisvel por um nmero superior a 100.
Ateno que o factorial de 0 1. Note ainda que a frmula calcula 1

Utilize o tipo double e a string de formatao


preciso 17 em notao cientfica.

%.17g

para obter o valor com

Para gerao de nmeros aleatrios devem-se utilizar inteiros positivos:


unsigned int v;

Espao central, UC Programao, 2010/2011

18. Exerccios: Dicas, Respostas e Resolues


2) rand.c
no percebo o
resultado 33

179

Primeira parte da frmula:

2315331282148053+ 82571 = 296859585237820


No entanto o nmero tem de caber num inteiro de 4 bytes, que tem um total de
hipteses de:

2 32 = 4294967296
Portanto, o valor que fica guardado no inteiro de 4 bytes ser:

2968595852 37820 % 4294967296 = 35672892


Falta agora efectuar a segunda parte da frmula, o resto da diviso com m:

35672892 % 428573 =101333


Finalmente, como o utilizador quer nmeros entre 0 e 99, tem de efectuar o resto
da diviso por 100:

101333 %100 = 33

2) find.c
no obtenho os
mesmos valores

O valor de seed ficar com 101333, j que esta ltima operao do resto da
diviso por 100 foi o utilizador da funo que fez, mas a implementao da funo
rand apenas se preocupa com o resto da diviso por m, e retorna 101333.
Verifique se a funo rand gera os mesmos valores aleatrios:
1
2
3
4
5
6
7
8
9

#include <stdio.h>
int main()
{
int i;
srand(1);
for(i=0;i<10;i++)
printf("%d ",rand());
}

Execuo do programa:

C:\>testerand
41 18467 6334 26500 19169 15724 11478 29358 26962 24464

Se estes valores no coincidirem, ento utilize a seguinte funo para gerar


valores aleatrios:
1 unsigned int randaux()
2 {
3
static long seed=1;
4
return(((seed = seed * 214013L + 2531011L) >> 16) & 0x7fff);
5 }

2) find.c
pedido a posio do
primeiro 2, o valor
absoluto ou o dgito 2?
2) maximo.c
no primeiro valor do
exemplo d-me 5.50 e
no 5.47

2) maximo.c
obtenho os valores do
exemplo correctos,
mas a resposta d-me
incorrecta

Se os nmeros a sarem fossem estes: 13 15 678 32 45 26 2


Dever-se-ia retornar 6, e no a posio do 2 no nmero 32.

Ateno nota sobre a gerao dos nmeros, no deve gerar valores entre 0 e
9999, porque isso poderia levar a gerar o zero, e no lhe interessa calcular o
logaritmo de zero.
O primeiro valor gerado da funo rand() 41, para coloc-lo entre 0,0001 e
1,0000, temos de somar 1 (porque a funo rand() pode devolver 0), ficando 42,
dividir por 10000, ficando 0,0042, e depois chamar a funo log (ln na
calculadora). Ora ln(0.0042)=-5,47267..., enquanto que ln(0.0041)=-5,4967.
No se est a esquecer de fazer o resto da diviso por 10000?
Est a gerar um vector com 1000 elementos (e no 10.000 elementos)?
Pode ainda estar a fazer rand()%9999+1 em vez de rand()%10000+1. Se fizer o
resto da diviso por 9999 no obtm os elementos distintos necessrios, j que
so precisos 10000 valores distintos (incio em 0 e fim em 9999).

180
2) mdc.c
que pares utilizar, por
exemplo, os pares
(1,1), e (2,2) devem
ser utilizados?

2) sort.c
qual a relao entre
ambos os vectores
mostrados?
2) trocos2.c
qual o significado do
20? O ltimo montante
ou o nmero de
montantes?
2) jogodados.c
obtenho os mesmos
valores entre
chamadas, com
preciso a duas casas
decimais
2) jogodados.c
no percebo a
pergunta
2) strreplace.c
a string pode
aumentar de
tamanho?
3) adiciona.c
o 116 o segundo
elemento da lista?

Parte IV Anexos
No devem ser utilizados todos os "pares de nmeros" distintos, nem os pares de
nmeros iguais: (1,1) e (2,2), etc. Relativamente ao nmeros distintos significa
que no devem ser utilizados os pares (1,2) e (2,1), apenas um.
Conjunto de pares =
{(1,2);(1,3);(1,4)...(1,100);
(2,3);(2,4);(2,5)...(2,100);
...
(97,98);(97,99);(97,100);
(98,99);(98,100);
(99,100)}
Os elementos so exactamente os mesmos, mas no output so mostrados apenas
os 10 primeiros, embora o vector tenha 1000 elementos. O segundo vector est
ordenado, da mostrar elementos diferentes, que so os 10 mais baixos.
o montante maior possvel, a iterao deve seguir a expresso indicada, at ao
valor mximo de 20 euros. No significa que o ltimo elemento ser exactamente
20, significa que o ltimo elemento no ser superior a 20.

srand(time(NULL)); deve ser chamado uma s vez, no incio do programa.

O gerador de nmeros aleatrios no to aleatrio assim, e normalmente existe


correlao dos primeiros nmeros gerados. Por outro lado a funo time retorna
o nmero de segundos passados desde uma determinada data, e entre chamadas
pode retornar o mesmo valor, levando a que sejam gerados os mesmos nmeros.
Com a funo clock seria melhor mas a mesma coisa porque 1/1000 segundos
para um computador ainda assim tempo suficiente para muitas instrues.
Pretende-se o nmero de lanamentos, e no os seus valores, (ex: "6 7 8" e no
"3.08 3.55 4.21").
Sim, se a string a substituir for maior.

O 116 o segundo valor na lista, mas o penltiplo a ser adicionado.

18. Exerccios: Dicas, Respostas e Resolues

181

VERIFICAO DAS RESPOSTAS


Para validar uma resposta de um exerccio, verifique se a mesma est na seguinte lista:
'#'
'%'
'&'
'('
')'
'/'
'@'
'['
']'
'{'
'}'
'0 160 241'
'0 160 41'
'0 2 3 0'
'0 60 241'
'1 1 3 6'
'1 160 41'
'1 2 3'
'1 2 3 4'
'1 3 4 6'
'1 4 3 6'
'1 60 241'
'10400600'
'113670040'
'12'
'12444'
'126'
'128 554 902'
'13015'
'14361'
'15 2 15 405 5 2 38 531'
'15 2 15 415 5 2 18 533'
'15 2 16 435 5 2 20 536'
'15 2 17 425 5 2 19 532'
'15 2 51 405 5 2 28 530'
'15 2 51 405 5 2 8 537'
'15231'
'155'
'16'
'16442663'
'165580141'
'169 464 242'
'17034'
'172'
'176324603'
'178'
'18'
'18 89'
'184'
'19 18'
'19 62 48'
'2.718281828459'
'2.718281828459046'
'2.71828182845955'
'2.8281828459'
'2.8281828459046'
'20'
'222404595'
'225561900'
'23 25 12 25 29 40 17 19'
'240 404 731'
'240 503 731'
'240 734 243'
'248 496 745'
'249'
'249 404 902'
'249 503 734'
'249 503 734'
'249 649 574'
'249 649 745'
'249 764 731'
'25'
'25 29 13 29 34 47 18 21'
'25003'
'258 496 574'
'258 649 740'

'26 30 1 13 30 34 47 19 21'
'26 30 1 13 30 34 48 19 20'
'26 30 1 14 30 35 48 19 22'
'26 30 2 12 30 35 46 19 20'
'26 30 2 13 30 35 49 19 21'
'26 30 2 15 31 36 50 19 23'
'267'
'271'
'28'
'286'
'3 2 3 1'
'3 5 4'
'3.14159265358979'
'3.1415926535897931'
'3.1415926535897962'
'3.15926535897'
'3.15926535897931'
'3000'
'32'
'3278'
'339'
'34'
'37'
'390'
'393'
'4 4 3'
'4 6 3'
'40'
'410001344'
'420'
'423500044'
'423501344'
'43'
'479001600'
'488224456'
'5 2 18 118'
'5 3 linhas que alocao com'
'5 3 linhas que com'
'5 3 no de nome'
'5 3 no linha comprimento de
nome'
'5 4 linhas alocao com'
'5 4 linhas que alocao'
'5 5 ecr linha maximo de
ficheiro'
'5 7 4'
'50'
'503 702 194'
'507'
'519 464 48'
'55'
'59'
'59 462 248'
'5995'
'6000'
'603255503'
'612'
'621'
'6222446'
'63'
'68 s bk'
'69 62 48'
'695'
'698'
'7 78'
'7 8 3'
'7 8 9'
'7.82101'
'7.82405'
'7.82444'
'70'
'70 s bj'
'70 s bjk'
'700 404 243'
'700 760 491'
'702 764 419'

'71 r bjk'
'714140004'
'714443004'
'714443104'
'72 r bjk'
'725'
'73712'
'74 r bj'
'74993'
'75 s bk'
'7500'
'77 r bk'
'784143104'
'8 8'
'8 9 8'
'8273'
'83311'
'83322'
'83333'
'84'
'896'
'9'
'9 1 9'
'9 8 9'
'9 98'
'91'
'924'
'94'
'942'
'95134'
'96200'
'98023'
'998.12321023'
'9998.12321234'
'99998.1132102111'
'99998.123210234'
'aaaa abc'
'abcd'
'argumentos ABC 123 123 ABC'
'argumentos ABC_123 123_ABC'
'argumentos ABC123 123ABC'
'argumentos ABC-123 123-ABC'
'bbb abcd'
'ccc abc'
'cesar 21090 cifra'
'cifra 012210 cesar'
'cifracesar 21090'
'cifra-cesar21090'
'dcba'
'jkdsfdsfd sfsd daqwrewfd
sdafakjhds dfdsds
dslixozjgkfsdcl'
'prog 1 2 3'
'prog 123'
'prog a b c'
'prog abc'
'r+b'
'r+t'
'r+w'
'r+wb'
'r+wt'
'rb'
'rstux'
'rt'
'rtxuv'
'rw+b'
'rw+t'
'rwb'
'rwt'
'sduxjuckflj suxjucuxjucdfgklj
sdfli sdlfkuxjucj erlijs sdf
ikxjuxjuculsj uxjuc xdvflkj
wsefrlij wefsuxjucld dfg'
'sfgha'
'ssdfd'

182

Parte IV Anexos

RESOLUES DOS EXERCCIOS


Exerccios resolvidos, para comparao aps resolver. Se no conseguir mesmo resolver um
exerccio com as dicas disponveis, pea a algum que o ajude, e que em ltimo caso veja o cdigo
por si para lhe dar uma dica extra, mas no veja um exerccio sem o resolver. O objectivo dos
exerccios coloc-lo perante a realizao de actividades reais, para os quais o texto e exemplos
fornecidos nos captulos o ajudam a enfrentar. No entanto, apenas a realizao de exerccios lhe
poder dar a experincia de programao que apenas sua e intransmissvel. Se no consegue
resolver um exerccio, deve ver essa situao como uma oportunidade de evoluo e procurar
eliminar a raiz do problema, e no desperdi-la vendo a resoluo do exerccio.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/* olamundosizeof.c */
int main()
{
/* texto com acentos: */
printf("\nOl Mundo!");
/* tamanho em bytes dos diversos tipos */
printf("\nsizeof(char): %d", sizeof(char));
printf("\nsizeof(short): %d", sizeof(short));
printf("\nsizeof(int): %d", sizeof(int));
printf("\nsizeof(long): %d", sizeof(long));
printf("\nsizeof(long long): %d", sizeof(long long));
printf("\nsizeof(float): %d", sizeof(float));
printf("\nsizeof(double): %d", sizeof(double));
printf("\nsizeof(long double): %d", sizeof(long double));

}
/* soma.c */
int main()
{
int n, i, soma;

printf("Calculo da soma dos primeiros N numeros.\nIndique N:");


/* ler um nmero inteiro */
scanf("%d", & n);
/* na varivel soma, ser acumulado o resultado */
soma = 0;
/* a varivel i vai iterar de 1 a N */
i = 1;
while(i <= n)
{
/* a varivel soma vai acumulando o resultado de 1 at i */
soma = soma + i;
/* mostrar o resultado parcial */
printf("\n adicionar %d, parcial %d", i, soma);
/* incrementar a varivel i */
i = i + 1;
}
/* mostrar resultado final */
printf("\nTotal: %d\n", soma);
}
/* hms.c */
int main()
{
int horas, minutos, segundos;
printf("Calculo do numero de segundos desde o inicio do dia.\nHora: ");
scanf("%d", & horas);
printf("Minuto: ");
scanf("%d", & minutos);
printf("Segundos: ");
scanf("%d", & segundos);
printf("Numero de segundos desde o inicio do dia: %d",
segundos + 60 *(minutos + 60 *horas));
}
/* produto.c */
int main()
{
int n, i, produto;
printf("Calculo do produto dos primeiros N numeros.\nIndique N:");
scanf("%d", & n);
produto = 1;
i = 1;
while(i <= n)
{
/* utilizao do operador *= em vez da atribuio e multiplicao */
produto *= i;
/* resultado parcial */
printf(" Factorial(%d)=%d\n", i, produto);
/* utilizao do operador de incremento ++, em vez da atribuio

18. Exerccios: Dicas, Respostas e Resolues


18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

e soma com a unidade. */


i++;
}
printf("Resultado: %d", produto);
}
/* arranjos.c */
int main()
{
int i, n, r, arranjos;
printf("Calculo dos arranjos de N, R a R:\nIndique N:");
scanf("%d", & n);
printf("Indique R:");
scanf("%d", & r);
/* verificao da validade dos argumentos introduzidos */
if(n < r || r < 1)
printf("Erro: N tem de ser maior que R e este maior que 0.\n");
else
{
/* inicializao da varivel iteradora a expresso
indicada no enunciado */
i = n - r + 1;
arranjos = 1;
while(i <= n)
{
arranjos *= i;
/* parcial */
printf(" i=%d; arranjos=%d\n", i, arranjos);
i++;
}
printf("Resultado: %d\n", arranjos);
}
}
/* somadigitos.c */
int main()
{
/* varivel soma declarada e inicializada no mesmo comando */
int n, soma = 0;
printf("Calculo da soma do quadrado dos digitos de um numero:\nNumero: ");
scanf("%d", & n);
while(n != 0)
{
/* extrair o dgito mais baixo com o resto da diviso
por 10 e som-lo o seu quadrado */
soma += (n % 10) *(n % 10);
/* mostrar parcial */
printf(" n=%d; soma=%d\n", n, soma);

/* diviso inteira por 10, de forma a que o segundo


dgito mais baixo, passe a ser o mais baixo */
n /= 10;

printf("Resultado: %d\n", soma);


}
/* fibonacci.c */
int main()
{
/* necessrio duas variveis auxiliares para guardar os
dois ltimos valores */
int i, n, fib1, fib2, resultado;
printf("Calculo do valor da funcao Fibonacci:\nIndique N:");
scanf("%d", & n);
if(n < 1)
printf("Erro: n tem de ser maior ou igual a 1.\n");
else
{
/* caso n<=2, o resultado n */
resultado = n;
if(n > 2)
{
/* os dois primeiros valores de fib 1 e 2 */
fib1 = 1;
fib2 = 2;
/* comear no nmero 3, que o primeiro cujo
resultado a soma dos dois anteriores */
i = 3;
while(i <= n)
{
/* aps calcular o fib de i, actualizar as variveis
auxiliares com os dois valores anteriores, para o
passo seguinte, o fib de i+1 */
resultado = fib1 + fib2;
fib1 = fib2;
fib2 = resultado;
/* mostrar valor actual */
printf(" Fib(%d)=%d\n", i, resultado);
i++;

183

184
36
37
38
39
40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Parte IV Anexos
}
}
printf("Resultado: %d\n", resultado);

}
}
/* combinacoes.c */
int main()
{
int i, n, r, combinacoes;
printf("Calculo das combinacoes de N, R a R:\nIndique N:");
scanf("%d", & n);
printf("Indique R:");
scanf("%d", & r);
if(n < r || r < 1)
printf("Erro: N tem de ser maior que R e este maior que 0.\n");
else
{
/* no efectuar as multiplicaes e depois as divises,
c.c. rapidamente se atinge o limite do inteiro, tem de
se multiplicar e dividir ao mesmo tempo */
i = 1;
combinacoes = 1;
while(i <= r)
{
/* mostrar valor actual */
printf(" %d", combinacoes);
/* a iterao em R, o numerador tem de ser modificado
para ir de N-R+1 (quando i=1) at N (quando i=R) */
combinacoes *= (n - r + i);
/* mostrar valor aps produto */
printf("*%d=%d", n - r + i, combinacoes);
combinacoes /= i;
/* mostrar valor aps diviso */
printf("/%d=%d\n", i, combinacoes);
i++;
/* Nota: possvel a diviso inteira, dado que quando i=2 j
dois nmeros foram multiplicados, portanto um deles par,
quando i=3 j trs nmeros foram multiplicados, portanto um
deles multiplo de 3, e assim sucessivamente */
}
printf("Resultado: %d\n", combinacoes);
}

}
/* euler.c */
#define ITERACOES 20

int main()
{
int i = 0;
/* atendendo a que se pretende alguma preciso, utilizar reais
de preciso dupla: double
ao criar uma varivel para o factorial do passo i,
evita-se ter de calcular de i em cada passo, basta multiplicar
a varivel factorial por i */
double resultado = 0, factorial = 1;
while(i <= ITERACOES)
{
/* graas varivel factorial, o nmero de operaes a realizar
apenas um produto, uma diviso e uma soma por cada iterao */
if(i > 1)
factorial *= i;
resultado += 1 / factorial;
/* mostrar com preciso 16 em notao cientfica: %.16g */
printf(" %d: %.5g\n", i, resultado);
i++;
}
printf("Resultado: %.16g", resultado);
}
/* trocos.c */
int main()
{
float montante;
int count;
printf("Introduza um montante em euros, podendo ter centimos: ");
/* receber um nmero real do utilizador, com %f */
scanf("%f",&montante);
/* arrendondar para o cntimo mais prximo */
montante+=0.005;
/* contar o nmero de vezes que cabe a moeda maior no remanescente */
count=(int)(montante/2.);
montante-=2*count;
if(count>0)
printf("2 euros: %d\n",count);
/* efectuar com o montante restante o mesmo, para as restantes moedas */
count=(int)(montante);
montante-=count;
if(count>0)
printf("1 euro: %d\n",count);
count=(int)(montante/.5);
montante-=0.5*count;

18. Exerccios: Dicas, Respostas e Resolues


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3

if(count>0)
printf("50 centimos: %d\n",count);
count=(int)(montante/.2);
montante-=0.2*count;
if(count>0)
printf("20 centimos: %d\n",count);
count=(int)(montante/.1);
montante-=0.1*count;
if(count>0)
printf("10 centimos: %d\n",count);
count=(int)(montante/.05);
montante-=0.05*count;
if(count>0)
printf("5 centimos: %d\n",count);
count=(int)(montante/.02);
montante-=0.02*count;
if(count>0)
printf("2 centimos: %d\n",count);
count=(int)(montante/.01);
montante-=0.01*count;
if(count>0)
printf("1 centimo: %d\n",count);
/* o cdigo acima algo repetido, resultando desnecessariamente
em cdigo muito extenso para a funo implementada. No mdulo
seguinte este exerccio ser pedido novamente, de forma a que
possa ser resolvido sem repeties, fazendo uso das as novas
ferramentas leccionadas. */

/* primo.c */
int main()
{
int divisor, n;
printf("Funcao que verifica se um numero N e' primo:\nIndique N:");
scanf("%d", & n);
if(n < 1)
printf("Erro: o numero tem de ser maior que zero.\n");
else
{
/* a varivel divisor vai iterar at que o seu quadrado seja
maior que o nmero (evitando a utilizao da raiz quadrada) */
divisor = 2;
while(divisor *divisor < n)
{
/* o nmero divisivel se o resto da diviso for nula */
if(n % divisor == 0)
{
printf("\nNumero divisivel por %d\n", divisor);
return;
}
/* mostrar iterao */
printf("%d ", divisor);
divisor++;
}
printf("\nNumero primo!\n");
}

}
/* triplasoma.c */
int main()
{
int i, j, n, count;

printf("Escreva um numero para decompor em somas de tres parcelas.\nNumero:");


scanf("%d", & n);
count = 0;
/* percorrer todas as possveis permutaes (apenas dois ciclos aninhados),
assumindo que os nmeros mais altos aparecem sempre primeiro */
i = n - 2;
while((n - i) <= i *2)
{
/* este condicional garante que h parcelas para alm desta,
mais pequenas ou iguais, pelo menos duas iguais a i */
j = (n - i) - 1;
if(j > i)
j = i;
while((n - i - j) <= j)
{
/* encontrado i+j+(n-i-j), imprimir */
printf(" %d+%d+%d\n", i, j, n - i - j);
count++;
j--;
}
i--;
}
printf("Numero de somas: %d\n", count);
}
/* pi.c */
#define ITERACOES 3

185

186
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

Parte IV Anexos
int main()
{
int i = 0, j;
double resultado, factor = 0, parcela;
resultado = 2. *sqrt(2.) / 9801.;
while(i < ITERACOES)
{
parcela = 1103 + 26390 *i;
/* (4k)! */
j = 4 *i;
while(j > 0)
{
parcela *= j;
j--;
}
/* /(k!)^4 */
j = i;
while(j > 0)
{
parcela /= j *j *j *j;
j--;
}
/* /(396)^(4k) */
j = 4 *i;
while(j > 0)
{
parcela /= 396;
j--;
}
i++;
factor += parcela;
}
resultado *= factor;
resultado = 1 / resultado;
printf("\nValor de PI (%d iteracoes): %.17g\n", ITERACOES, resultado);
}
/* formularesolvente.c */
/* listar os coeficientes de funes inteiros, entre -4 e 4,
que tm apenas raizes inteiras */
int main()
{
/* variveis so agora reais, do tipo float */
float a, b, c, delta;
/* verso inteira para resposta pergunta */
int ia, ib, ic, idelta, isqrt, k, count;
printf("Equacao do segundo grau a*x^2+b*x+c=0.\nIndique a b c: ");
/* leitura dos trs argumentos em conjunto */
scanf("%f %f %f", & a, & b, & c);
/* calcular primeiramente o interior da raiz quadrada, dependente
do sinal deste valor, haver nmero de razes distintas */
delta = b *b - 4 *a *c;
/* mostrar parcial */
printf("Delta: %f\n", delta);
if(delta < 0)
printf("A equacao nao tem raizes reais.\n");
else if(delta == 0)
printf("A equacao tem uma raiz unica, x=%f\n", - b / (2 *a));
else
/* pode-se colocar logo o resto da expresso nos argumentos
do printf */
printf("O valor de x pode ser %f ou %f\n",
(- b + sqrt(delta)) / (2 *a),(- b - sqrt(delta)) / (2 *a));
/* resposta pergunta */
printf("\nCalculo de coeficientes entre -K e K inteiros nao nulos, com raizes inteiras.");
printf("\nIntroduza K:");
scanf("%d", & k);
printf("\nCoeficientes de -%d a %d inteiros nao nulos, com raizes inteiras:\n",
k, k);
count = 0;
for(ia =- k; ia <= k; ia++)
for(ib =- k; ib <= k; ib++)
for(ic =- k; ic <= k; ic++)
{
if(ia == 0 || ib == 0 || ic == 0)
continue;
idelta = ib *ib - 4 *ia *ic;
if(idelta == 0 && ia != 0 && ib % (2 *ia) == 0)
{
count++;
printf(" [%d %d %d]", ia, ib, ic);
}
else if(idelta > 0)
{
/* verificar se um quadrado perfeito */
isqrt = sqrt(idelta);
if(isqrt *isqrt == idelta && ia != 0 &&

18. Exerccios: Dicas, Respostas e Resolues


55
56
57
58
59
60
61
62
63
64
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1
2
3
4

((- ib + isqrt) % (2 *ia) == 0 &&


(- ib - isqrt) % (2 *ia) == 0))
{

count++;
printf(" (%d %d %d)", ia, ib, ic);

}
}
}
printf("\nTotal: %d", count);

}
/* argumentos.c */
int main(int argc, char **argv)
{
/* argumentos da funo main: argc - nmero de argumentos;
argv - vector de strings com os argumentos */
int i;
for(i = 0; i < argc; i++)
/* acesso string na posio i do vector argv: argv[i] */
printf("Argumento %d: %s\n", i, argv[i]);
}
/* inverte.c */
/* definir o tamanho mximo de uma string em macros, para assim ser
fcil de alterar este valor sem ter de ver todo o cdigo */
#define MAXSTR 255
/* funes sobre strings, mantendo a norma utilizada para os nomes
em string.h o nome da funo fica com trs letras str, seguido de
trs letras indicando a operao, neste caso a inverso: strinv */
void strinv(char *str)
{
int len, i;
char aux;
len = strlen(str);
/* necessrio percorrer apenas metade do tamanho da string */
for(i = 0; i < len / 2; i++)
{
/* trocar este caracter pelo seu simtrico */
aux = str[i];
str[i] = str[len - 1 - i];
str[len - 1 - i] = aux;
/* mostrar a iterao */
printf(" %d: %s\n", i, str);
}
}
int main()
{
char str[MAXSTR];
printf("Inversao de um texto.\nTexto: ");
/* ler a string atravs da funo gets */
gets(str);
/* inverter a string */
strinv(str);
/* aps a execuo da funo anterior, a string ficou invertida */
printf("Texto invertido: %s", str);
}
/* rand.c */
/* as constantes utilizadas, podem ser definidas como macros,
de forma a aparecerem no incio do programa. */
#define RAND_A 231533
#define RAND_B 82571
#define RAND_M 428573
/* varivel global, com a semente/valor da ltima gerao */
unsigned int seed = 0;
/* definio de uma funo rand. J existe uma funo rand
nas bibliotecas, que pode ser utilizada nos exerccios seguintes. */
unsigned int rand()
{
seed = (RAND_A *seed + RAND_B) % RAND_M;
return seed;
}
int main()
{
int n, i;
printf("Gerador de numeros aleatorios inteiros.\nValor maximo: ");
scanf("%d", & n);
n++;
/* inicializar a semente com o tempo */
seed = time(NULL);
/*seed=1;*/
printf("seed=%d: ", seed);
for(i = 0; i < 10; i++)
printf("%d ", rand() % n);
printf("\n");
}
/* find.c */
/* na declarao do vector v, no se deve colocar o nmero de
elementos, para funcionar para qualquer dimenso */
int Find(int v[], int n, int elemento)

187

188
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1
2
3
4
5
6
7
8
9
10
11
12

Parte IV Anexos
{
int i;
for(i = 0; i < n; i++)
if(v[i] == elemento)
/* caso se encontre o valor correcto, retornar da
funo mesmo com o ciclo a meio */
return i;
/* percorreu-se todos os elementos e no se encontrou
nenhum igual, retornar -1 */
return - 1;
}
/* funo que mostra no ecran os primeiros elementos de um vector */
void PrintInts(int v[], int n, char *nome)
{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)
printf("%d ", v[i]);
}
int main()
{
/* declarao do vector v com 1000 elementos (de 0 a 999) */
int v[1000], i;
/* fixar a semente a 1 */
srand(1);
/* inicializar o vector, com valores aleatrios de 0 a 999 */
for(i = 0; i < 1000; i++)
/* acesso posio i do vector v: v[i] */
v[i] = rand() % 1000;
/* mostrar os 10 primeiros elementos */
PrintInts(v, 10, "Vector: ");
/* chamada da funo find no prprio argumento do printf */
printf("\nPosicao de 2: %d", Find(v, 1000, 2));
}
/* maximo.c */
float Maximo(float v[], int n)
{
int i;
float resultado = 0;
for(i = 0; i < n; i++)
/* actualizar o resultado se for o primeiro elemento,
ou o valor registado for inferior ao elemento actual */
if(i == 0 || v[i] > resultado)
{
resultado = v[i];
/* mostrar iterao */
printf("\n %d: %.2f", i, resultado);
}
return resultado;
}
void PrintFloats(float v[], int n, char *nome)
{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)
printf("%.2f ", v[i]);
}
int main()
{
int i;
float v[1000];
srand(1);
for(i = 0; i < 1000; i++)
v[i] =- log((1 + rand() % 10000) / 10000.);
PrintFloats(v, 10, "Vector: ");
printf("\nValor maximo: %.6g", Maximo(v, 1000));
}
/* mdc.c */
int mdc(int x, int y)
{
/* caso y=0, retornar x */
if(y == 0)
return x;
/* c.c. retornar mdc(y,mod(x,y)), exactamente o comando seguinte
aps substituir o mod pelo comando do resto da diviso % */
return mdc(y, x % y);
}
int main()

18. Exerccios: Dicas, Respostas e Resolues


13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

{
int x, y, i, j, soma;
printf("Calculo do maximo divisor comum entre dois numeros.\nIndique x e y:");
scanf("%d %d", & x, & y);
printf("MDC: %d\n", mdc(x, y));
/* calcular o valor da resposta pedido no exerccio */
soma = 0;
for(i = 1; i < 100; i++)
for(j = i + 1; j <= 100; j++)
soma += mdc(i, j);
printf("A soma dos MDC dos pares de numeros de 1 a 100: %d\n", soma);

}
/* sort.c */
void Sort(int v[], int n)
{
/* duas variveis iteradoras, j que se tem de percorrer todos
os pares */
int i, j, aux;
for(i = 0; i < n; i++)
/* como a ordem indiferente, a segunda varivel comea
logo com o valor acima da primeira, desta forma apenas
analisado o par i=10, j=35, e nunca o par i=35, j=10, dado
que quando i=35, o j comea logo em 36 */
for(j = i + 1; j < n; j++)
if(v[i] > v[j])
{
/* pares pela ordem incorrecta, trocar recorrendo
a uma varivel auxiliar */
aux = v[i];
v[i] = v[j];
v[j] = aux;
}
}
void PrintInts(int v[], int n, char *nome)
{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)
printf("%d ", v[i]);
}
int main()
{
int v[1000], i;
srand(1);
for(i = 0; i < 1000; i++)
v[i] = rand() % 1000;
PrintInts(v, 10, "\nVector antes de ser ordenado: ");
Sort(v, 1000);
PrintInts(v, 10, "\nVector depois de ser ordenado: ");

printf("\nQuartil 25: %d\nQuartil 50: %d\nQuartil 75: %d\n",


v[250], v[500], v[750]);
}
/* removedups.c */
int RemoveDups(int v[], int n)
{
int i, j;
/* como o vector est ordenado, os duplicados esto seguidos,
pelo que necessrio saber se o elemento seguinte igual */
for(i = 0; i < n - 1; i++)
/* enquanto o elemento seguinte for igual, apag-lo */
while(i < n - 1 && v[i] == v[i + 1])
{
/* para apagar o elemento actual, tem de se copiar
todos os elementos seguintes uma posio para trs. */
for(j = i; j < n - 1; j++)
v[j] = v[j + 1];
/* no esquecer de actualizar a dimenso do vector */
n--;
}
return n;
}
/* Nota: numa verso mais eficiente, mas requerendo matria do bloco
seguinte, poderia-se criar um vector novo e ir colocando os elementos
distintos nesse vector. Esse mtodo faz uma s passagem pelo vector,
independentemente do nmero de elementos duplicados, enquanto que a
funo acima percorre o resto do vector por cada elemento duplicado.
*/
void Sort(int v[], int n)
{
int i, j, aux;
for(i = 0; i < n; i++)
for(j = i + 1; j < n; j++)

189

190
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

Parte IV Anexos

if(v[i] > v[j])


{
aux = v[i];
v[i] = v[j];
v[j] = aux;
}

void PrintInts(int v[], int n, char *nome)


{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)
printf("%d ", v[i]);
}
int main()
{
int v[1000], i;
srand(1);
for(i = 0; i < 1000; i++)
v[i] = rand() % 1000;
PrintInts(v, 10, "\nVector inicial: ");
Sort(v, 1000);
printf("\nElementos que permanecem no vector: %d",
RemoveDups(v, 1000));
PrintInts(v, 10, "\nVector final: ");
}
/* somanumeros.c */
#define MAXSTR 255
int main()
{
char numeros[MAXSTR], *pt;
double soma = 0;
printf("Introduza um conjunto de numeros reais separados por espacos.\n");
gets(numeros);
/* pt fica a apontar para a primeira token (neste caso nmero real)*/
pt = (char *) strtok(numeros, " ");
while(pt != NULL)
{
/* converter a string para o seu valor numrico */
soma += atof(pt);
/* mostrar parcial */
printf(" %.2f (%s)\n", soma, pt);
/* passar para a prxima token */
pt = (char *) strtok(NULL, " ");
}
printf("A soma dos numeros e' %.15g.\n", soma);
}
/* trocos2.c */
int trocos(float montante, int mostrar)
{
int count, i, total;
/* vectores com informao esttica */
float moedas[] =
{
2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01, 0
};
char *smoedas[] =
{
"2 euros",
"1 euro",
"50 centimos",
"20 centimos",
"10 centimos",
"5 centimos",
"2 centimos",
"1 centimo"
};
total = 0;
montante += 0.005;
/* arrendondar para o cntimo mais prximo */
/* processar a informao esttica, em vez de duplicar o cdigo
utilizando constantes para cada possvel valor do vector. */
for(i = 0; moedas[i] > 0; i++)
{
count = (int)(montante / moedas[i]);
montante -= moedas[i] *count;
if(count > 0 && mostrar)
printf("%s: %d\n", smoedas[i], count);
total += count;
}
return total;

18. Exerccios: Dicas, Respostas e Resolues


35
36
37
38
39
40
41
42
43
44
45
46
47
48
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

}
int main()
{
float montante;
int total = 0;
printf("Introduza um montante em euros, podendo ter centimos: ");
scanf("%f", & montante);
trocos(montante, 1);
/* resposta pergunta */
for(montante = 0.01; montante < 20; montante = montante *2 + 0.01)
total += trocos(montante, 0);
printf("\nTotal moedas: %d", total);
}
/* jogodados.c */
#define ITERACOES 100000
#define MAXLANCAMENTOS 10
/* simulao de um jogo, dado um nmero fixo de lanamentos */
int JogoDados(int lancamentos, int mostraDado)
{
int dado,i,anterior=-1,pontos=0;
/* executar o nmero de lanamentos pedido, assumindo que o
dado anterior -1, de forma a no dar derrota no primeiro
lanamento, nem ser necessrio fazer um teste especial. */
for(i=0;i<lancamentos;i++) {
dado=1+rand()%6;
/* mostrar o dado */
if(mostraDado)
printf("%d ",dado);
pontos+=dado;
if(dado==anterior)
/* caso de derrota retorna logo, com a pontuao negativa */
return -pontos;
/* actualizar a varivel anterior, com o valor correcto
para o prximo ciclo */
anterior=dado;
}
/* foi possvel efectuar todos os lanamentos, retornar os
pontos positivos. */
return pontos;
}
int main()
{
int n,i,j,pontos;
srand(time(NULL));
printf("Jogo do lancamento de dados.\nIndique numero de lancamentos: ");
scanf("%d",&n);
printf("Pontos: %d\n\nValores medios dos pontos:\n",JogoDados(n,1));
/* simulao de vros jogos */
for(i=1;i<MAXLANCAMENTOS;i++) {
/* contabilizar os pontos ao longo de todos os jogos*/
pontos=0;
for(j=0;j<ITERACOES;j++)
pontos+=JogoDados(i,0);
/* fornecer a pontuao mdia */
printf("Com %d lancamento(s): %.2f\n",
i,(float)pontos/ITERACOES);
}
}
/* baralhar.c */
void Baralhar(int v[], int n)
{
int i, j, aux;
/* processar todos os elementos */
for(i = 0; i < n - 1; i++)
{
/* gerar um valor aleatrio para sortear o elemento do
vector a ficar na posio i (entre i e n-1). */
j = i + rand() % (n - i);
aux = v[i];
v[i] = v[j];
v[j] = aux;
}
}
void PrintInts(int v[], int n, char *nome)
{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)
printf("%d ", v[i]);
}
int main()
{
int v[1000], i;
srand(1);
/* desta vez inicializar o vector com a funo identidade */
for(i = 0; i < 1000; i++)
v[i] = i;

191

192
32
33
34
35
36
37
38
39
40
41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
1
2
3
4
5
6
7
8
9
10
11
12
13

Parte IV Anexos

PrintInts(v, 10, "Vector identidade: ");


Baralhar(v, 1000);
PrintInts(v, 10, "\nVector baralhado: ");
printf("\nNa posicao 250, 500, 750: %d %d %d",
v[250], v[500], v[750]);

}
/* mergersort.c */
#define MAXVECTOR 1000000

/* funo auxiliar para juntar duas partes do vector ordenadas */


void MergeSort2(int v[], int a, int meio, int b)
{
/* juntar ambas as metades no vector auxiliar */
static int aux[MAXVECTOR];
int i, j, k;
/* i primeira parte do vector,
j segunda parte,
k vector ordenado */
i = a;
j = meio;
k = a;
/* k avana de a a b */
while(k < b)
/* vector auxiliar fica com o menor dos valores, a no
ser que uma das partes j tenha acabado, e ao mesmo tempo
incrementa as respectivas variveis iteradoras */
if(i < meio && (j >= b || v[i] <= v[j]))
aux[k++] = v[i++];
else
aux[k++] = v[j++];
/* copiar o vector auxiliar para o vector em ordenao */
for(k = a; k < b; k++)
v[k] = aux[k];
/* mostrar indicao que j est ordenado o vector de a a b */
if(b - a > 100000)
printf(" Ordenado de %d a %d\n", a, b - 1);
}
/* funo auxiliar para ordenar de a at b */
void MergeSort1(int v[], int a, int b)
{
if(b - a < 2)
/* um ou zero elementos para ordenar, retornar */
return;
/* dividir em dois grupos, ordenar separadamente */
MergeSort1(v, a,(a + b) / 2);
MergeSort1(v,(a + b) / 2, b);
/* juntar os dois grupos ordenados */
MergeSort2(v, a,(a + b) / 2, b);
}
void MergeSort(int v[], int n)
{
MergeSort1(v, 0, n);
}
int main()
{
int i;
/* keyword static fora a que esta varivel seja na realidade global,
e mantem os valores entre chamadas da funo. Neste caso a funo
main, que no ser chamada segunda vez, mas a keyword static permite
que o compilador utilize outra zona de memria que no o stack. */
static int v[MAXVECTOR];
srand(1);
for(i = 0; i < MAXVECTOR; i++)
v[i] = rand() % 1000;
MergeSort(v, MAXVECTOR);
printf("Quantis: %d %d %d\n", v[250000], v[500000], v[750000]);
}
/* binarysearch.c */
/* funo auxiliar, procura num intervalo de a a b (inclusiv) */
int BinarySearch1(int v[], int a, int b, int elemento)
{
/* mostrar informao */
printf(" Procurar %d entre %d e %d\n", elemento, a, b);
/* caso a seja j maior que b, retornar fracasso */
if(a > b)
return - 1;
/* testar o elemento do meio, e se for igual, retornar esta posio */
if(v[(a + b) / 2] == elemento)
return(a + b) / 2;

18. Exerccios: Dicas, Respostas e Resolues


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

/* caso seja menor, chamar recursivamente esta funo para o segmento


da esquerda, c.c. chamar para o segmento da direita. */
if(v[(a + b) / 2] < elemento)
return BinarySearch1(v,(a + b) / 2 + 1, b, elemento);
/* notar que no h necessidade de else nos ifs, dado que se est
a retornar logo da funo dentro dos ifs*/
return BinarySearch1(v, a,(a + b) / 2 - 1, elemento);
}
int BinarySearch(int v[], int n, int elemento)
{
return BinarySearch1(v, 0, n - 1, elemento);
}
#define MAXVECTOR 1000000
void MergeSort2(int v[], int a, int meio, int b)
{
static int aux[MAXVECTOR];
int i, j, k;
i = a;
j = meio;
k = a;
while(k < b)
if(i < meio && (j >= b || v[i] <= v[j]))
aux[k++] = v[i++];
else
aux[k++] = v[j++];
for(k = a; k < b; k++)
v[k] = aux[k];
}
void MergeSort1(int v[],
{
if(b - a < 2)
return;
MergeSort1(v, a,(a +
MergeSort1(v,(a + b)
MergeSort2(v, a,(a +
}

int a, int b)

b) / 2);
/ 2, b);
b) / 2, b);

void MergeSort(int v[], int n)


{
MergeSort1(v, 0, n);
}
int main()
{
int i;
static int v[MAXVECTOR];
srand(1);
for(i = 0; i < MAXVECTOR; i++)
v[i] = rand() % 1000;
MergeSort(v, MAXVECTOR);
printf("Posicao
BinarySearch(v,
BinarySearch(v,
BinarySearch(v,

de 250, 500 e 750: %d %d %d\n",


MAXVECTOR, 250),
MAXVECTOR, 500),
MAXVECTOR, 750));

}
/* numeracaoromana.c */
#define MAXROMANA 24
/* Este exerccio tem tambm a questo da repetio de cdigo do exerccio
dos trocos */
void ArabeParaRomana(int arabe, char romana[], int mostrar)
{
static int valor[] =
{
1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1, 0
};
static char *texto[] =
{
"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I", ""
};
int i;
strcpy(romana, "");
/* processar todos os valores, do maior para o menor, e ir
concatenando as respectivas strings, idntico ao exerccio dos trocos */
for(i = 0; valor[i] > 0; i++)
while(arabe >= valor[i])
{
strcat(romana, texto[i]);
arabe -= valor[i];
/* mostrar esta operao */
if(mostrar)
printf(" %d %s\n", arabe, romana);
}
}
int RomanaParaArabe(char romana[], int mostrar)
{

193

194
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Parte IV Anexos
/* estas variveis estticas esto declarads em duas funes,
poderiam ser globais, mas como so de interesse apenas para duas
funes optou-se pela sua duplicao localmente. */
static int valor[] =
{
1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1, 0
};
static char *texto[] =
{
"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I", ""
};
int arabe = 0, i;
char *pt;
pt = romana;
while(pt[0] != 0)
for(i = 0; valor[i] > 0; i++)
/* tentar encontrar na string as letras romanas (1 e 2 caracteres) */
if(texto[i][1] == 0 && pt[0] == texto[i][0])
{
/* 1 caracter, match perfeito */
pt++;
arabe += valor[i];
/* mostrar esta operao */
if(mostrar)
printf(" %d %s\n", arabe, pt);
/* o valor rabe actualizado, avanar para o prximo
caracter e voltar a procurar por match com as letras romanas*/
break;
}
else if(texto[i][1] != 0 &&
pt[0] == texto[i][0] &&
pt[1] == texto[i][1])
{
/* 2 caracteres, match perfeito */
pt += 2;
arabe += valor[i];
/* mostrar esta operao */
if(mostrar)
printf(" %d %s\n", arabe, pt);
/* idntico, mas avana dois caracteres */
break;
}
return arabe;
}
int main()
{
int numero, i, soma;
char romana[MAXROMANA];
printf("Conversao de numeracao arabe para romana e vice-versa.\n");
printf("Numero arabe: ");
gets(romana);
numero = atoi(romana);
ArabeParaRomana(numero, romana, 1);
printf("Resultado: %s\nNumero romano: ", romana);
gets(romana);
printf("Resultado: %d\n", RomanaParaArabe(romana, 1));
/* resposta pergunta */
soma = 0;
for(i = 1; i < 1000; i++)
{
ArabeParaRomana(i, romana, 0);
soma += strlen(romana);
}
printf("Caracteres dos numeros de 1 a 1000, em numeracao romana: %d",
soma);
}
/* pokerdados.c */
void PrintInts(int v[], int n, char *nome)
{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)
printf("%d ", v[i]);
}
char *poker5dados(int dados[5], int mostra)
{
int count[6], i, j, max;
/* contar quantos dados existem de cada tipo */
max = 0;
for(i = 0; i < 6; i++)
count[i] = 0;
for(i = 0; i < 5; i++)
{
count[dados[i] - 1]++;
if(max < count[dados[i] - 1])
max = count[dados[i] - 1];
}
/* mostra contagem */
if(mostra)
PrintInts(count, 6, "\n Frequencia dos valores: ");

18. Exerccios: Dicas, Respostas e Resolues


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

/* verificar primeiro as situaes de maior valor */


if(max == 5)
return "poker real";
if(max == 4)
return "poker quadruplo";
if(max == 3)
{
/* se existir um nmero com contagem 2, ento temos um fullen */
for(i = 0; i < 6; i++)
if(count[i] == 2)
return "fullen";
}
if(max == 1)
{
/* verificar a sequncia */
if(count[0] == 0 || count[5] == 0)
return "sequencia";
}
if(max == 3)
/* agora a nica hiptese o trio */
return "trio";
if(max == 2)
{
/* se existir dois nmeros com contagem 2, temos dois pares */
for(i = 0, j = 0; i < 6; i++)
if(count[i] == 2)
j++;
if(j == 2)
return "duplo par";
else
return "par";
}
return "nada";
}
int main()
{
int dados[5], i, j, nada, par, duplopar;
char reter[80], *resposta;
/* simulao de um jogo, lanando 5 dados */
srand(time(NULL));
for(i = 0; i < 5; i++)
dados[i] = 1 + rand() % 6;
PrintInts(dados, 5, "Valor dos dados lancados: ");
printf("(%s)\nReter (10100 reter o primeiro e o dado central): ",
poker5dados(dados, 1));
gets(reter);
/* lanar os dados no retidos, e apresentar resultado final */
for(i = 0; i < 5 && reter[i]; i++)
if(reter[i] != '1')
dados[i] = 1 + rand() % 6;
PrintInts(dados, 5, "Valor dos dados finais: ");
printf("(%s)\n", poker5dados(dados, 1));
/* cdigo para responder pergunta */
srand(1);
nada = par = duplopar = 0;
for(i = 0; i < 1000; i++)
{
for(j = 0; j < 5; j++)
dados[j] = 1 + rand() % 6;
resposta = poker5dados(dados, 0);
if(strcmp(resposta, "nada") == 0)
nada++;
else if(strcmp(resposta, "par") == 0)
par++;
else if(strcmp(resposta, "duplo par") == 0)
duplopar++;
}
printf("nada: %d\npar: %d\nduplo par: %d\n", nada, par, duplopar);
}
/* strreplace.c */
#define MAXSTR 1024
int strreplace(char *str, char *find, char *replace)
{
char *pt;
int szfind, szreplace, i, count = 0;
szfind = strlen(find);
if(szfind == 0)
return;
szreplace = strlen(replace);
pt = str;
while((pt = strstr(pt, find)) != NULL)
{
count++;
/* substituir find por replace */

195

196
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

Parte IV Anexos
if(szfind >= szreplace)
{
/* h espao */
for(i = 0; i < szreplace; i++)
pt[i] = replace[i];
if(szfind > szreplace)
{
/* reposicionar o resto da string */
for(i = szreplace; pt[i + szfind - szreplace]; i++)
pt[i] = pt[i + szfind - szreplace];
pt[i] = 0;
}
}
else
{
/* no h espao, reposicionar o resto da string */
for(i = strlen(pt); i > 0; i--)
pt[i - szfind + szreplace] = pt[i];
/* copiar o replace */
for(i = 0; i < szreplace; i++)
pt[i] = replace[i];
}
/* importante que pt avane o tamanho do replace, para
no efectuar substituies na prpria substituio */
pt += szreplace;
/* mostrar operao */
printf(" %s\n", str);
}

}
return count;

int main()
{
char str[MAXSTR], find[MAXSTR], replace[MAXSTR];
int count;
printf("Substituir num texto, as ocorrencias de uma string A \
por uma string B.\nTexto: ");
gets(str);
printf("String A: ");
gets(find);
printf("String B: ");
gets(replace);
count = strreplace(str, find, replace);
printf("Resultado (%d trocas): %s\n", count, str);
}
/* ndamas.c */
#define MAXDAMAS 16
int total;
void MostraDamas(int linhasDamas[MAXDAMAS], int n)
{
int i, j;
static int mostrar = 1;
/* contar as posies e mostrar apenas a primeira vez */
total++;
if(mostrar == 0)
return;
/* como a varivel mostrar esttica, da prxima vez
que esta funo for chamada ser 0, e esta parte do cdigo
no ser executada */
mostrar = 0;
/* barra horizontal superior */
printf("\n+");
for(i = 0; i < n; i++)
printf("---");
printf("+");
for(i = 0; i < n; i++)
{
/* barra vertical esquerda */
printf("\n|");
/* casas vazias esquerda da dama desta linha */
for(j = 0; j < linhasDamas[i]; j++)
printf(" . ");
/* dama nesta linha */
printf(" # ");
/* casas vazias direita da dama desta linha */
for(j = linhasDamas[i] + 1; j < n; j++)
printf(" . ");
/* barra vertical direita */
printf("|");
}
/*barra horizontal inferior */
printf("\n+");
for(i = 0; i < n; i++)
printf("---");
printf("+");
}
/* com base nas linhas das damas j colocadas, colocar uma dama em coluna */

18. Exerccios: Dicas, Respostas e Resolues


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

void ColocarDama(int linhasDamas[MAXDAMAS], int coluna, int n)


{
int i, j;
/* percorrer todas as linhas para tentar colocar a dama */
for(i = 0; i < n; i++)
{
for(j = 0; j < coluna; j++)
if(linhasDamas[j] == i ||
/* mesma linha */
linhasDamas[j] + coluna - j == i ||
/* mesma diagonal */
linhasDamas[j] + j - coluna == i)
break;
/* se no houve uma paragem porque esta linha i possvel */
if(j == coluna)
{
linhasDamas[coluna] = i;
/* mostrar as damas se esta a ltima coluna,
c.c. chamar recursivamente esta funo para a coluna seguinte */
if(coluna + 1 == n)
MostraDamas(linhasDamas, n);
else
ColocarDama(linhasDamas, coluna + 1, n);
}
/* notar que mesmo aps ter colocado uma dama na linha i,
o algoritmo continua a explorar o i+1, e prossegue at n-1,
explorando todas as hiptese possveis. */
}
}
int main()
{
int n;
int linhasDamas[MAXDAMAS];
printf("Conta quantas posicoes existem, num tabuleiro de NxN, ");
printf("para colocacao de N damas sem que estas se ataquem ");
printf("mutuamente.\nIndique N (maximo %d): ", MAXDAMAS);
scanf("%d", & n);
total = 0;
ColocarDama(linhasDamas, 0, n);
printf("\nTotal: %d\n", total);
}
/* doisdados.c */
#define ITERACOES 10000
#define MAXELEMENTOS 100
#define MAXVALORES 22
/* mostra um grfico com tantas colunas quantos os elementos,
e 16 linhas sendo os valores re-escalados. Retorna o nmero
de cardinais impressos (para responder pergunta) */
int GraficoDeBarras(int serie[], int n)
{
char grafico[MAXVALORES][MAXELEMENTOS];
int i, j, maximo, count = 0;
float factor;
if(n > MAXELEMENTOS)
return;
/* determinar valor mximo da srie */
for(i = 0; i < n; i++)
if(i == 0 || maximo < serie[i])
maximo = serie[i];
/* obter o factor para re-escalar os valores para 16 linhas */
factor = 15. / maximo;
/* desenhar os eixos do grfico */
sprintf(grafico[0], "");
sprintf(grafico[1], "
+");
/* barra horizontal */
for(i = 0; i < n; i++)
strcat(grafico[1], "-");
/* barra vertical */
for(i = 2; i < 18; i++)
sprintf(grafico[i], "% 5d|%*s ",(int)((i - 2) / factor + 0.5), n,
" ");
/* desenhar a srie para o array */
for(i = 0; i < n; i++)
for(j = 0; j < 16; j++)
if(serie[i] *factor >= j)
{
grafico[j + 2][i + 6] = '#';
count++;
}
/* imprimir todo o array (aqui considerado um vector de strings) */
for(i = 17; i >= 0; i--)
printf("\n%s", grafico[i]);
return count;
}
void PrintInts(int v[], int n, char *nome)
{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)

197

198
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

Parte IV Anexos
printf("%d ", v[i]);
}
int main()
{
int soma[11], i, count;
srand(1);
printf("Simulacao de %d lancamento de dois dados:\n", ITERACOES);
for(i = 0; i < 11; i++)
soma[i] = 0;
for(i = 0; i < ITERACOES; i++)
soma[(1 + rand() % 6) + (1 + rand() % 6) - 2]++;
count = GraficoDeBarras(soma, 11);
PrintInts(soma, 11, "
23456789DOD\n\nValores: ");
printf("\nNumero de # presentes no histograma: %d", count);
}
/* calendario.c */
/* teste de ano bissexto */
int bissexto(int ano)
{
if(ano % 400 == 0)
return 1;
if(ano % 100 == 0)
return 0;
if(ano % 4 == 0)
return 1;
return 0;
}
/* retorna o nmero de dias do ms, a contar com os anos bissextos */
int DiasDoMes(int ano, int mes)
{
static int diasDoMes[] =
{
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
if(mes == 2)
return diasDoMes[1] + bissexto(ano);
else if(mes >= 1 && mes <= 12)
return diasDoMes[mes - 1];
return 0;
}
/* retorna a diferena entre duas datas*/
int DiasDeDiferenca(int ano1, int mes1, int dia1, int ano2, int mes2, int
dia2)
{
int dias = 0, i;
/* somar os dias de diferena por anos */
for(i = ano1; i < ano2; i++)
dias += 365 + bissexto(i);
/* subtrair os dias desde o incio do ano at mes1/dia1 */
for(i = 0; i < mes1 - 1; i++)
dias -= DiasDoMes(ano1, i + 1);
dias -= dia1 - 1;
/* somar os dias desde o incio do ano at mes2/dia2 */
for(i = 0; i < mes2 - 1; i++)
dias += DiasDoMes(ano2, i + 1);
dias += dia2 - 1;
}

return dias;

/* retorna o dia da semana de uma data */


int DiaDaSemana(int ano, int mes, int dia)
{
/* ver os dias de diferena entre um dia conhecido, e fazer o
resto da diviso por 7 */
return(6 + DiasDeDiferenca(2000, 1, 1, ano, mes, dia)) % 7;
}
/* determinao do ms/dia da Pscoa */
void Pascoa(int ano, int *mes, int *dia)
{
/* cdigo de acordo com o algoritmo */
int a, b, c, d, e, f, g, h, i, k, l, M;
a = ano % 19;
b = ano / 100;
c = ano % 100;
d = b / 4;
e = b % 4;
f = (b + 8) / 25;
g = (b - f + 1) / 3;
h = (19 *a + b - d - g + 15) % 30;
i = c / 4;
k = c % 4;
l = (32 + 2 *e + 2 *i - h - k) % 7;
M = (a + 11 *h + 22 *l) / 451;
*mes = (h + l - 7 *M + 114) / 31;

18. Exerccios: Dicas, Respostas e Resolues


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

*dia = ((h + l - 7 *M + 114) % 31) + 1;


}
/* esta funo adiciona/subtrai dias apenas no mesmo ano */
void AdicionarDias(int *ano, int *mes, int *dia, int ndias)
{
/* se h mais dias a subtrair que o ms, ento subtrair
um ms inteiro */
while(ndias > 0 && ndias > DiasDoMes(*ano, *mes) -*dia)
{
ndias -= DiasDoMes(*ano, *mes);
(*mes)++;
}
/* situao inversa */
while(ndias < 0 &&- ndias >*dia - 1)
{
ndias += DiasDoMes(*ano, *mes - 1);
(*mes)--;
}
/* j h dias suficientes, subtrair apenas os dias*/
*dia += ndias;
}
/* coloca os dias de feriado para um dado ano/ms */
void Feriados(int ano, int mes, int dias[32])
{
int i, pmes, pdia, fmes, fdia;
static int deltaPascoa[] =
{
- 48, - 47, - 2, + 60
};
static int mesFeriadoFixo[] =
{
1, 4, 5, 6, 10, 12, 12
};
static int diaFeriadoFixo[] =
{
1, 25, 1, 10, 5, 1, 25
};
for(i = 0; i < 32; i++)
dias[i] = 0;
Pascoa(ano, & pmes, & pdia);
if(pmes == mes)
dias[pdia] = 1;
for(i = 0; i < 4; i++)
{
fmes = pmes;
fdia = pdia;
AdicionarDias(& ano, & fmes, & fdia, deltaPascoa[i]);
if(fmes == mes)
dias[fdia] = 1;
}
for(i = 0; i < 7; i++)
if(mesFeriadoFixo[i] == mes)
dias[diaFeriadoFixo[i]] = 1;
}
/* imprime um ms formatadamente */
void PrintMes(int ano, int mes)
{
static char *nomesDosMeses[] =
{
"Janeiro", "Fevereiro", "Marco", "Abril", "Maio", "Junho",
"Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
};
int diasDoMes, i = 1, j;
int dias[32];
Feriados(ano, mes, dias);
diasDoMes = DiasDoMes(ano, mes);
printf("\n%s de %d:", nomesDosMeses[mes - 1], ano);
printf("\n#######################");
printf("\n# D S T Q Q S S #");
printf("\n#######################");
/* processar todos os dias, de semana a semana */
while(diasDoMes > 0)
{
/* incio de uma nova semana */
printf("\n#");
j = DiaDaSemana(ano, mes, i);
/* na primeira semana pode no comear logo no domingo */
if(j > 0)
printf("%*s", 3 *j, " ");
do
{
if(dias[i] == 1)
printf(" F ");
else
printf("%2d ", i);

199

200
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

Parte IV Anexos
i++;
diasDoMes--;
j = DiaDaSemana(ano, mes, i);

}
while(j > 0 && diasDoMes > 0);
/* se a semana acabou e no foi ao sbado
(ltima semana), completar */
if(j > 0)
printf("%*s", 3 *(7 - j), " ");
printf("#");
}
printf("\n#######################");
}
int main()
{
int ano, mes, resultado, i;
int dias[32];
printf("Calendario mensal, com indicacao de feriados.\nIndique ano mes:");
scanf("%d %d", & ano, & mes);
PrintMes(ano, mes);
/* cdigo para resposta pergunta */
resultado = 0;
for(ano = 2000; ano < 2100; ano++)
{
/* feriados */
Feriados(ano, 2, dias);
for(i = 0; i < 32; i++)
resultado += dias[i];
/* dias do ms */
resultado += DiasDoMes(ano, 2);
/* dia da semana do dia 1 */
resultado += DiaDaSemana(ano, 2, 1);
}
printf("\nResposta: %d", resultado);
}
/* editdistance.c */
#define MAXSTR 256
void PrintInts(int v[], int n, char *nome)
{
int i;
printf("%s", nome);
for(i = 0; i < n; i++)
printf("%d ", v[i]);
}
void MostraMatriz(int[MAXSTR][MAXSTR], char *, char *, int, int);
void MostraOperacoes(int[MAXSTR][MAXSTR], char *, char *, int, int);
/* ver http://en.wikipedia.org/wiki/Levenshtein_distance */
int EditDistance(char *s1, char *s2)
{
int szs1, szs2, i, j, aux;
/* vector bidimensional, para suporte ao algoritmo fornecido */
int m[MAXSTR][MAXSTR];
/* calcular o tamanho das strings */
szs1 = strlen(s1);
szs2 = strlen(s2);
/* caso fora dos limites, retornar */
if(szs1 >= MAXSTR || szs2 >= MAXSTR)
return - 1;
/* inicializaes */
for(i = 0; i <= szs1; i++)
m[i][0] = i;
for(j = 0; j <= szs2; j++)
m[0][j] = j;
/* calcular a matriz por ordem, at szs1, szs2
isto pode ser feito uma vez que a expresso fornecida
calcula i,j com base em ndices inferiores, portanto
j calculados. */
for(i = 1; i <= szs1; i++)
for(j = 1; j <= szs2; j++)
if(s1[i - 1] == s2[j - 1])
m[i][j] = m[i - 1][j - 1];
else
{
aux = m[i - 1][j] + 1; // apagar
if(m[i][j - 1] + 1 < aux)
// inserir
aux = m[i][j - 1] + 1;
if(m[i - 1][j - 1] + 1 < aux)
// substituir
aux = m[i - 1][j - 1] + 1;
m[i][j] = aux;
}
MostraMatriz(m, s1, s2, szs1, szs2);
MostraOperacoes(m, s1, s2, szs1, szs2);
/* retornar a distncia de edio, na ltima posio do vector */
return m[szs1][szs2];

18. Exerccios: Dicas, Respostas e Resolues


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

}
void MostraMatriz(
int m[MAXSTR][MAXSTR],
char *s1,
char *s2,
int szs1,
int szs2)
{
int i;
char str[MAXSTR];

/* mostrar a matriz de base comparao */


printf(" Matriz de distancias:\n
");
for(i = 0; i < szs2; i++)
printf("%c ", s2[i]);
for(i = 0; i <= szs1; i++)
{
if(i > 0)
sprintf(str, "\n %c ", s1[i - 1]);
else
strcpy(str, "\n
");
PrintInts(m[i], szs2 + 1, str);
}

void MostraOperacoes(
int m[MAXSTR][MAXSTR],
char *s1,
char *s2,
int szs1,
int szs2)
{
int i, j, k, aux, apagar, substituir, inserir;
char str[MAXSTR];
printf("\n Operacoes:");
strcpy(str, s1);
apagar = substituir = inserir = 0;
for(i = szs1, j = szs2; i > 0 || j > 0;)
{
printf("\n %s ", str);
if(i > 0 && j > 0 && s1[i - 1] == s2[j - 1])
{
printf("(%c) %s", s1[i - 1], str);
i--;
j--;
}
else
{
if(i > 0)
aux = m[i - 1][j]; // apagar
else
aux = m[i][j];
if(j > 0 && m[i][j - 1] < aux)
// inserir
aux = m[i][j - 1];
if(j > 0 && i > 0 && m[i - 1][j - 1] < aux)
// substituir
aux = m[i - 1][j - 1];
if(i > 0 && aux == m[i - 1][j])
// apagar
{
i--;
k = i - 1;
while(str[++ k])
str[k] = str[k + 1];
printf("[-%c] %s", s1[i], str);
apagar++;
}
else if(j > 0 && aux == m[i][j - 1])
/* inserir */
{
j--;
k = i;
while(str[k++]);
while(k > i)
{
str[k] = str[k - 1];
k--;
}
str[i] = s2[j];
printf("[+%c] %s", s2[j], str);
inserir++;
}
else if(i > 0 && j > 0)
{
/* substituir */
str[-- i] = s2[-- j];
printf("[%c/%c] %s", s1[i], s2[j], str);
substituir++;
}

201

202
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

Parte IV Anexos
else
break;
}

}
printf("\nApagar: %d, Inserir: %d, Substituir: %d\n",
apagar,
inserir,
substituir);
}
int main()
{
char s1[MAXSTR], s2[MAXSTR];
printf("Calculo da distancia de edicao entre duas strings.\nIndique s1: ");
gets(s1);
printf("Indique s2: ");
gets(s2);
printf("Resultado: %d", EditDistance(s1, s2));
}
/* adiciona.c */
/* declarao da estrutura SLista, com typedef para Lista */
typedef struct SLista
{
/* variveis pertencentes estrutura */
int valor;
/* apontador para o elemento seguinte da lista,
ou NULL se no existirem mais elementos */
struct SLista *seg;
}
Lista;
/* funo adiciona um elemento ao topo da lista, retornando
o novo incio da lista */
Lista *Adiciona(Lista *lista, int valor)
{
/* alocar espao para um novo elemento */
Lista *elemento = (Lista *) malloc(sizeof(Lista));
if(elemento != NULL)
{
/* atribuir o valor do novo elemento */
elemento->valor = valor;
/* o elemento seguinte o actual primeiro elemento da lista */
elemento->seg = lista;
}
/* em caso de falha, retorna NULL, c.c. retorna o novo incio
da lista, que o elemento criado */
return elemento;
}
/* remover o primeiro elemento da lista, retornando para o segundo,
agora primeiro elemento da lista */
Lista *Remove(Lista *lista)
{
Lista *aux;
if(lista != NULL)
{
/* guarda o elemento a retornar */
aux = lista->seg;
/* liberta o espao alocado por este elemento */
free(lista);
/* retornar o segundo, agora primeiro elemento da lista */
return aux;
}
return NULL;
}
/* contar o nmero de elementos na lista */
int Elementos(Lista *lista)
{
int count = 0;
/* pode-se utilizar o prprio parmetro para iterar pela lista */
while(lista != NULL)
{
lista = lista->seg;
count++;
}
return count;
}
/* verso de PrintInts para listas */
void PrintListaInts(Lista *lista, int n, char *nome)
{
printf("%s", nome);
while(lista != NULL && n-->0)
{
printf("%d ", lista->valor);
lista = lista->seg;
}
}
int main()
{

18. Exerccios: Dicas, Respostas e Resolues


74
75
76
77
78
79
80
81
82
83
84
85
86
87
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

int i;
Lista *lista = NULL;
srand(1);
/* adicionar 1000 elementos lista */
for(i = 0; i < 1000; i++)
lista = Adiciona(lista, rand() % 1000);
/* retornar os valores pedidos */
printf("Numero de elementos: %d\n",
Elementos(lista));
PrintListaInts(lista, 10, "Elementos: ");
/* libertar toda a lista antes de sair */
while(lista != NULL)
lista = Remove(lista);
}
/* more.c */
#define MAXSTR 1024
int main(int argc, char **argv)
{
FILE *f;
char str[MAXSTR], *pt, c;
int count = 0;
/* verificar se foi dado um argumento */
if(argc <= 1)
{
printf("Utilizao: more <ficheiro>\n");
return;
}
/* abrir o ficheiro em modo de texto*/
f = fopen(argv[1], "rt");
if(f == NULL)
{
printf("Ficheiro %s no existe.\n", argv[1]);
return;
}
while(! feof(f))
{
count++;
/* contador da linha */
if(fgets(str, MAXSTR, f) != NULL)
{
/* caso a string tenha menos de 75 caracteres,
arrumar a questo */
if(strlen(str) <= 75)
printf("%4d:%s", count, str);
else
{
/* strings com mais de uma linha, utilizar um apontador */
pt = str;
while(strlen(pt) > 75)
{
/* finalizar a string para ter 75 caracteres */
c = pt[75];
pt[75] = 0;
/* na primeira linha, colocar o nmero da linha
do ficheiro */
if(pt == str)
printf("%4d:%s", count, pt);
else
printf("
%s", pt);
/* repr o caracter retirado ao cortar a string */
pt[75] = c;
/* avanar 75 caracteres */
pt += 75;
}
/* colocar o resto da string */
printf("
%s", pt);
}
}
}

}
/* insere.c */
typedef struct SLista
{
int valor;
struct SLista *seg;
}
Lista;

Lista *Adiciona(Lista *lista, int valor)


{
Lista *elemento = (Lista *) malloc(sizeof(Lista));
if(elemento != NULL)
{
elemento->valor = valor;
elemento->seg = lista;
}
return elemento;
}

203

204
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
1
2
3
4
5
6
7

Parte IV Anexos

Lista *Remove(Lista *lista)


{
Lista *aux;
if(lista != NULL)
{
aux = lista->seg;
free(lista);
return aux;
}
return NULL;
}
int Elementos(Lista *lista)
{
int count = 0;
while(lista != NULL)
{
lista = lista->seg;
count++;
}
return count;
}
Lista *Insere(Lista *lista, int valor)
{
/* caso a lista esteja vazia, ou
o primeiro elemento igual ou maior,
adicionar o elemento no incio */
if(lista == NULL || lista->valor >= valor)
return Adiciona(lista, valor);
/* caso a lista no seja vazia e o primeiro
elemento menor, ento inserir a partir
do segundo elemento */
lista->seg = Insere(lista->seg, valor);
/* tendo sido inserido o elemento a partir
do segundo elemento, o valor da lista
no se altera */
return lista;
}
Lista *Apaga(Lista *lista, int valor)
{
/* apagar todos os elementos iniciais iguais ao valor */
while(lista != NULL && lista->valor == valor)
lista = Remove(lista);
/* se a lista ficou vazia, retornar */
if(lista == NULL)
return NULL;
/* repetir para os elementos seguintes */
lista->seg = Apaga(lista->seg, valor);
}

return lista;

void PrintListaInts(Lista *lista, int n, char *nome)


{
printf("%s", nome);
while(lista != NULL && n-->0)
{
printf("%d ", lista->valor);
lista = lista->seg;
}
}
int main()
{
int i;
Lista *lista = NULL;
srand(1);
for(i = 0; i < 1000; i++)
lista = Insere(lista, rand() % 1000);
PrintListaInts(lista, 10, "Elementos apos inserir 1000 elementos: ");
for(i = 0; i < 1000; i += 2)
lista = Apaga(lista, i);
printf("\nNumero de elementos na lista apos apagar: %d\n",
Elementos(lista));
PrintListaInts(lista, 10, "Elementos apos apagar: ");
while(lista)
lista = Remove(lista);
}
/* insertsort.c */
typedef struct SLista
{
int valor;
struct SLista *seg;
}
Lista;

18. Exerccios: Dicas, Respostas e Resolues


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

void PrintListaInts(Lista *lista, int n, char *nome)


{
printf("%s", nome);
while(lista != NULL && n-->0)
{
printf("%d ", lista->valor);
lista = lista->seg;
}
}
Lista *Adiciona(Lista *lista, int valor)
{
Lista *elemento = (Lista *) malloc(sizeof(Lista));
if(elemento != NULL)
{
elemento->valor = valor;
elemento->seg = lista;
}
return elemento;
}
Lista *Remove(Lista *lista)
{
Lista *aux;
if(lista != NULL)
{
aux = lista->seg;
free(lista);
return aux;
}
return NULL;
}
Lista *Insere(Lista *lista, int valor)
{
if(lista == NULL || lista->valor >= valor)
return Adiciona(lista, valor);
lista->seg = Insere(lista->seg, valor);
return lista;
}
/* ordenar a lista inserindo os elementos de forma ordenada */
Lista *InsertSort(Lista *lista)
{
int count = 0;
/* criar uma lista vazia onde se vai colocando os elementos */
Lista *aux = NULL;
while(lista != NULL)
{
/* inserir o novo elemento */
aux = Insere(aux, lista->valor);
/* apagar o elemento da lista antiga, e avanar para o prximo */
lista = Remove(lista);
/* mostrar este passo durante as primeiras iteraes */
if(count++< 10)
PrintListaInts(aux, 10, "\n Lista: ");
}
/* retornar a nova lista, a antiga j no existe */
return aux;
}
int main()
{
int i;
Lista *lista = NULL;
srand(1);
for(i = 0; i < 1000; i++)
lista = Adiciona(lista, rand() % 1000);
PrintListaInts(lista, 10, "Lista nao ordenada: ");
lista = InsertSort(lista);
PrintListaInts(lista, 10, "\nLista ordenada: ");
while(lista != NULL)
lista = Remove(lista);
}
/* enesimo.c */
typedef struct SLista
{
int valor;
struct SLista *seg;
}
Lista;
Lista *Adiciona(Lista *lista, int valor)
{
Lista *elemento = (Lista *) malloc(sizeof(Lista));
if(elemento != NULL)
{
elemento->valor = valor;
elemento->seg = lista;
}

205

206
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Parte IV Anexos
return elemento;
}
Lista *Remove(Lista *lista)
{
Lista *aux;
if(lista != NULL)
{
aux = lista->seg;
free(lista);
return aux;
}
return NULL;
}
Lista *Insere(Lista *lista, int valor)
{
if(lista == NULL || lista->valor >= valor)
return Adiciona(lista, valor);
lista->seg = Insere(lista->seg, valor);
return lista;
}
Lista *InsertSort(Lista *lista)
{
Lista *aux = NULL;
while(lista != NULL)
{
aux = Insere(aux, lista->valor);
lista = Remove(lista);
}
return aux;
}
/* retorna o valor em uma determinada posio, ou 0 se no existirem
elementos suficientes */
int Valor(Lista *lista, int posicao)
{
/* avanar na lista at atingir a posio especificada */
while(posicao > 1 && lista != NULL)
{
posicao--;
lista = lista->seg;
}
/* se a lista n acabou antes de se obter a posio pretendida,
retornar o valor actual */
if(lista != NULL)
return lista->valor;
}

return 0;

int main()
{
int i;
Lista *lista = NULL;
srand(1);
for(i = 0; i < 1000; i++)
lista = Adiciona(lista, rand() % 1000);
lista = InsertSort(lista);
printf("elementos 250, 500 e 750: %d %d %d",
Valor(lista, 250),
Valor(lista, 500),
Valor(lista, 750));
while(lista != NULL)
lista = Remove(lista);
}
/* palavras.c */
#define MAXSTR 1024
/* funo que dada uma string retorna o nmero de palavras */
int Palavras(char *str)
{
char strBackup[MAXSTR];
char *pt = str;
int count = 0;
strcpy(strBackup, str);
pt = strtok(pt, " \t\n\r");
while(pt != NULL)
{
count++;
/* uma palavra s com letras partida */
while(pt[0] != 0)
if(! isalpha(pt[0]))
{
/* afinal a palavra no tem apenas letras */
count--;
break;
}
else pt++;
pt = strtok(NULL, " \t\n\r");

18. Exerccios: Dicas, Respostas e Resolues


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
1
2

}
/* mostrar resultado se existirem palavras */
if(count > 0)
printf(" [%d] %s", count, strBackup);
return count;
}
int main(int argc, char **argv)
{
FILE *f;
char str[MAXSTR], *pt, c;
int count = 0;
setlocale(LC_ALL, "");
/* verificar se foi dado um argumento */
if(argc <= 1)
{
printf("Utilizao: palavras <ficheiro>\n");
return;
}
/* abrir o ficheiro em modo de texto*/
f = fopen(argv[1], "rt");
if(f == NULL)
{
printf("Ficheiro %s no existe.\n", argv[1]);
return;
}
while(! feof(f))
if(fgets(str, MAXSTR, f) != NULL)
count += Palavras(str);
printf("\nO ficheiro %s contem %d palavras.\n", argv[1], count);
}
/* cifracesar.c */
#define MAXSTR 255
void CifraDeCesar(char *str, int chave)
{
int i, chaved;
chaved = chave;
/* chave negativa, somar a diferena at ser positiva */
while(chave < 0)
chave += 'z' - 'a' + 1;
while(chaved < 0)
chaved += '9' - '0' + 1;
/* processar todos os caracteres */
for(i = 0; str[i] != 0; i++)
if(str[i] >= 'a' && str[i] <= 'z')
/* novo valor ser: a + (k-a + chave mod z-a+1) */
str[i] = 'a' + (str[i] - 'a' + chave) % ('z' - 'a' + 1);
else if(str[i] >= 'A' && str[i] <= 'Z')
str[i] = 'A' + (str[i] - 'A' + chave) % ('Z' - 'A' + 1);
else if(str[i] >= '0' && str[i] <= '9')
str[i] = '0' + (str[i] - '0' + chaved) % ('9' - '0' + 1);
}
int main(int argc, char **argv)
{
FILE *f;
int chave;
char str[MAXSTR];
if(argc != 3)
{
printf("Indicar o nome do ficheiro e chave.\n");
return;
}
chave = atoi(argv[2]);
f = fopen(argv[1], "rt");
if(f == NULL)
{
printf("No foi possivel abrir para leitura o ficheiro %s.\n",
argv[1]);
return;
}
while(fgets(str, MAXSTR, f) != NULL)
{
CifraDeCesar(str, chave);
printf("%s", str);
}
fclose(f);
}
/* more.c */
#define MAXSTR 1024

207

208
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

Parte IV Anexos

int main(int argc, char **argv)


{
FILE *f;
char *pt;
int i, j, tamanho;
/* verificar se foi dado um argumento */
if(argc <= 1)
{
printf("Utilizacao: more <ficheiro>\n");
return;
}
/* abrir o ficheiro em modo de texto*/
f = fopen(argv[1], "rb");
if(f == NULL)
{
printf("Ficheiro %s nao existe.\n", argv[1]);
return;
}
fseek(f, 0, SEEK_END);
tamanho = ftell(f);
pt = (char *) malloc(tamanho);
if(pt == NULL)
return;
fseek(f, 0, SEEK_SET);
fread(pt, tamanho, 1, f);
fclose(f);
/* processar todos os caracteres do ficheiro */
for(i = 0; i < tamanho; i += 16)
{
printf("\n%6d|", i);
/* colocar os caracteres em hexadecimal */
for(j = 0; j < 16 && i + j < tamanho; j++)
printf("%02x ",(unsigned char) pt[i + j]);
/* colocar espaos no final da linha */
while(j++< 16)
printf("
");
/* colocar os caracteres em texto, se possvel */
printf("|");
for(j = 0; j < 16 && i + j < tamanho; j++)
if(isprint(pt[i + j]))
printf("%c", pt[i + j]);
else
printf(" ");
}
free(pt);
}
/* basededados.c */
#define MAXSTR 255
/* nome do ficheiro da BD fixo, para no ter de o estar a introduzir
ao entrar na aplicao */
#define NOME_FICHEIRO "basededados.dat"
/* estrutura definda com os campos necessrios para cada registo */
typedef struct
{
int id;
char nome[MAXSTR];
char telefone[MAXSTR];
char cidade[MAXSTR];
char descricao[MAXSTR];
}
SContacto;
/* funo para gravar este novo registo num no ficheiro */
void Gravar(SContacto *registo)
{
FILE *f = fopen(NOME_FICHEIRO, "r+b");
/* se no abrir, tentar criar o ficheiro de raiz */
if(f == NULL)
f = fopen(NOME_FICHEIRO, "w+b");
if(f != NULL)
{
/* posicionar o ficheiro na posio do registo */
fseek(f,(long)(registo->id) *sizeof(SContacto), SEEK_SET);
/* gravar sobrepondo ou gravando um novo registo */
fwrite(registo, sizeof(SContacto), 1, f);
fclose(f);
}
else
printf("\nErro: nao foi possivel abrir o ficheiro.");
}
/* ler um registo do ficheiro */
void Ler(SContacto *registo)

18. Exerccios: Dicas, Respostas e Resolues


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

{
FILE *f = fopen(NOME_FICHEIRO, "rb");
if(f != NULL)
{
/* posiciona e l o registo com base no id */
fseek(f,(long)(registo->id) *sizeof(SContacto), SEEK_SET);
fread(registo, sizeof(SContacto), 1, f);
fclose(f);
}
else
printf("\nErro: nao foi possivel abrir o ficheiro.");
}
/* retorna o nmero de registos, obtido atravs do ficheiro */
int Registos()
{
int n;
FILE *f = fopen(NOME_FICHEIRO, "rb");
if(f != NULL)
{
fseek(f, 0, SEEK_END);
n = ftell(f);
fclose(f);
return n / sizeof(SContacto);
}
return 0;
}
/* pedir ao utilizador para introduzir os dados de um registo */
void Carregar(SContacto *registo)
{
printf("Nome: ");
gets(registo->nome);
printf("Telefone: ");
gets(registo->telefone);
printf("Cidade: ");
gets(registo->cidade);
printf("Descrio: ");
gets(registo->descricao);
}
int Menu()
{
char str[MAXSTR];
printf("\n#########################");
printf("\n# Menu:
#");
printf("\n#########################");
printf("\n# 1 | LISTAR registos
#");
printf("\n# 2 | ADICIONAR registo #");
printf("\n# 3 | VER registo
#");
printf("\n# 4 | EDITAR registo
#");
printf("\n#########################");
printf("\nOpo: ");
gets(str);
return atoi(str);
}
void ListarRegistos()
{
int i, total;
SContacto registo;
total = Registos();
printf("\n########");
printf("\n# Lista:", total);
printf("\n########");
for(i = 0; i < total; i++)
{
registo.id = i;
Ler(& registo);
printf("\n# %4d | %s", registo.id, registo.nome);
}
printf("\n########\n");
}
void VerRegisto()
{
char str[MAXSTR];
SContacto registo;
printf("ID: ");
gets(str);
registo.id = atoi(str);
Ler(& registo);
printf("\n#############");
printf("\n# Nome
| %s",
printf("\n# Telefone | %s",
printf("\n# Cidade
| %s",
printf("\n# Descrio | %s",
printf("\n#############\n");
}
int main()
{

registo.nome);
registo.telefone);
registo.cidade);
registo.descricao);

209

210
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

Parte IV Anexos
int opcao, i;
char str[MAXSTR];
SContacto registo;
while(1)
{
opcao = Menu();
if(opcao == 1)
{
ListarRegistos();
}
else if(opcao == 2)
{
/* adicionar registo */
registo.id = Registos();
Carregar(& registo);
Gravar(& registo);
}
else if(opcao == 3)
{
VerRegisto();
}
else if(opcao == 4)
{
/* editar registo */
printf("ID: ");
gets(str);
registo.id = atoi(str);
Carregar(& registo);
Gravar(& registo);
}
else
break;
}
}
/* mergesort.c */
typedef struct SLista
{
int valor;
struct SLista *seg;
}
Lista;
Lista *Adiciona(Lista *lista, int valor)
{
Lista *elemento = (Lista *) malloc(sizeof(Lista));
if(elemento != NULL)
{
elemento->valor = valor;
elemento->seg = lista;
}
return elemento;
}
Lista *Remove(Lista *lista)
{
Lista *aux;
if(lista != NULL)
{
aux = lista->seg;
free(lista);
return aux;
}
return NULL;
}
Lista *MergeSort(Lista *lista)
{
Lista *lista1 = NULL, *lista2 = NULL;
/* se a lista tiver apenas um elemento, retornar */
if(lista == NULL || lista->seg == NULL)
return lista;
/* dividir a lista a meio */
while(lista != NULL)
{
lista1 = Adiciona(lista1, lista->valor);
lista = Remove(lista);
/* elementos pares para a lista 2 */
if(lista != NULL)
{
lista2 = Adiciona(lista2, lista->valor);
lista = Remove(lista);
}
}
/* neste momento j no h lista original, apenas lista1 e lista2 */
/* ordenar ambas as metades */
lista1 = MergeSort(lista1);
lista2 = MergeSort(lista2);

18. Exerccios: Dicas, Respostas e Resolues


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

/* juntar ambas as listas ordenadas */


while(lista1 != NULL && lista2 != NULL)
{
if(lista1->valor < lista2->valor)
{
lista = Adiciona(lista, lista1->valor);
lista1 = Remove(lista1);
}
else
{
lista = Adiciona(lista, lista2->valor);
lista2 = Remove(lista2);
}
}
/* adicionar os restantes elementos */
if(lista1 == NULL)
lista1 = lista2;
while(lista1 != NULL)
{
lista = Adiciona(lista, lista1->valor);
lista1 = Remove(lista1);
}
/* inverter a lista, j que os elementos mais baixos
so colocados primeiro, mas ficam em ltimo */
while(lista != NULL)
{
lista1 = Adiciona(lista1, lista->valor);
lista = Remove(lista);
}
return lista1;
}
int main()
{
int i;
Lista *lista = NULL;
srand(1);
for(i = 0; i < 100000; i++)
lista = Adiciona(lista, rand() % 1000);
lista = MergeSort(lista);
/* remover os primeiros 999 elementos, para aceder posio 1000 */
for(i = 0; i < 999; i++)
lista = Remove(lista);
printf("Elemento na posio 1000: %d\n", lista->valor);
/* remover os primeiros 9000 elementos, para aceder posio 10000 */
for(i = 0; i < 9000; i++)
lista = Remove(lista);
printf("Elemento na posio 10000: %d", lista->valor);
while(lista != NULL)
lista = Remove(lista);
}
/* sort.c */
#define MAXSTR 1024
typedef struct SListaSTR
{
char *str;
struct SListaSTR *seg;
}
ListaSTR;
ListaSTR *Adiciona(ListaSTR *lista, char *str)
{
ListaSTR *elemento = (ListaSTR *) malloc(sizeof(ListaSTR));
if(elemento != NULL)
{
/* alocar espao para a string, e copiar a string */
elemento->str = (char *) malloc(strlen(str) + 1);
strcpy(elemento->str, str);
elemento->seg = lista;
}
return elemento;
}
ListaSTR *Remove(ListaSTR *lista)
{
ListaSTR *aux;
if(lista != NULL)
{
aux = lista->seg;
/* libertar no s a estrutura como a string */
free(lista->str);
free(lista);
return aux;
}
return NULL;
}

211

212
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

Parte IV Anexos
ListaSTR *MergeSort(ListaSTR *lista)
{
ListaSTR *lista1 = NULL, *lista2 = NULL;
if(lista == NULL || lista->seg == NULL)
return lista;
while(lista != NULL)
{
lista1 = Adiciona(lista1, lista->str);
lista = Remove(lista);
if(lista != NULL)
{
lista2 = Adiciona(lista2, lista->str);
lista = Remove(lista);
}
}
lista1 = MergeSort(lista1);
lista2 = MergeSort(lista2);
/* tudo igual at aqui, excepto utilizar str em vez de valor.
Agora, ao juntar ambas as listas ordenadas, utilizao de strcmp */
while(lista1 != NULL && lista2 != NULL)
{
if(strcmp(lista1->str, lista2->str) < 0)
{
lista = Adiciona(lista, lista1->str);
lista1 = Remove(lista1);
}
else
{
lista = Adiciona(lista, lista2->str);
lista2 = Remove(lista2);
}
}
if(lista1 == NULL)
lista1 = lista2;
while(lista1 != NULL)
{
lista = Adiciona(lista, lista1->str);
lista1 = Remove(lista1);
}
while(lista != NULL)
{
lista1 = Adiciona(lista1, lista->str);
lista = Remove(lista);
}
return lista1;
}
int main(int argc, char **argv)
{
FILE *f;
char str[MAXSTR], *pt, c;
ListaSTR *lista = NULL, *aux;
/* verificar se foi dado um argumento */
if(argc <= 1)
{
printf("Utilizao: sort <ficheiro>\n");
return;
}
/* abrir o ficheiro em modo de texto*/
f = fopen(argv[1], "rt");
if(f == NULL)
{
printf("Ficheiro %s no existe.\n", argv[1]);
return;
}
while(! feof(f))
if(fgets(str, MAXSTR, f) != NULL)
lista = Adiciona(lista, str);
lista = MergeSort(lista);
/* mostrar a lista ordenada */
aux = lista;
while(aux != NULL)
{
printf("%s", aux->str);
aux = aux->seg;
}
/* libertar a lista */
while(lista != NULL)

18. Exerccios: Dicas, Respostas e Resolues


130
131
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

lista = Remove(lista);
}
/* find.c */
#define MAXSTR 1024
int Find(char *str, char *find)
{
int ifind, istr, i;
/* verificar se a string em find possvel de corresponder
string de str, desde i */
for(i = 0; str[i] != 0; i++)
{
ifind = 0;
istr = i;
do
{
if(find[ifind] == 0)
return i;
/* se se pretende ignorar um caracter, avanar ifind e istr */
if(find[ifind] == '?')
{
ifind++;
istr++;
}
else if(find[ifind] == '*')
{
/* pretende-se ignorar zero ou mais caracteres:
verificar se o resto da string pode-se obter com
o resto da string de procura*/
if(Find(str + istr, find + ifind + 1) >= 0)
return i;
break;
}
else if(find[ifind] == str[istr])
{
/* match de dois caracteres, avanar */
ifind++;
istr++;
}
else
/* caracteres distintos, parar */
break;
}
while(find[ifind] == 0 || str[istr] != 0);
}
return - 1;
}
int main(int argc, char **argv)
{
FILE *f;
char str[MAXSTR], *pt, c;
int count = 0;
/* verificar se foi dado um argumento */
if(argc <= 2)
{
printf("Utilizao: find <ficheiro> <procura>\n");
return;
}
/* abrir o ficheiro em modo de texto*/
f = fopen(argv[1], "rt");
if(f == NULL)
{
printf("Ficheiro %s no existe.\n", argv[1]);
return;
}
/* adaptado de more.c */
while(! feof(f))
{
count++;
/* apresenta a linha apenas se h match */
if(fgets(str, MAXSTR, f) != NULL && Find(str, argv[2]) >= 0)
{
if(strlen(str) <= 75)
printf("%4d:%s", count, str);
else
{
pt = str;
while(strlen(pt) > 75)
{
c = pt[75];
pt[75] = 0;
if(pt == str)
printf("%4d:%s", count, pt);
else
printf("
%s", pt);
pt[75] = c;
pt += 75;

213

214
91
92
93
94
95
96
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
1
2
3
4

Parte IV Anexos
}
printf("
}

%s", pt);

}
}
/* histograma.c */
#define MAXELEMENTOS 100
#define MAXVALORES 22
#define MAXSTR 255
/* funo reutilizada do exerccio doisdados.c da AF7 */
int GraficoDeBarras(int serie[], int n)
{
char grafico[MAXVALORES][MAXELEMENTOS];
int i, j, maximo, count = 0;
float factor;
if(n > MAXELEMENTOS)
return;
for(i = 0; i < n; i++)
if(i == 0 || maximo < serie[i])
maximo = serie[i];
factor = 15. / maximo;
sprintf(grafico[0], "");
sprintf(grafico[1], "
+");
for(i = 0; i < n; i++)
if(i % 5 == 0)
strcat(grafico[1], "+");
else
strcat(grafico[1], "-");
for(i = 2; i < 18; i++)
sprintf(grafico[i], "% 5d|%*s ",(int)((i - 2) / factor + 0.5), n,
" ");
for(i = 0; i < n; i++)
for(j = 0; j < 16; j++)
if(serie[i] *factor >= j)
{
grafico[j + 2][i + 6] = '#';
count++;
}
for(i = 17; i > 0; i--)
printf("%s\n", grafico[i]);
return count;
}
int main(int argc, char **argv)
{
int histograma[256], i, j, count;
char str[MAXSTR], schar[256];
FILE *f;
if(argc != 2)
{
printf("Deve indicar um ficheiro para fazer o histograma.\n");
return;
}
f = fopen(argv[1], "r");
if(f == NULL)
{
printf("Nao foi possivel abrir para leitura o ficheiro %s.\n",
argv[1]);
return;
}
/* primeiro fazer o histograma completo */
for(i = 0; i < 256; i++)
histograma[i] = 0;
while(fgets(str, MAXSTR, f) != NULL)
for(i = 0; str[i] != 0; i++)
histograma[(unsigned char) str[i]]++;
/* concatenar e construir a string de valores */
for(i = 0; i < 256; i++)
schar[i] = i;
/* utilizar apenas os caracteres alfa-numricos */
for(i = 0, j = 0; i < 256; i++)
if(schar[i] >= 'a' && schar[i] <= 'z' ||
schar[i] >= 'A' && schar[i] <= 'Z' ||
schar[i] >= '0' && schar[i] <= '9')
{
histograma[j] = histograma[i];
schar[j] = schar[i];
j++;
}
schar[j] = 0;
count = GraficoDeBarras(histograma, j);
printf("
%s\nNumero de cardinais no histograma: %d", schar,
count);
}
/* indicador.c */
#define MAXSTR 1024
typedef struct SListaSTR

18. Exerccios: Dicas, Respostas e Resolues


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

{
char *str;
struct SListaSTR *seg;
}
ListaSTR;
ListaSTR *Adiciona(ListaSTR *lista, char *str)
{
ListaSTR *elemento = (ListaSTR *) malloc(sizeof(ListaSTR));
if(elemento != NULL)
{
elemento->str = (char *) malloc(strlen(str) + 1);
strcpy(elemento->str, str);
elemento->seg = lista;
}
/* em caso de falha, retorna NULL */
return elemento;
}
ListaSTR *Remove(ListaSTR *lista)
{
if(lista != NULL)
{
ListaSTR *aux = lista->seg;
free(lista->str);
free(lista);
return aux;
}
return NULL;
}
ListaSTR *Inverte(ListaSTR *lista)
{
ListaSTR *aux = NULL;
while(lista != NULL)
{
aux = Adiciona(aux, lista->str);
lista = Remove(lista);
}
return aux;
}
char *EspacosCodigo(char *codigo)
{
char *pt;
/* colocar todos os caracteres brancos em espaos */
while((pt = strchr(codigo, '\n')) != NULL)
*pt = ' ';
while((pt = strchr(codigo, '\r')) != NULL)
*pt = ' ';
while((pt = strchr(codigo, '\t')) != NULL)
*pt = ' ';
}

return codigo;

void RemoverStrings(ListaSTR *lista)


{
char *pt, tipo = ' ', lastchar = ' ', llastchar = ' ';
while(lista != NULL)
{
/* apagar as macros */
if(lista->str[0] == '#')
{
pt = (char *) malloc(1);
*pt = '\0';
free(lista->str);
lista->str = pt;
continue;
}
pt = lista->str;
while(*pt != '\0')
{
/* verificar se h incio de string entre
aspas/plicas/comentrios */
if(tipo != ' ' || pt[0] == '\'' || pt[0] == '"' ||
pt[0] == '/' && pt[1] == '*')
{
if(tipo == ' ')
{
tipo = pt[0];
pt++;
}
while(*pt != '\0' &&
(pt[0] != tipo ||
tipo == '/' && lastchar != '*' ||
(*pt == '\'' ||*pt == '"') &&
lastchar == '\\' && llastchar != '\\'))
{
llastchar = lastchar;

215

216
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

Parte IV Anexos
lastchar = pt[0];
/* o contedo anulado e colocado c, de forma a
no influenciar o indicador */
*pt = 'c';
pt++;
}
if(*pt != '\0')
{
tipo = ' ';
pt++;
}
}
else
pt++;
}
lista = lista->seg;
}
}
void InicioFimBlocos(ListaSTR *lista)
{
static char *inicioFimBlocos = "{}";
char *pt;
int i;
while(lista != NULL)
{
for(i = 0; inicioFimBlocos[i] != '\0'; i++)
{
/* no pode estar nada antes do incio/fim do bloco */
if((pt = strchr(lista->str, inicioFimBlocos[i])) != NULL &&
pt != lista->str)
{
lista->seg = Adiciona(lista->seg, pt);
*pt = 0;
}
/* no pode estar nada depois do incio/fim do bloco */
if(lista->str[0] == inicioFimBlocos[i] &&
strlen(lista->str) > 1)
{
lista->seg = Adiciona(lista->seg, lista->str + 1);
lista->str[1] = 0;
}
}
lista = lista->seg;
}
}
int Indicador(ListaSTR *lista)
{
int chavetasAbertas = 0, i;
int resultado = 0, conds = 0, ciclos = 0, comandos = 0;
char *pt;
static char *condsSTR[] =
{
"if", "case", NULL
};
static char *ciclosSTR[] =
{
"for", "while", NULL
};
/* processar todos os blocos globais */
printf("\n
conds
ciclos comandos parcial");
while(lista != NULL)
{
/* contar os blocos */
if(lista->str[0] == '{')
chavetasAbertas++;
else if(lista->str[0] == '}')
{
/* fim de bloco */
chavetasAbertas--;
if(chavetasAbertas == 0)
{
/* contabilizar o bloco global */
resultado += (conds + 2 *ciclos + 1) *(conds + 2 *ciclos +
1) + comandos;
/* mostrar parcial */
printf("\n%8d %8d %8d %8d",
conds, ciclos, comandos, resultado);
/* zerar os valores para o prximo bloco */
conds = ciclos = comandos = 0;
}
else if(chavetasAbertas < 0)
chavetasAbertas = 0;
}
else
{
/* nmero de comandos */
pt = lista->str;
while((pt = strchr(pt + 1, ';')) != NULL)
comandos++;

18. Exerccios: Dicas, Respostas e Resolues


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

/* nmero de conds */
for(i = 0; condsSTR[i] != NULL; i++)
{
pt = lista->str;
while((pt = strstr(pt, condsSTR[i])) != NULL)
{
conds++;
pt++;
}
}
/* nmero de ciclos */
for(i = 0; ciclosSTR[i] != NULL; i++)
{
pt = lista->str;
while((pt = strstr(pt, ciclosSTR[i])) != NULL)
{
ciclos++;
pt++;
}
}

lista = lista->seg;
}
return resultado;
}
int main(int argc, char **argv)
{
FILE *f;
int i;
char str[MAXSTR], *pt, c;
ListaSTR *lista = NULL, *aux, *buffer;
/* verificar se foi dado um argumento */
if(argc <= 1)
{
printf("Utilizacao: indicador <ficheiro>\n");
return;
}
/* abrir o ficheiro em modo de texto*/
f = fopen(argv[1], "rt");
if(f == NULL)
{
printf("Ficheiro %s nao existe.\n", argv[1]);
return;
}
while(! feof(f))
if(fgets(str, MAXSTR, f) != NULL)
lista = Adiciona(lista, str);
lista = Inverte(lista);
/* acertar os espaos nas linhas */
aux = lista;
while(aux != NULL)
{
aux->str = EspacosCodigo(aux->str);
aux = aux->seg;
}
/* congelar strings de comentrios, strings e plicas */
RemoverStrings(lista);
/* incio/fim de blocos em linhas isoladas */
InicioFimBlocos(lista);
/* calcular indicador */
printf("\nIndicador: %d", Indicador(lista));
/* libertar a lista */
while(lista != NULL)
lista = Remove(lista);
}

217

NDICE DE PROGRAMAS
Programa 1-1 Ol Mundo ..................................................................................................................................................... 7
Programa 2-1 Troca o valor de duas variveis............................................................................................................ 9
Programa 2-2 Troca o valor de duas variveis sem varivel auxiliar .............................................................. 10
Programa 2-3 Imprime o nmero de bytes que um inteiro ocupa ................................................................... 11
Programa 2-4 Imprime diferentes tipos de variveis ............................................................................................ 12
Programa 2-5 Pede valores de variveis de diferentes tipos que depois imprime .................................... 13
Programa 3-1 Determina se um nmero par ou impar ...................................................................................... 16
Programa 3-2 Determina se um ano normal ou bissexto .................................................................................. 18
Programa 3-3 Determina o nmero de dias de um ms/ano .............................................................................. 19
Programa 4-1 Soma dos primeiros 4 quadrados naturais ................................................................................... 23
Programa 4-2 Soma dos primeiros 4 quadrados naturais utilizando um ciclo ........................................... 24
Programa 4-3 Imprime o cdigo numrico correspondente a cada letra ...................................................... 25
Programa 4-4 Calculo do nmero de pares de inteiros que respeitam uma determinada condio . 26
Programa 5-1 Escolha de opes num menu ............................................................................................................. 40
Programa 5-2 Escolha de opes num menu, utilizando uma funo ............................................................. 42
Programa 5-3 Determina se um ano normal ou bissexto utilizando funes ........................................... 44
Programa 5-4 Determina o nmero de dias de um ms/ano utilizando funes ....................................... 45
Programa 6-1 Rplica do Programa 4-2....................................................................................................................... 49
Programa 6-2 Soma dos primeiros 4 quadrados naturais com o ciclo for ................................................. 49
Programa 6-3 Utilizao alternativa do ciclo for na soma dos 4 quadrados naturais........................... 50
Programa 6-4 Verso com ciclos for do Programa 4-4 ...................................................................................... 50
Programa 6-5 Verso com ciclos do-while do Programa 5-2 .......................................................................... 52
Programa 6-6 Verso removendo a funo Menu do Programa 6-5 ................................................................ 53
Programa 6-7 Verso com switch do Programa 6-6 ........................................................................................... 54
Programa 6-8 Funo DiasDoMes com switch do Programa 5-4................................................................... 55
Programa 7-1 Calculo do total e mdia de um indicador por cada dia da semana .................................... 57
Programa 7-2 Calculo do total e mdia, verso com vectores ............................................................................ 58
Programa 7-3 Confirmao que uma string um vector de caracteres ......................................................... 60
Programa 7-4 Calcular o tamanho de uma string .................................................................................................... 61
Programa 7-5 Funo DiasDoMes com um vector inicializado na declarao............................................. 62
Programa 7-6 Exemplificao das diferentes maneiras de se inicializar strings........................................ 62
Programa 7-7 Calculo do total e mdia, sem cdigo repetido, utilizando vectores ................................... 63
Programa 7-8 Leitura de argumentos passados ao programa ........................................................................... 64
Programa 7-9 Soma de duas matrizes .......................................................................................................................... 64
Programa 8-1 Tentativa de implementao de uma funo para trocar o valor de duas variveis.... 69
Programa 8-2 Funo Troca que realmente troca o valor de duas variveis ............................................... 70
Programa 8-3 Verso final da funo de troca de duas variveis...................................................................... 71
Programa 8-4 Exemplo de utilizao de variveis globais ................................................................................... 72
Programa 8-5 Exemplo de utilizao de variveis estticas (static) ............................................................ 73
Programa 8-6 Procura a primeira ocorrncia de uma string em outra (strstr) .......................................... 74
Programa 9-1 Factorial, verso iterativa e recursiva ............................................................................................. 78
Programa 9-2 Torres de Hanoi ........................................................................................................................................ 80
Programa 10-1 Generalizao do Programa 7-2, calculando a mdia e o desvio padro .................... 101
Programa 10-2 Rplica do Programa 9-1 ................................................................................................................. 103
Programa 10-3 Calculo da mdia e desvio padro, alocando apenas a memria necessria ............. 104
Programa 10-4 Concatena as strings introduzidas, utilizando apenas a memria necessria .......... 106

Programa 10-5 Gerao de um vector aleatrio.................................................................................................... 107


Programa 11-1 Rplica do Programa 5-4 ................................................................................................................. 113
Programa 11-2 Calculo dos dias de um ms/ano, utilizando estruturas .................................................... 115
Programa 11-3 Registo de uma estrutura com dados de diferentes tipos de pessoas .......................... 116
Programa 11-4 Verso do registo de pessoas, mais legvel e eficiente ........................................................ 118
Programa 11-5 Estrutura de um vector com os valores e a sua dimenso ................................................ 119
Programa 11-6 Tipo abstracto de dados TVectorInt ........................................................................................... 121
Programa 11-7 Verso de TVectorInt com apontadores, e controle do tempo ........................................ 123
Programa 11-8 Ajustes feitos no tipo abstracto de dados TVectorInt, para melhoria da eficincia 125
Programa 11-9 Tipo abstracto de dados TListaInt............................................................................................... 128
Programa 12-1 Nmero de dias dado ms/ano, lendo dados a partir do ficheiro in.txt .................. 134
Programa 12-2 Nmero de dias do ms dado ms/ano lendo dados de in.txt e escrevendo para
out.txt ................................................................................................................................................................................... 135
Programa 12-3 Ficha de pessoa, com gravao e leitura do ficheiro ............................................................ 138
Programa 12-4 Actualizao do tipo abstracto TVectorInt para ler/gravar para ficheiro, em modo
binrio ..................................................................................................................................................................................... 141
Programa 13-1 Verso condensada do nmero de dias de um dado ms/ano ........................................ 143
Programa 13-2 Verso ainda mais condensada do nmero de dias de um dado ms/ano ................. 144
Programa 13-3 Instruo de shift vs multiplicao/diviso por 2 ................................................................ 145
Programa 13-4 Representao binria de um nmero real ............................................................................. 146
Programa 13-5 Verso do Programa 13-4 utilizando a estrutura union ................................................. 147
Programa 13-6 Utilizao do formato do nmero real para aumentar a eficincia de algumas
operaes ............................................................................................................................................................................... 148
Programa 13-7 Exemplo de utilizao de vrias variveis booleanas numa s varivei inteira ...... 149
Programa 15-1 Cdigo incompreensvel .................................................................................................................. 166
Programa 15-2 Programa incompreensvel, preparado para debug ............................................................ 167

NDICE REMISSIVO
"(null)" ................................. 75, 108
#define ......................................... 65
#include ......................................... 7

A
abrir um ficheiro e no chegar a
fechar .................................... 142
abrir um ficheiro e no testar se foi
bem aberto ........................... 142
abstraco de dados .................. 120
abstraco funcional .................... 43
adiciona ..................................... 151
alocar memria e no testar se a
operao foi bem sucedida ... 112
apontadores................................. 74
argumentos ................................. 83
argv .............................................. 64
arranjos ....................................... 31
atribuies ..................................... 9
atribuies entre variveis de tipos
distintos ................................ 112

B
baralhar ...................................... 89
basededados .............................. 155
binarysearch ............................... 91
bits num nmero real ................ 146
bloco de cdigo ............................ 19
break ............................................ 55

C
calendario ................................... 94
cast............................................. 105
char .............................................. 11
ciclo dentro de outro ciclo ........... 26
Ciclos ............................................ 23
ciclos contendo apenas uma
varivel lgica no teste de
paragem .................................. 55
cifracesar ................................... 154
combinacoes ............................... 33
comentrios ao cdigo .................. 9
comentrios explicam a linguagem
C .............................................. 14
comentrios inexistentes em partes
complexas ............................. 150
compilador ..................................... 7
compilador e Editor de C ........... 161
compile time .............................. 100
Condicinais ................................... 16

conjunto de conjuntos de
caracteres ............................. 63
conjunto de strings .................. 64
conjuntos de variveis ............. 57
continue ...................................... 55
copy / paste................................. 41

funes muito grandes ................ 76


Funes standard mais utilizadas
.............................................. 169

G
gets .............................................. 60

D
declarao de variveis ................. 9
declaraes de variveis fora do
incio das funes ................... 47
declaraes ou atribuies a
variveis nunca utilizadas ....... 13
doisdados ................................... 94
double ......................................... 11
do-while ...................................... 51
duas instrues na mesma linha . 21

E
editdistance................................ 96
else .............................................. 16
enesimo..................................... 153
erros comuns .............................. 13
estrutura de dados .................... 128
Estruturas .................................. 113
euler............................................ 33
execuo alternativa ................... 16
execuo do programa................ 13
execuo passo-a-passo .............. 10
Exemplo de cdigo incompreensivel
............................................. 165
expresso lgica .....................16, 20

H
Heap .......................................... 104
histograma ................................ 158
hms ............................................. 30

I
if... ............................................... 16
indentao varivel ..................... 20
indicador.................................... 159
inicializao de strings ................. 62
inicializao de um vector ........... 62
insere ......................................... 152
insertsort ................................... 153
instrues parecidas no seguidas
................................................ 46
instrues parecidas seguidas ..... 27
int... ............................................. 11
inverte ........................................ 83

J
jogodados ................................... 88

L
F
fibonacci ..................................... 32
ficheiros..................................... 130
find.......................................84, 157
float ............................................. 11
fopen / fprintf / fscanf / fgets / feof
/ fclose .................................. 133
for................................................ 49
formularesolvente ..................... 36
fread / fwrite ............................. 140
free ............................................ 104
fseek / ftell ................................ 140
funo ......................................... 40
funes com muitos argumentos 77
funes com parmetros no nome
............................................... 46
funes especficas ..................... 47

linguagem C ................................... 7
linguagem de programao ........... 7
linguagem imperativa .................... 7
linhas de cdigo nunca executadas
................................................ 21
listas .......................................... 126
long .............................................. 11
long long ...................................... 11

M
m utilizao da recurso ............ 82
m utilizao do switch ou cadeia
if-else ...................................... 66
macro.......................................... 65
macros em todas as constantes .. 67

main ............................................... 8
mais ciclos e condicionais ............ 49
malloc ........................................ 104
matrizes ....................................... 64
maximo ....................................... 85
mdc .............................................. 85
Memria .................................... 100
mergesort ........................... 90, 156
modo binrio ............................. 139
modo de texto ........................... 139
more .................................. 151, 155

N
No consigo perceber o resultado
do programa ......................... 164
no libertar memria aps alocar
.............................................. 112
ndamas ........................................ 93
nomes sem significado................. 76
Notepad++ ................................. 163
NULL..................................... 75, 105
numeracaoromana ..................... 91

P
palavras ..................................... 154
passagem por referncia............. 71
pi. ............................................. 35
pokerdados ................................ 92
pr-processamento ....................... 7
Primeiro Programa ........................ 7
primo .......................................... 34
printf ............................................. 8
Procedimentos ............................ 69
produto....................................... 31
programa....................................... 7
programar ..................................... 7

R
rand ............................................ 84
Recurso...................................... 78
removedups ............................... 87
run time .................................... 100

S
O
ol mundo ...................................... 7
olamundosizeof .......................... 29
operaes binrias lgicas ......... 146
operador "." ............................... 116
operador & .................................. 71
operador * ........................... 71, 116
operador ? ................................. 143
operador -> ................................ 116
operador >> >>= << <<= ............. 145
operador de linha de comando |
.............................................. 132
operador de linha de comando >>
.............................................. 132
operadores | & ^ ~..................... 146
operadores || && !................... 17
operadores + - * / % .................. 17
operadores ++ -- .......................... 24
operadores += -= *= /= %= ........... 24
operadores == != > >= < <= ...... 17
operadores binrios ................... 145
operadores de linha de comando <
e > ......................................... 130

scanf ............................................ 12
short ............................................ 11
situao de desespero .............. 164
sizeof ........................................... 11
soma ........................................... 30
somadigitos ................................ 32
somanumeros ............................ 87
sort ......................................86, 157
srand ........................................... 84
Stack .......................................... 102
static............................................ 72
stdin .......................................... 135
stdlib.h ........................................ 84
stdout ........................................ 136
strcat ........................................... 74
strcpy .......................................... 74
string ......................................10, 59
string de formatao ................... 11
string.h ........................................ 74
strlen ........................................... 60
strreplace ................................... 93
strstr ............................................ 74
strtok ........................................... 87
struct .................................... 114

switch .......................................... 53

T
TCC: Tiny C Compiler ................. 161
tempo de compilao ................ 100
tempo de corrida ....................... 100
time(NULL); ................................. 84
time.h .......................................... 84
tipos abstractos de dados.......... 120
tipos elementares........................ 11
torres de Hanoi ............................ 79
triplasoma .................................. 35
trocos .......................................... 34
trocos2 ........................................ 88
truques ...................................... 143
typedef ...................................... 117

U
uma atribuio, uma leitura ........ 13
union ......................................... 147
utilizao do goto ........................ 27
utilizar atribuies como
expresses ............................ 144

V
Variveis ........................................ 9
variveis com nomes quase iguais
................................................ 66
variveis desnecessrias .............. 14
variveis estticas........................ 72
variveis globais.................. 72, 76
varivel .......................................... 9
varivel iteradora ........................ 24
vector de caracteres ................ 59
Vectores ...................................... 57
vectores de vectores ................... 64
vectores multi-dimensionais ....... 64

W
while ............................................ 23

Você também pode gostar