10 li¸c˜ oes para aprender C

Transcription

10 li¸c˜ oes para aprender C
10 lições
para aprender
a linguagem C
em Português.
Tarcisio Praciano Pereira1
Universidade Estadual Vale do Acaraú
Sobral, 12 de julho de 2005- Ceará
1
Dep de Matemática - U.V.A. - [email protected]
Tarcisio Praciano Pereira
PhD em Matemática
10 LIÇÕES
PARA APRENDER A LINGUAGEM C
em português
Edição eletrônica
3
P496c
Pereira, Tarcisio Praciano
10 lições para aprender C
Sobral: UVA, 2001
224.p
Bibliografia
ISBN: solicitado
1 - A linguagem C
I. Tı́tulo
CDD xxx.xx
4
Lista de Figuras
1.1
árvore de diretórios - BC
4.1
4.2
4.3
4.4
4.5
4.6
se() ou entao
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
Fluxograma com dois se(), uma entrada e uma saı́da dados . . .
Fluxograma da equação do segundo grau. . . . . . . . . . . .
.
.
.
.
.
74
75
76
77
78
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
95
Fluxograma do se() . . .
Fluxograma com dois se()
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
22
Ao encontrar pare() o fluxo é desviado para a próxima função externa ao
bloco.
6.1
6.2
Variável global e variável local.
Variável global e local . . . .
. . . . . . . . . . . . . . . . . . . . . . 118
. . . . . . . . . . . . . . . . . . . . . . 122
7.1
7.2
7.3
7.4
Máquina do balcão do comércio, coleção do autor.
8.1
8.2
Equação do segundo grau . . . . . .
Cáculo da integral, aproximadamente.
9.1
O produto de números complexos: parte imaginária se obtem em cruz
. .
.
.
. . . . . . . . . . . . . . . . . .
duas formas equivalentes para imprimir 30 na base 8
Formatação de dados em printf() . . . . . . . . .
Uso de printf()
5
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
137
166
167
168
. . . . . . . . . . . . . . . . . . 180
. . . . . . . . . . . . . . . . . . 183
. . . 200
6
LISTA DE FIGURAS
Sumário
Introdução ................................... . . . . . . . . . . . . . . . . . . . .
I
Usandos os comandos em Português
10
17
1 Uma primeira suite de programas
19
1.1 Como rodar um programa. . . . . . . . . . . . . . . . . . . . . . 19
2 O segundo programa em C
33
2.1 Programas e erros... . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.1.1 Análise do prog02 1.c . . . . . . . . . . . . . . . . . . . . 46
3 Números e Letras
3.1 Brincando com números em C. . . .
3.1.1 Leitura de dados . . . . . . .
3.2 Brincando com as palavras em C. . .
3.2.1 Palavras, macros, caracteres.
3.2.2 Vetores de caracteres. . . . .
4 Controle lógico do fluxo
4.1 O condicional se() (if()) . . .
4.2 Múltiplas escolhas. . . . . . . .
4.3 enquanto() while() . . . . . . .
4.4 Outro método para laços. . . .
4.5 Parando no meio de um bloco.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5 Criando funções
5.1 Verificador de senhas. . . . . . . . . . . . .
5.1.1 Metamorfoses do Leitor de Palavras.
5.1.2 Sistema de contabilidade geral . . .
5.1.3 Como registrar dinheiro . . . . . . .
5.2 Máquina de calcular. . . . . . . . . . . . . .
5.2.1 O menu de opções . . . . . . . . . .
7
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
53
53
59
62
62
66
.
.
.
.
.
71
71
83
87
92
94
.
.
.
.
.
.
97
100
101
107
109
109
109
8
II
SUMÁRIO
Aprofundando os conhecimentos
6 Variável global e local
6.1 Variável global e local . . . . . . . . .
6.1.1 Comentários sobre os exercı́cios
6.2 Técnicas com o uso de variáveis locais
6.3 Passando valores entre funções . . . .
7 Os tipos básicos de dados
7.1 Os números em C . . . . . . . . . .
7.1.1 Os números inteiros . . . .
7.1.2 Os números reais . . . . . .
7.1.3 Bibliotecas do BC . . . . . .
7.2 Caracteres e vetores de caracteres.
7.3 Ponteiros. . . . . . . . . . . . . . .
7.3.1 Operações com ponteiros. .
7.4 Manipulando arquivos em disco . .
7.5 Matriz, (array) . . . . . . . . . . .
7.6 Estrutura, struct. . . . . . . . . . .
7.6.1 tempo para os humanos . .
7.6.2 tempo para o computador .
7.7 Formatadores para saı́da de dados
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
111
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8 Matemática em C
8.1 Operadores aritméticos e lógicos . . . . . . .
8.1.1 Uma lista seca de operadores . . . . .
8.2 Equação do segundo grau . . . . . . . . . . .
8.3 Somas e integrais em C . . . . . . . . . . . . .
8.3.1 Integral de funções univariadas . . . .
8.4 Gráficos de funções usando C . . . . . . . . .
8.4.1 Comentando o programa grafun01.c
9 Programação avançada
9.1 Continuar se aprofundando em C . . . . .
9.1.1 C + + . . . . . . . . . . . . . . . .
9.1.2 Programação orientada a objeto .
9.2 O programa menu.cc . . . . . . . . . . . .
9.2.1 Construção da idéia . . . . . . . .
9.3 Números complexos . . . . . . . . . . . .
9.3.1 Que é número complexo . . . . . .
9.3.2 O programa em C . . . . . . . . .
9.3.3 Construção de complexo milenium
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
plus.cc
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
117
117
120
123
127
.
.
.
.
.
.
.
.
.
.
.
.
.
131
131
132
137
142
144
148
153
154
155
159
163
165
165
.
.
.
.
.
.
.
171
172
173
178
182
182
185
186
.
.
.
.
.
.
.
.
.
191
191
192
193
197
197
199
199
200
201
SUMÁRIO
10 Manual introdutório de referência
10.1 O Sistema operacional e a shell . . . . . . . . . . . . . . . . .
10.2 instruções de compilação . . . . . . . . . . . . . . . . . . . . .
10.3 linha de comando . . . . . . . . . . . . . . . . . . . . . . . . .
10.4 Operadores aritméticos e lógicos . . . . . . . . . . . . . . . .
10.5 A libc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.6 Manual do compilador gcc . . . . . . . . . . . . . . . . . . . .
Bibliografia ............................................................................... 212
9
.
.
.
.
.
.
.
.
.
.
.
.
203
204
206
206
208
208
210
10
SUMÁRIO
Introdução.
Toda manhã, na Africa, uma corça se levanta e sabe
terá que ser mais rápida que o mais rápido dos leões,
ou será morta.
Toda manhã um leão se levanta e sabe
terá que superar a mais lenta das corças,
ou morrerá de fome.
Não importa se você é leão ou corça
quando o sol se levantar,
é melhor sair correndo.
- autor desconhecido
Como usar este livro.
Para começar, sugerimos que não leia, agora, esta introdução. Leia primeiro
o resto do livro, depois a introdução, porque, lhe confessamos, primeiro escrevemos o livro, depois a introdução. Mas se você quiser insistir, faça uma leitura
rápida e depois volte para ler com mais cuidado. Você vai ver que, então, vale
a pena.
Este livro tem dez capı́tulos em que lhe apresentamos as técnicas básicas
para programar na linguagem C, mas é preciso enfatizar, você vai apenas se
iniciar na linguagem com este livro.
O livro está divido em duas partes. Na primeira, vamos apresentar-lhe C em
português por duas razões:
• para vencer a dificuldade psicológica com o Inglês, creamos um arquivo
que traduz os comandos da linguagem C para o Português, de modo que
você se inicie sem a dificuldade linguı́stica;
• para mostrar-lhe que com C podemos facilmente construir outra linguagem, neste caso é “C em português”. Isto mostra o poder da linguagem.
Mas você não deve se enganar com o apoio linguı́stico e nem queremos induzı́lo no erro de que é possivel viver sem o inglês. Por esta mesma razão vamos
manter os programas traduzidos junto com os programas naturais em C de modo
que você vá aos poucos se habituando com as palavras da linguagem em Inglês.
Na segunda parte usaremos apenas os comandos em inglês.
O conteúdo das duas partes do livro, em linha gerais é o seguinte:
1. A primeira parte, constituida dos cinco primeiros capı́tulos, deverá deixálo escrevendo pequenos programas em C e todos os exemplos serão em
“português” com a tradução ao lado. Depois você tomará sua decisão, se
SUMÁRIO
11
quiser continuar escrevendo seus programas em Inglês, como é habitual,
ou continuar a escrevê-los em português.
Observe que os programas em “português” rodam da mesma forma como
os programas em “inglês”, eles não são de mentira.
2. Na segunda parte vamos avançar em profundidade em algumas direções. A
partir dai só apresentaremos pedaços de programas, porque os programas
inteiros você pode conseguı́-los num disco ou via Internet, veja como fazer
na bibliografia, ou envie e-mail para
[email protected]
solicitando os programas. Você pode usar este enderço para consultas
rápidas, mas, por favor, não espere que lhe possamos dar um curso particular via internet.
Se você quiser continuar programando em “português” esta será uma
opção sua e até lá você terá aprendido como fazer.
O método que vamos usar no livro se assemelha àquele que usaram quando
você era pequeno, para aprender a sua lingua materna. Mostraram-lhe várias
vezes o mesmo objeto, cadeira, e lhe disseram: “cadeira”, aı́ você aprendeu a
diferença “lógica” entre uma cadeira e uma mesa.
Vamos lhe mostrar pequenos programas, pedir que você os rode num computador em que o C esteja instalado1 . Depois iremos comentar os programas e
lhe indicar como você os pode alterar, e assim por diante.
Parte do método consiste do estilo com que os capı́tulos foram escritos: há
superposição entre eles, quer dizer, o mesmo assunto, o mesmo conceito, aparece
várias vezes, aumentando de intensidade nos capı́tulos de “número maior”. É o
método de explicação lógica da diferença entre cadeira e mesa, de tanto falar,
termina ficando claro o que as coisas são. Você será inclusive convidado a pular
para os capı́tulos mais avançados com frequência e, não tenha dúvida em fazê-lo,
se achar que está bem lá na frente, não precisa voltar atrás...
Como rodar o C ?
Se você puder escolher, use LinuX e a linguagem gratuita C que vem com
este sistema operacional. Se você tiver ainda alguma chance de escolher, mesmo
tendo que trabalhar dentro do Windows, (poucas chances de escolha...), sugerimos
• use o C da fundação GNU, procure em www.gnu.org ou envie e-mail para
o autor. Veja abaixo instruções mais detalhadas,
1 se
você tiver acesso a um computador rodando LinuX, então o C estará com toda certeza
instalado
12
SUMÁRIO
• dê preferência ao C da Borland, BC que todos que o analisam consideram
melhor que o Microsoft C,
• se você não tiver mesmo nenhuma outra opção, use o que tiver à mão, mas
aprenda o C.
• Se você usa Linux e por alguma razão complicada precisar de usar Borland
C, você pode fazê-lo sob dosemu. Foi assim que as versões do programas
para BC foram testadas por mim.
Este livro foi escrito em cima do gcc, Gnu C Compiler (o Compilador C da
Fundação para Software Livre, 2 Free Software Foundation, FSF). A FSF criou
também uma versão do gcc para rodar em DOS e em Windows de modo que se
você não tiver comprado um pacote de C, você pode obter um, gratuito, diretamente da FSF no endereço http://www.gnu.org procure “What we provide”e
você vai ser direcionado para os diversos programas que feitos sob o patrocı́nio
desta fundação, em algum lugar você vai encontrar djdev que é o nome do gcc
para DOS/Windows. .
O que é C ?
Há muitos mitos envolvendo a linguagem C, entre eles destacamos:
• É uma linguagem difı́cil. Você verá que é fácil iniciar o seu aprendizado em C. Mas seria uma mentira dizer-lhe que o conteúdo deste livro
é suficiente para que se torne um exı́mio programador. Prometemos que
ao término deste livro você poderá estar fazendo alguns programas interessantes e com muita vontade de continuar. Na bibliografia você vai
encontrar dicas de como fazer isto.
• É uma linguagem perigosa, você pode estragar o computador.
Não é perigosa, mas é poderosa. Se pode dizer, sem perigo de erro maior,
que tudo que roda hoje nos computadores foi feito em C ou foi feito com
alguma ferramenta, uma outra linguagem, que foi feita em C.
• E de fato, você pode, com facilidade, travar o computador com um programa errado. Você também pode deixar o sistema operacional confuso
gravando algum dado impróprio em local indevido de memória, mas na
pior das hipóteses a solução para o problema vai consistir em desligar a
máquina e depois ter cuidado com o programa que causou esta confusão.
Na absoluta pior das hipóteses, você pode ter que instalar tudo de novo,
se o seu programa houver se intrometido por cima do sistema operacional
2 GNU
é uma sigla que representa a FSF e também o nome de um tipo de antı́lope, “large
African antelope having a head with horns like an ox and a long tufted tail”, copiado do meu
dicionário gratuito produzido pelo Lab. de Ciências Cognitivas de Princeton.
SUMÁRIO
13
gravado no disco... mas para fazer isto só um programa bem avançado e
muito mal intencionado.
Claro, logo aqui no começo podemos dizer quem pode causar tais transtornos para que você aprenda a manipular com cuidado o vilão: são as
variáveis do tipo ponteiro porque elas fazem referência aos endereços das
variávei na memória RAM3 . Consequentemente, se você mandar escrever
dados muito grandes em uma variável de tamanho pequeno, haverá uma
invasão em áreas de memória e pode ser difı́cil de predizer as consequências
desta invasão.
É isto que se chama de endereçamento indireto . A maioria das linguagens de programação não usa este recurso, nelas você pode apenas fazer
endereçamento direto, usando o próprio nome da variável. Veja o seguinte
exemplo:
Exemplo: 1 Endereçamento indireto e indireto
numero = 23; // endereçamento direto
&numero ← 23;// endereçamento indireto
A primeira atribuição é a comum nas linguagens de programação, foi atribuido o valor 23 diretamente à variável numero. Na segunda linha estamos
dizendo que o valor 23 seja “associado”ao endereço de número.
Não é assim que se faz em C, veja o programa
endereco indireto.c,
mas não se preocupe com entendê-lo completamente agora. Estamos lhe
dizendo que olhe o programa apenas para contrabalançar as duas linhas
acima que não são reais em programação, apenas uma tentativa de transmitirlhe o que significa endereçamento indireto. No programa
endereco indireto.c
você pode ver como é que realmente se faz.
Em C também fazemos atribuições diretas de valores nas variávei, mas,
além disto, C pode acessar, quando você usar ponteiros, a memória de
forma absoluta, e aı́ se encontra o risco de que você mande escrever por
cima de alguma parte do sistema operacional, por exemplo...e neste caso,
com certeza a máquina vai parar. Desligando-a e novamente ligando, uma
versão nova do sistema operacional vai ser instalada a partir do disco e
tudo voltará ao normal. Este é o grande dano que obviamente deve ser
evitado e por isto primeiro entenda ponteiros antes de usá-los.
Mas aqui você irá aprender o que é um ponteiro, vai aprender a compreender o que pode estar acontecendo e dominar os poderes da linguagem.
Você sabe que pode levar um choque elétrico pegando de mal jeito nos
fios, mas nem por isso você prefere viver no escuro...
3 Random
Access Memory, é a memória que você adquire a mais ou a menos para sua
máquina e na qual os programas tem direito a fazer registros.
14
SUMÁRIO
Observação: 1 A função scanf() e o direcionador “&”.
O sı́mbolo “&” se chama algumas vezes “redirecionador”de memória, porque ele associa endereço e memória. Algumas funções da linguagem C fazem atribuição de
dados via endereço, é o caso da função scanf() e poristo ela representa um problema,
com frequência. Ela exige, um “direcionador de registro”, & na frente de algumas
variáveis. Sua omissão fará com o compilador o advirta do erro e se você não levar
a sério a advertência pode acontecer que com o valor lido seja colocado numa posição
de memória difı́cil de prever. Se o sistema operacional não tiver um bom controle do
uso da memória, e este é o caso do “windows”, isto pode levar a sobreposição de uma
variável do sistema e consequentemente a uma parada cardiáca violenta do mesmo...
mas em geral o “ctrl-alt-del”resolve o problema e o “windows”vai lhe brindar o disco
com um monte de lixo quando se re-iniciar.
Evite de esquecer o “&” antes das variáveis quando usar scanf. Mas não precisa se
assustar, o compilador que você estiver usando dentro, mesmo dentro do ”windows”,
lhe fará uma advertência se você esquecer um “&” na primeira etapa da compilação
do programa, tenha apenas o cuidado de levar a sério a advertência e corrija o esquecimento. Há outras funções que, como scanf() exigem o &. Tome o mesmo cuidado
nestes outros casos.
• Existem outras formas de copiar informações em lugares errados, elas serão
identificadas mais adiante, e todas estão ligadas ao uso indevido do endereçamento de memória. Um exemplo comum como utilização de uma
variável com tamanho maior do que deveria. Rodando programas em LinuX, o maior problema que isto pode causar consiste em deixar o programa
inconsistente e podendo travar indesejavelmente o que pode em geral ser
resolvido entrando n’outra “área de trabalho” e “matando” o programa
mal comportado.
A prevenção para este problema consiste no uso cuidadoso das variáveis
segundo a declaração das mesmas. Claro, é verdade, se espera de um
programador da linguagem C muita atenção no uso de variáveis.
O nosso objetivo consiste em deixá-lo em condições de escolher um dos caminhos seguintes:
• Se aprofundar em C para construir programas que executem tarefas difı́ceis
com esta linguagem, mas usando um outro livro, não este. Na bibliografia
você irá encontrar alternativas.
• Escolher uma outra linguagem, vamos lhe sugerir algumas, usando a experiência adquirida aqui. Queremos lhe dizer com esta segunda opção que
C pode ser uma linguagem introdutória antes de você se definir por uma
linguagem apropriada para o seu desenvolvimento, que pode ser em C, mas
há muitas outras para escolher. Ao final deste livro você deve se encontrar
no ponto de fazer esta escolha.
• Iniciar o estudo de C pelos seus aspectos de linguagem de alto nı́vel, deixando para o final os indicativos de como se aprofundar na linguagem.
E porque C é tão importante, mesmo que finalmente você vá programar em
outra linguagem? Algumas das respostas para esta pergunta são as seguintes:
SUMÁRIO
15
• A primeira é aquela que já mencionamos algumas linhas atrás, praticamente tudo que roda nos computadores hoje, ou é feito em C ou com alguma ferramenta que foi feita em C e, como consequência, por trás de tudo
isto sempre podemos encontrar as pegadas desta importante linguagem.
• Em geral, na solução de problemas computacionais se usa C como uma
linguagem final para escrever na forma definitiva os algoritmos que estão
rodando bem e sem erros e muitas vezes para escrever pequenos pedaços
crı́ticos do algoritmo, não o algoritmo todo. Quer dizer que se começa a
escrever numa outra linguagem que, por alguma razão, é mais apropriada,
e quando se conseguiu montar o algoritmo, funcionando, sem erros, se o
traduz para C ou pelo menos parte dele é traduzido para C.
• Outras vezes se escreve em C uma outra linguagem de alto nı́vel na qual se
produzem os programas. Neste caso, o que é comum fazer-se é, continuar
espandindo esta outra linguagem com novos módulos escritos em C. Esta
é, possivelmente, o uso mais comum da linguagem C.
• Seria um erro não mencionar aqui a estensão construida para C que se
chama C + +. Esta é uma nova linguagem mas que admite C como uma
sub-linguagem, quer dizer que você pode programar exclusivamente em
C + + mas você pode misturar as duas de uma forma conveniente. Outro
exemplo é Python que é uma linguagem um pouco mais nova que C + +
e que admite também C como uma linguagem de estensão. Mas aqui
teriamos que fazer uma lista com uma dezena de linguagens, ou mais,
para as quais isto é verdade.
• No ı́ndice remissivo você encontra uso de C, remetendo-o para outros pontos no livro onde mostramos pequenos exemplos de uso da linguagem
na construção de outras ferramentas. Não espere, obviamente, encontrar
nada revolucionário a nı́vel de um livro introdutório, como este...
Por todas estas razões é importante conhecer a linguagem C.
Por outro lado ela é fácil de se aprender e serve como uma primeira linguagem
de programação. É este o intuito principal deste livro: apresentar C como
uma primeira linguagem de programação. Por exemplo, é facı́limo escrever
programas em Português que rodem em C e seria um pouco mais difı́cil de fazer
o mesmo em qualquer outra linguagem de programação.
Observações e outros meios de comunicação.
O texto é completado com observações de dois tipos. Um dos tipos se chama
claramente “observação”, o outro são as notas de rodapé.
Você deve ler as observações na ordem em que elas aparecerem, mas sem
lhes dar muita importância numa primeira leitura.
16
SUMÁRIO
Para lhe permitir uma busca mais acurada de informações, o livro tem um
ı́ndice remissivo alfabético, ao final, em que todos os conceitos que surgem nas
observações se encontram indexados, de forma que você poderá facilmente retornar a eles quando achar necessário. Também se encontram indexadas todas
as palavras-chave do texto.
Quando falamos usamos encenação para completar o sentido das palavras
usadas no discurso: mexemos as mãos, o corpo e alteramos a entonação da
voz. Para suprir um pouco deste teatro usaremos uma convenção tipográfica:
texto em itálico representa material que você deve olhar com cuidado, possivelmente não está definido ainda e estamos usando a concepção intuitiva do termo.
Quando usarmos texto tipográfico estaremos fazendo referência a um termo
técnico já definido anteriormente ou considerado bem conhecido como tal. As
palavras da linguagem C serão escritas no estilo tipográfico. Quan-do usarmos letra pequena estamos lhe querendo dizer que o assunto é polêmico e que há
muito mais coisa para ser dito do que estamos conseguindo dizer naquele momento. Usamos texto sublinhado para chamar sua atenção de um detalhe que
poderia passar desapercebido, tem o mesmo sentido texto em negrito.
Existe alguma técnica para programar bem?
Bom, chamar de técnica é um certo exagero, mas o que vamos dizer agora
e repetir umas tantas vezes ao longo do livro, pode aos poucos se tornar numa
técnica de programação.
O segredo consiste em fazerem-se pequenos programas. Costuma-se dizer que
um programa nunca deve ser maior do que a tela do micro. É possivel programar
assim com as linguagens que temos hoje, porque elas são modularizadas, quer
dizer, um programa é um aglomerado de pequenos programas. Você vai aos
poucos entender o que queremos dizer, mas torne esta idéia uma obsessão: nunca
faça um programa que passe em tamanho da tela do micro.
O maior problema de um programador são os erros que teimam em se esconder, como “insetos”, no interior dos programas. Os americanos os chamam
de bugs. Quanto maiores os programas, mais lugar os insetos encontram para
se esconder, acredite. Quando o programa fica do tamanho da tela, a gente
consegue rapidamente detectar os “insetos” e então não é necessária nenhuma
técnica de dedetizaç~
ao para consertar programas defeituosos. Mais à frente
vou chamar sua atenção dos ambientes de programação com que você poderá trabalhar, eles estão equipados com instrumentos para fazer esta “dedetização”nos
programas.
Você pode muito bem viver sem estes “instrumentos” de análise de programas se aprender, desde o inı́cio, a programar bem, e, por outro lado, se
o seu programa for ruim, nem elas adiantam muito... é tão difı́cil consertar
um programa mal feito, que é mais fácil re-aprender a programar e fazer outro
programa.
Parte I
Usandos os comandos em
Português
17
Capı́tulo 1
Uma primeira suite de
programas
1.1
Como rodar um programa.
D
epende do que você dispõe como ambiente de programação.
Infelizmente alguns ambientes tem mais o objetivo de se apoderarem do
usuário do que ajudá-lo a ser um indivı́duo livre e criativo. Mas, se você comprou este livro, então você quer ser livre e criativo, logo se prepare para descobrir
as coisas por si próprio e conte com algum auxı́lio por parte deste livro, mas não
espere que o livro seja uma muleta para quem não quer superar as suas próprias
dificuldades. Use o endereço eletrônico do autor1 para tirar algumas dúvidas,
mas faça isto de forma moderada. Discuta com outros colegas que já dominam
um pouco assunto, este é certamente a melhor forma de evoluir em qualquer
ramo do conhecimento: trabalho em equipe.
Vamos discutir alguns ambientes de programação, para ser franco, três ambientes: C da Borland, C da Microsoft, e o C da Fundaç~
ao GNU dentro de
um ambiente LinuX.
Vamos dar discutir com mais atenção o primeiro, C da Borland, que é considerado por todos trabalham como esta linguagem como o melhor existente para
Windows. Também existe um ambiente mais antigo, ainda em franco uso, que é
Turbo C. O que dissermos sobre o C da Borland vale muito aproximadamente
para Turbo C.
O ambiente do C da Microsoft segue os padrões habituais de ambientes
gráficos dentro do Windows, de formas que, se você estiver acostumado a trablhar dentro deste sistema, rapidamente poderá adaptar o que dissermos sobre
C da Borland para o ambiente da Microsoft.
Observe, entretanto, que este livro não é um manual para estes ambientes,
e sim um livro para ensiná-lo a programar em C, portanto a nossas discussão
1 [email protected]
19
20
CAPÍTULO 1. UMA PRIMEIRA SUITE DE PROGRAMAS
sobre ambientes de determinados pacotes, tem que ser breve. Além do mais,
todos estes pacotes computacionais tem manuais que lhe poderão apresentar
suas possiblidades de forma muito mais efetiva do que nós poderiamos fazer
aqui.
A melhor forma para dominar estes ambientes integrados de programação
consiste em gastar algum tempo descobrindo a funcionalidade dos botões que
eles oferecem. Você poderá fazer isto com tranquilidade e sem o menor receio de
estragar o sistema, porque ele foi feito para ser usado. A pior coisa que poderia
acontecer seria que você apagar algum programa gravando outro por cima, e isto
com certeza vai acontecer em algum momento, portanto é melhor que aconteça
logo no começo quando o prejuizo ainda será pequeno... Portanto perca algum
tempo experimentando os botões do ambiente integrado.
Os dois outros ambientes serão apenas citados, se você não tiver opção para
trabalhar em LinuX, deverá completar o conteúdo deste livro com o manual
do C correspondente, mas fique tranqüılo, as diferenças são pequenas e são
importantes apenas no começo.
1. O ambiente BC Suponhamos que você esteja no Windows e que esteja
usando o BC, Borland C. Como já disse, gaste algum tempo para reconhecer o ambiente integrado2 do BC, o IDE3 . Ele se chama assim, ambiente integrado, porque lhe oferece um atelier onde produzir os programas,
guardá-los, e automaticamente rodá-los. Observo que você também corre
o risco se tornar excessivamente dependente do ambiente integrado, procure evitar esta dependência, e faça um uso inteligente do ambiente, aos
poucos você mesmo verá o que esta advertência significa.
Assim que você tiver gasto uns quinze minutos experimentando o ambiente
integrado, passe para o quarto item desta lista.
Ao estabelecer quinze minutos estamos exatamente querendo lhe dizer que
não procure entender tudo que se encontra à sua disposição dentro do ambiente integrado, e você logo vai ver que, se aprender a programar corretamente, muitas das “ferramentas”disponı́veis são inúteis, e, pelo contrário,
se você vier a precisar delas isto significa que estará programando mal....
e aı́ será preciso, de fato, usar estas ferramentas.
Parte do que há disponı́vel no ambiente integrado só lhe será útil mais a
frente, quando seus programas ganharem mais densidade e já estiverem
caminhando na direção de um projeto.
Digamos que, no momento, o mais importante é aprender a
• abrir um arquivo, (código fonte), encontrar um arquivo no disco; Para
isto use o botão File. Experimente agora, clique no botão e vai cair
um menu com
(a) new, (novo) se você quiser começar um novo programa. Nunca
faça isto! Entre os meus programa tem um que se chama esqueleto.c,
2 muito
3 IDE
semelhante ao ambiente do Turbo C
- Integrated Development Environment
1.1. COMO RODAR UM PROGRAMA.
21
comece abrindo este programa para não começar do zero... Crie
um esqueleto.c para você.... Veja abaixo o que você pode fazer
com “esqueleto.c” !
(b) open para você abrir um programa existente no disco. Você pode
indicar o caminho onde o BC deve procurar o arquivo;
(c) save para você gravar o programa que estiver escrevendo, observe que basta acionar F2
(d) save as, (gravar como), para você escolher um outro nome de arquivo onde gravar o programa. Use esta opção com o “esqueleto.c”.
Abra esqueleto.c e o grave com o nome que desejar. Você já
terá o novo programa na sua frente depois que fizer isto. Experimente, abra “esqueleto.c” e o grave como “teste.c”.
(e) change dir é para mudar diretório, provavelmente pouco útil no
começo.
(f) print para enviar para a impressora uma cópia do programa que
estiver na tela.
(g) DOS shell para usar o DOS, pouco útil para os usuários do windows...
(h) quit quando você quiser ir embora...
• procurar uma palavra num arquivo e trocar palavras erradas e isto
você vai fazer com o botão search, (procura). Nos editores de texto,
em geral isto se faz com o botão edit, aqui não. Se você quiser
traduzir para o inglês os nosso programas, vai usar este botão. Nele
tem
(a) find para procurar uma palavra.
(b) replace para procurar e trocar palavras. Vão aparecer dois
campos, no primeiro para você indicar qual a palavra que deve
ser trocada, no segundo, o que a deve substituir.
Há várias opções para você ligar ou desligar sensı́vel à maı́uscula,
palavras completas, express~
oes regulares, pergunta ao trocar,
forward (pra frente), backward (pra trás), from cursor (a partir do cursor), entire scope (no documento todo), OK, change
all (muda tudo), cancel e help...
(c) go to line number (vai para uma linha de número), e espera
que você indique o número da linha.
• rodar o programa guardado num arquivo. Você vai usar o botâo run.
Quando clicar cai um menu contendo
(a) run que vai rodar o programa
(b) program reset, botão importantı́ssimo.
Quando você tiver rodado um programa e, depois, fizer modificações, se pedir para rodar, o BC vai rodar o anterior.
Clique no program reset - (renova o programa), e depois no
run.
Este é um erro comum, se você alterar um programa e tudo voltar
acontecer como antes, se lembre de fazer o reset.
22
CAPÍTULO 1. UMA PRIMEIRA SUITE DE PROGRAMAS
• compilar um programa. Você vai fazer isto com o botão compile.
Clique no botão e ao cair o menu, escolha compile.
• configurar os diretórios de trabalho Se você tiver instalado o pacote
usando o instalador então não tem porque se preocupar com este
item. Se sua instalação não for a padronizada você corre riscos de
que o C da Borland não encontre os arquivos necessários. Neste caso
você deve ir ao botão Options, lá escolher a Directories (diretórios)
e registrar cuidadosamente a árvore dos diretórios onde se encontram
os arquivos. Veja na figura (fig. 1.1) página 22, como se encontra
Figura 1.1: árvore de diretórios - BC
organizada a árvore de diretório no meu micro de trabalho. É preciso indicar o disco e todo o caminho anterior aos diretórios que BC
procura:
BGI BIN OUT BINOUT INCLUDE LIB
Ao abrir o item Directories você já deve encontrar uma seção de
arquivos. Veja se tudo está de acordo com sua instalação.
• help
C da Borland tem um auxı́lio (help) muito bom que é preciso aprender a usar. Infelizmente não irá funcionar com os programas escritos
em português. Experimente o programa4 primeiro01.c. O comando
4 os
programas para BC ganharam nomes mais curtos, em vez de primeiro01.c, procure
prim01.c
1.1. COMO RODAR UM PROGRAMA.
23
inicial do programa é clsscr(); que serve para limpar a tela. Coloque o cursor sobre esta palavra e aperte ctrl-F1. O resultado
é uma pequena5 janela com informações especı́ficas sobre este comando. Aprenda a fazer uso destas informações, elas são um manual
da linguagem on-line. Para sair do help, acione a tecla ESC.
Se você apertar F1 virá o manual do C da Borland. É um conjunto
de várias janelas descrevendo toda a linguagem. Eu não poderia
deixar de sugerir que você se habituasse a ler este manual on-line.
Infelizmente em inglês, mas se você não se acostumar a, pelo menos,
ler em inglês, ficará cortado de grande parte das informações tecnicocientı́ficas. Você sai do manual acionando a tecla ESC.
2. O ambiente C da Microsoft Se você estiver o usando o Microsoft C,
também você vai dispor de um ambiente integrado bem parecido com o
ambiente do Borland C.
Leia o item anterior e gaste uns 15 minutos para ganhar experiência com
o Microsoft C e depois passe para o quarto item desta lista. O objetivo
principal é carregar um programa para dentro do editor de textos e depois
rodá-lo. Valem as mesmas observações que já fizemos sobre a configuração
da árvore de diretórios. Nenhum sistema operacional pode advinhar onde
se encontram os seus programas ou os arquivos de dados, tudo isto tem
que ser registrado nas opções.
Como sempre, o ideal é instalar os pacotes usando o programa apropriado
para isto, ele se ocupará de toda a configuração básica. Não fazer isto é
querer dores de cabeça. Se você for um usuário experiente, poderá, possivelmente, brincar com a configuração, caso contrário use os instaladores.
3. Em ambiente Linux Em geral ninguém instala Linux manualmente6 ,
tudo é feito por um instalador que vem junto com a distribuição adquirida. Estes instaladores, habitualmente, deixam a linguagem C instalada
corretamente, até mesmo porque C é a linguagem natural para Linux, de
modo que, tudo que você tem que fazer é trabalhar com seus programas
no seu diretório pessoal.
Em LinuX você conta com diversos ambientes integrados, por exemplo,
wpe, xwpe, xcoral, xemacs ou o espartano joe, para citar alguns.
Em alguns deles você deve indicar o modo com que deseja trabalhar, o
modo C . O xemacs entra no modo C automáticamente se você abrir um
arquivo com extensão “.c”. Se o xemacs estiver bem instalado você pode
contar com um ambiente integrado muito poderoso lhe oferecendo inclusive
uma ajuda “on-line” sobre os conceitos da linguagem. Aprenda a usar o
ambiente integrado que você tiver escolhido. Se você tiver paciência para
usá-lo, terá uma poderosa ferramenta nas mãos.
5 se
não funcionar, coloque o cursor sobre a palavra, clique no botão help e, no menu que
cair, escolha topic search; procure help no ı́ndice remissivo
6 nem Linux, e nem nenhum outro sistema complexo
24
CAPÍTULO 1. UMA PRIMEIRA SUITE DE PROGRAMAS
Se tiver escolhido joe aı́ você vai penar um pouquinho mais, mas foi porque
você mesmo quis...eu uso o joe e me dou muito bem. joe é um editor
de textos muito poderoso mas difı́cil de usar pois é orientado a comandos
no padrão dos editores da Borland, do Turbo C ou do Turbo Pascal, ou
ainda do “wordstar” dos anos 80 que foi um poderoso ambiente de edição
para aquela época.
Ainda existe uma outra possibilidade, seria usar o vi, mas se esta for a sua
escolha, então, fora de dúvidas você pertence a facção mais xiita do Unix
e eu não tenho dúvidas de que você está lendo este livro somente para sacan(*) o autor... porque certamente você deve ser um exı́mio programador
em C.
4. Começando a trabalhar Suponhamos que você já tenha ganho experiência com o ambiente integrado de sua escolha. Abra agora uma janela de
edição. Escolha o primeiro botão à esquerda onde estiver escrito “FILE”ou
“ARQUIVO”.
Se o ratinho7 não estiver disponı́vel, você pode chegar ao menu superior
com F108 e depois manipular os botões do menu usando as setas para
esquerda, direita ou para baixo.
Clique com o ratinho, (ou com a seta para baixo) e escolha “open” ou
“abrir”, para abrir um dos programas que você já deve ter gravado no
HD. Este inı́cio será semelhante em todos os ambientes. Escolha o primeiro programa abaixo e rode-o. Os manuais que acompanham BC ou
Microsoft C trazem programas bem elementares do tipo “primeiro.c”e
lhe informam como rodá-lo. É tudo que você precisa para começar, depois
aos poucos você irá aprendendo como usar melhor o ambiente até o limite
do necessário, (ou se preferir, fique um expert no uso destes ambientes...).
5. Em LinuX a coisa pode ser tão simples como escrever numa ”shell”9
gcc -v primeiro.c -oprimeiro
ou
gcc -Wall primeiro.c -oprimeiro
em que
• gcc é o nome do compilador produzido e distribuido pela Fundação
GNU, “g”indica isto, “cc”signfica “compilador c”.
• -v é sua solicitação de “verbosidade”, você deseja que o compilador
lhe diga o que fizer. Evite esta opção no começo, para não se afogar
nas informações que lhe serão apresentadas. Use -Wall em vez de
-v.
7 também
chamado mouse....
alguns casos será com F9, ESC ou TAB, sem dúvida, é melhor garantir que o ratinho
funcione...
9 uma área de trabalho
8 em
1.1. COMO RODAR UM PROGRAMA.
25
• -Wall Parecido com -v, mas lhe apresenta apenas as reclamações
mais importantes, muito bom para quem se inicia.
• primeiro.c é o nome do arquivo-fonte10 .
• -o é a opção de compilação que indica qual é o nome do arquivo que
deverá ser criado. Neste exemplo escolhi primeiro.
Em princı́pio o Borland C aceita alguma coisa do tipo:
bc primeiro.c
para fazer o que descrevemos acima, produzindo um arquivo chamado
primeiro.exe. Porém, você terá dificuldades com o caminho para que
BC encontre o programa que você deseja compilar. A forma mais simpels de usar Borland C é mesmo dentro do ambiente integrado onde você
facilmente configura os diretórios em que se encontram os seus programas.
Vamos supor o uso do ambiente integrado11 . Abra um programa, prim01.c,
por exemplo. Com o programa visı́vel na janela de edição, clique no botão
RUN e seu programa será compilado e em seguida executado. Os programas
deste livro podem ter12 um defeito que será preciso corrigir, para que eles
funcionem bem em C da Borland. Vamos descrever o “problema” e a
solução:
• Quando um programa termina de ser executado, dentro do ambiente
integrado, o ambiente automaticamente retorna ao texto do programa
(código fonte). Conseqüência, você pode não ver o resultado do programa. Para programa pequenos, como os nossos primeiros programas, você pode ficar com a sensação de que nada aconteceu...
• De fato isto não é um problema, você logo verá que é uma vantagem,
porque assim o ambiente o trás de volta ao texto do programa (código
fonte) colocando uma marca vermelha em cima de algum erro que o
compilador tenha encontrado.
• Mas se não houver erros, é decepcionante... e você poderá evitar acrescentando no programa, antes do comando return13 , o comando getchar() Tentamos incluir em todos os programas o comando pausar() que é uma redefinição do getchar(), mas poderemos ter esquecido em algum caso. Se não acontecer nada, verifique
ao final do programa se não falta pausar() ou getchar().
Dentro do Windows/DOS, um arquivo só pode ser executado, se terminar
com as extensões
.exe .com .bat
10 arquivo
fonte é o arquivo em que se encontra o programa que você escreveu
11 IDE
12 tentamos
eliminar este problema, mas ainda pode ter ficado em algum programa, você
deve, então ser alertado
13 que por regra, todos os programas em C devem ter como última instrução
26
CAPÍTULO 1. UMA PRIMEIRA SUITE DE PROGRAMAS
Em LinuX não é o nome que indica isto, mas alguns dados internos do arquivo e gcc se ocupa disto, ”primeiro", compilado com as intruç~
oes
descritas acima, será um programa executável.
Observação: 2 Erros e problemas
Problemas ao usar BC. Observe que, ao enumerar problemas, não estamos
sugerindo que um pacote é de baixa qualidade. Não existem grandes programas,
sem erros, a não ser os programas mais mais simples. O defeito se encontra em
esconder os erros. Isto é de fato indecente. Este livro tinha muitos erros que
foram corrigidos com auxı́lio de leitores amigos.
Dificilmente conseguiriamos listar todos os problemas, mas indicaremos alguns mais importantes no momento apropriado, como agora.
Quando você rodar um programa, se fizer uma alteração no mesmo, observe o
item program reset (re-inicialização do programa) dentro do menu RUN. Com
freqüência o compilador guarda na memória a versão anterior do programa.
Eis a razão deste botão. Aperte o botão antes de voltar a compilar ou rodar o
programa.
Se você ainda não apertou em todos os botões do ambiente integrado que
estiver usando, faça-o agora, para pelo menos ver o que eles contém.
Se você não alterar nada, não irá estragar nada, também...olhar não faz
mal14 .
Vocabulário: 1 compilar, compile, executável, rodar, run
• compilar em inglês, compile, é uma das funções do “compilador”, fazer
uma análise sintática do código fonte para verificar se as regras (sintaxe)
da linguagem de programação foram todas respeitadas. Se isto for verdade,
o compilador cria um executável.
• executável é um arquivo que o sistema operacional considera que pode fazer algum processo, produzir um resultado. Este é o objetivo principal dos
programas de computador...este conceito se opõe ao de código fonte. Ao
criar um executável, este programa poderá ser executado em outro computador, que roda o mesmo sistema operacional. Se você tiver compilado
com o BC, poderá rodar o programa em qualquer computador sob Windows.
Se você tiver compilado com gcc, poderá rodá-lo em qualquer computador
sob Linux15 .
• código fonte é o texto que você escreveu como programa e gravou em um
arquivo no disco. Ele precisa ser compilado para que seja gerado um executável que o sistema operacional irá permitir que rode.
• rodar, em inglês run, executar um programa.
Agora vamos apresentar-lhe um bloco de exercı́cios. Você não conseguirá
aprender nada sem fazer exercı́cios, muito menos poderá aprender a programar
14 algumas
15 existe
vezes, talvez...
um compilador gratuito, djdev para DOS/Windows, veja no ı́ndice remissivo
1.1. COMO RODAR UM PROGRAMA.
27
sem fazer exercı́cios de programação16 . A grande maioria dos programas deste
livro, são exercı́cios. Os programas “dialogam” com você pedindo que você
volte a ler o programa e corrija erros que deixamos nos programas. Em geral o
programa seguinte contém a correção de um erro, e mais outro erro...
Exercı́cios: 1 Os primeiros programas
O objetivo deste bloco de exercı́cios é a compreensão do programa
primeiro.c
Vamos então pedir que você
rode e leia,
nesta ordem, os programas primeiro01.ca , primeiro02.c, ...
primeiro07.c que irão desembocar em primeiro.c que se encontra editado a seguir.
a para
DOS,Windows, use os programas com nomes curtos, prim01.c,
prim02.c,. . . porque primeiro01.c se confunde com primeiro02.c
1. Rode e depois leia primeiro01.c.
gcc primeiro01.c -Wall -oprog
./prog17
2. Volte a a rodar e ler primeiro01.c procurando entender cada linha de
comando do programa. Em particular leia os “comentários”, o texto inicial
do programa, e veja como ele se encontra destacado do corpo do programa.
Analise o objetivo dos comentários, inclusive o registro de que o programa
contém erros.
3. Corrija o comentário de primeiro01.c indicando qual é o erro o programa
comete: a falta de \n.
4. Rode e depois leia primeiro02.c.
gcc primeiro02.c -Wall -oprog
./prog
Altere o programa, como ele mesmo sugere, e tente compilá-lo, analise a
mensagem de erro. Experimente apagar alguns “ponto-e-vı́rgulas”, compile e analise a mensagem de erro. A falta de um único “ponto e vı́rgula”
pode gerar uma imensidão de mensagens de erro. Experimente, apague18
um “ponto-e-virgula” e compile o programa. Esta é uma dificuldade que
os compiladores têm, antes se perder na leitura de uma enxurrada de mensagens de erro verifique se não falta um simples ‘ponto e vı́rgula”.
5. Rode o programa primeiro03.c. Ele mente, corrija-o.
16 repetiremos mais ainda algumas vezes esta observação, para conscientizá-lo de que não
será apenas lendo, que aprenderá a programar
17 se o sistema estiver bem instalado não será necessário “./”, bastará “prog”...
18 você não precisa apagar, basta colocar // na frente
28
CAPÍTULO 1. UMA PRIMEIRA SUITE DE PROGRAMAS
6. Rode os programas primeiro04.c, primeiro05.c e faça o que eles sugerem.
7. Leia e rode o programa primeiro06.c. Este programa é um contraexemplo. Ficou muito grande, saiu da tela. Veja como ele é extranho,
tem uma linha de número 12 que aparece várias vezes, isto é um sintoma
de imperfeição...
8. Melhore a redação no programa primeiro07.c, tem letra maı́scula depois
de vı́rgula, e precisa de algumas mundaças de linhas... mas tome cuidado
para que o resultado não fique ilegı́vel...
9. Um exercı́cio importante: como re-utilizar um programa. Escolha algum
dos programas que você acabou de usar, algum que lhe tiver parecido especial. Grave-o com outro nome, por exemplo teste.c19 . Em Linux será
o mesmo se você estiver usando alguma IDE, ou numa,20 shell, digite
cp primeiro01.c teste.c
Agora você tem o mesmo texto de primeiro01.c (ou prim01.c) dentro
do arquivo (ainda não gravado) teste.c. Rode este novo programa depois
que você nele faça algumas modificações que lhe pareçam interessantes.
Este é o método que nós, programadores, usamos para construir novos
programas...nunca começamos do zero.
No último exercı́cio lhe contamos o segredo de como escrever novos programas. Deixe-me contar-lhe outro. Entre os programas que você recebeu estão
dois iguais, esqueleto.c e padrao.c. São este programas que usamos para
começar a escrever outro.Crie os seus, com os seus dados, e com as estruturas
de programação que considerar básicas.
O texto abaixo é uma cópia do arquivo primeiro.c que você tem no disco.
Nele não há os sinais de acentuação da lingua portuguesa, é um programa de
computador e não um texto em nossa lingua.
/∗ Programa primeiro.c
Assunto: Escreve algumas frases e recebe uma informacao pelo teclado.
Programa com erros na saida de dados, nao imprime o que se espera.
COMENTARIO
Este texto inicial do programa eh um COMENTARIO e
se encontra demarcado com ”barra+asterisco”no
inicio e depois ”asterisco+barra”ao final.
Programa define a variavel ’coisa’ como um vetor de caracteres
com 30 coordenadas.
por Tarcisio Praciano Pereira - 10 licoes para aprender C
Sobral, julho de 2001 - UVA
∗/
// isto aqui tambem eh um COMENTARIO
19 no
BC escolha “save-as” e use a caixa de diálogo que aparece para, nela, escrever teste.c
de trabalho
20 área
1.1. COMO RODAR UM PROGRAMA.
29
#include < stdio.h >
#include ”traducao.h”
principal()
inicio
palavra coisa[30]; // variavel
imprima(”%s\n”, ”Escreva uma frase no teclado, ”);
imprima(”%s\n”, ”pode ser o seu nome, por exemplo: ”);
ler(”%s”, coisa);// leitura de dados pelo teclado
imprima(”%s\n”,-————————————–”);
imprima(”Voce escreveu: %s %c\n”, coisa,’ ?’);
imprima(”%s\n”,-———————————–”);
imprima(”%s\n”,”Agora leia o programa para ”);
imprima(”%s\n”,”acompanhar a critica que vai ser feita.”);
imprima(”%s\n”,-————————————–”);
imprima(”%s\n”,”Observe que ’ler’ nao obedeceu a regra”);
imprima(”%s\n”,”de uso do direcionador & de enderecos,”);
imprima(”%s\n”,”como anunciamos no texto....”);
voltar 0; // todo programa deve terminar com este comando
fim
Exercı́cios: 2 Análise de primeiro.c
1. Rode o programa primeiro.c e depois o leia.
2. Ao compilar primeiro.c, você recebeu uma advertência:
primeiro.c:22: warning: return-type defaults to int
porque foi omitido o tipo de principal() e ao final o valor devolvido é
zero. Um conflito. Corrija isto, dado um tipo para principal():
inteira principal()
A advertência se compõe de três partes, separadas por “dois pontos”que é
o “separador oficial do Unix:
• primeiro.c o nome do programa;
• 22 a linha em que o erro foi detectado;
• warning, um aviso. Se o erro for mais grave será error. É o tipo
de dados da função principal() que não foi fornecido.
Em C todas as funções ou variáveis tem que ter um tipo.
3. Rode o programa primeiro.c digitando o seu nome sem espaços.
4. Digite uma seqüência de mais de 30 caracteres como resposta ao programa,
e analise o resultado.
30
CAPÍTULO 1. UMA PRIMEIRA SUITE DE PROGRAMAS
A solução para que o compilador deixe de reclamar contra primeiro.c é
definir
inteira principal()
Deixamos este erro ficar de propósito, para justificar esta observação. Ao mesmo
tempo acrescentamos: somente na segunda parte é que discutiremos a fundo a
questão do tipo de dados a que se encontra afeto este problema.
Infelizmente vamos ter que trabalhar com tipos de dados antes de conseguirmos explicar tudo direitinho eis a razão do erro ter ficado. Afinal, erros são
parte do aprendizado...
Se você quiser avançar este assunto, veja o capı́tulo 7 onde discutimos tipos
de dados, você deve, agora, fazer uma leitura rápida daquele capı́tulo para se
inteirar desta noção.... e voltar logo para cá.
Você está aprendendo a programar em C, com os comandos traduzidos para o
Português, e os programas em português estarão rodando...
No próximo capı́tulo trabalharemos com a suite de programas “prog*.c”...
lá discutiremos esta notação extranha, formatadores de dados que aparecem
dentro da função imprima(). Mas pode fazer uma leitura rápida do capı́tulo 2
agora.
Exercı́cios: 3 Alterando primeiro.c
Os dois últimos exercı́cios desta lista estão fora do contexto, e portanto são
difı́ceis para o iniciante. Eles se eoncontram aqui apenas para mostrar alguma
coisa do que pode ser feito com C.
1. Altere o texto dentro da função imprima(), por exemplo, mande escrever
o seu nome.
2. Altere substancialmente o texto de todas as funções imprima() no programa, por exemplo, mande escrever os nomes dos seus amigos e amigas.
3. Altere o texto das funções imprima do programa, por exemplo, para escrever uma pequena lista telefônica. Pode ser a sua lista telefônica particular...
4. Você viu que um programa pode colocar um texto na tela, altere primeiro.c
para colocar um texto seu na tela do computador, por exemplo, um lembrete sobre as coisas que você deve fazer no dia. O executável assim criado,
pode ser incluido no autoexec.bat do DOS/Windows e rodar sempre que
você ligar o computador.
1.1. COMO RODAR UM PROGRAMA.
31
5. ** fora do contexto Rode o programa agender01 p.c e depois o leia:
gcc -Wall -oprog agender01 p.c
./prog
A primeira linha serve para compilar o programa, quer dizer, pedir ao gcc
que crie um programa executável a partir do código fonte. A segunda linha
é para executar o arquivo executável prog.
6. ** fora do contexto Se você quiser compor sua lista telefônica em disco,
veja agender01 p.c. Tente modificar o programa, sem se preocupar com
entendê-lo, e faça sua agenda. Se precupe apenas com alterar as mensagens dentro das funções imprima(), imprime arq(). O arquivo produzido pelo programa poderá ser lido, e editado, depois, com qualquer editor
de textos e enviado para a impressora.
32
CAPÍTULO 1. UMA PRIMEIRA SUITE DE PROGRAMAS
Capı́tulo 2
O segundo programa em C
2.1
Programas e erros...
Observação: 3 Regras de trabalho
• Vamos sempre começar por lhe apresentar um programa;
• você deve digitá-lo1 num editor de textos2 ;
• depois rode o programa com o compilador que estiver à sua disposição
e em seguida leia os comentários que faremos e também os comentários
feitos pelo compilador, sobretudo porque você pode ter esquecido de digitar
algum direcionador de memória & ;
• tome a iniciativa de fazer alterações nos programas usando a experiência
que for adquirindo, mas grave-os com nomes diferentes; Aqui você vai adquirir experiência sobre um erro muito comum: perder programas porque
gravou por cima algum outro programa. Esta é uma dor de cabeça comum a todos os programadores. Tenha por hábito fazer backup, cópia de
reserva, dos seus programas. Tenha o cuidado de gravar a alteração de
um programa, com outro nome:
prog01.c prog02.c ...prog101.c
e você vai sempre encontrar o mais recente, ou algum mais antigo que
funcionava tão bem. . .
• aos poucos deixaremos de transcrever os programas no livro, é mais prático
que você leia os programas com um editor de textos, inclusive quando os
rodar, é interessante tê-los numa tela ao lado.
1 Todos os programas do livro se encontram distribuidos em disco para economisar-lhe a
digitação. Basta “carregá-los” para o editor.
2 grave os programas no modo texto, (sem acentos) Se você estiver usando alguma IDE
(ambiente integrado) o editor é próprio para programação. Se estiver usando algum editor
como word, tome o cuidado para gravar os programas no modo texto.
33
34
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
Exemplo: 2 O primeiro programa
/* Programa prog01.c
Assunto: le uma palavra pelo teclado e a imprime
por Tarcisio Praciano Pereira - 10 licoes para aprender C
Sobral, julho de 2002 - UVA
*/
# include < stdio.h > // (1) leitura da biblioteca stdio.h
# include ”traducao.h”// (2) leitura da biblioteca traducao.h
inteiro principal() // (3) inicio do programa, tipo palavra
inicio
palavra coisa; // (4) declaracao de variavel
imprima(”%s\n”,
”escreva alguma coisa pelo teclado, palavra, numero...”); // (5)
ler(”%c”,&coisa); // (6)
imprima(”%s%c\n”,”O primeiro caracter da coisa foi −− > ”,coisa); // (7)
imprima(”%s\n”,”==========================”);
voltar(0); // (8)
fim // (9)
Comentários do programa.
Os comentários são parte integrante de um programa e de forma alguma
devem ser considerados um “apêndice extra perfeitamente dispensável”. Um
programa é uma peça de abstração, escrito em linguagem técnica, em geral
muito conciso, e, consequentemente, difı́cil de ser lido. Os comentários vêm
suprir informações complementares e tornam o programa legı́vel.
Comentários podem ser caracterizados de duas formas:
• Com duas barras, “//”. O compilador ignora o que venha depois até o
final da linha.
• Entre os sinais ’/’ e ’*’ no começo, e revertidos ao final ’*’ e ’/’. Veja
logo no inicio do programa.
Quando se vai escrever um comentario longo, este segundo metodo,é o mais adequado. Pequenos comentários, como acima, depois de um comando, é preferivel
usar o primeiro metodo. Mas a decisão e o estilo são seus. Há programadores
que usam colunas de asteriscos no inı́cio e no final de cada linha de um bloco
de comentários para torná-lo mais ostensivo. Vale tudo, desde que você não se
atrapalhe (nem atrapelhe os outros) com a poluição visual...
Os comentários servem para explicar o programa (inclusive para o próprio
autor...) ou também como parte do planejamento do trabalho. No inicio do
programa os comentários dizem o que o programa faz, quem fez o programa,
as modificações que se pretendem fazer nele, os defeitos que ainda existam, etc...
2.1. PROGRAMAS E ERROS...
35
Os comentários que fizemos usando “//” tem uma numeração que vai servir
de referência para uma seção de observações que costumamos fazer ao final dos
programas. Veja, por exemplo, musica.c, não tente compreender o programa
agora, veja apenas como estamos usando os comentários numerados. E, claro,
você pode rodar e ler o programa, mas observe (leia o programa) ele necessita
que no sistema exista um programa chamado bell que acione o alto-falante
do computador. Troque bell pelo nome certo. Se este programa não existir
nada vai acontecer.
1. A linguagem C, como toda linguagem moderna, é expansı́vel, quer dizer,
você pode criar novos comandos, são as funções. Cada função é um novo
comando. Estes comandos novos ficam com freqüência dentro de arquivos
chamados ’bibliotecas’. O programa começa lendo a biblioteca padrão do
C para Input/Output - < stdio.h > Entrada/Saida. Leia também a nossa
biblioteca, traducao.h, em que fizemos as traduções dos comandos da
linguagem C. As bibliotecas são arquivos com a extensão “.h” e ficam
colocados em diretórios especı́ficos que o gcc sabe quais são. Quando
quisermos incluir uma biblioteca nossa, como “traducao.h”, temos que
usar aspas em volta da biblioteca e então gcc vai procurá-la no diretório
de trabalho, ou no diretório indicado pelo caminho que indicarmos:
# include "/home/meu nome/C/minha biblioteca.h"
2. erro grave concluir da observação anterior que você pode construir uma
linguagem C especial para você, com seus próprios comandos. É verdade,
mas seria inútil. Linguagens, mesmo de computador, existem para que
as pessoas se comuniquem. O conhecimento é social e não individual.
Só podemos ser avançados na medida em que o grupo social o seja junto
conosco. Não teria sentido criar o seu C! Mas tem sentido pensarmos em
programar em Português, aqui no Brasil, e seguir entendendo programação
em Inglês.
3. Exercı́cio: Experimente! Apague
# include < stdio.h >
e rode o programa:
gcc -Wall prog01.c -oprog.
Como resultado você vai receber a informação que printf(), scanf()
estão sendo usados pela primeira:
prog01.c:14:
warning: implicit declaration of function ‘printf’
O gcc vai ignorar, pedantemente, a nossa tradução ”imprima”e vai lhe falar de ”printf”fazendo o mesmo com ”scanf”. Porque nós não traduzimos
o compilador.
4. estrutura de um programa Há dois tipos de funções num programa:
36
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
• main() A função “principal()”,
• e as outras que a “principal()”chama.
Todo programa tem que ter uma função principal() e depois deve ter
outras funções que executam tarefas especı́ficas, tarefas auxiliáres.
Neste programa a função “principal()”chama apenas outras funções que
se encontram definidas na biblioteca ’stdio.h’. Portanto as outras funções
podem já existir em alguma bibilioteca e inclusive podem já ter sido usadas
e testadas por outro programa (melhor ainda).
5. Cada função é um pequeno programa. Neste sentido a linguagem C já
nasceu “moderna”, no espirito de programação modular. Um programa se
constitui essencialmente de sua função principal que irá colocar em ação
os demais atores, as outras funções, que foram feitas sob-medida para
executar pequenas tarefas especı́ficas. Assim, um grupo grande de funções
pode existir para compor, quando necessário, um determinado programa.
Isto se chama hoje reciclagem de programas ou em inglês, re-use of
programs.
6. Variáveis, funções, tem que ter um tipo. A sintaxe da declaração de
variáveis é
< tipo dados >< nome da variavel >;
Pode haver várias variáveis do mesmo tipo na mesma declaração, separadas por vı́rgulas.
7. leia acima... A linguagem C não distingue variáveis e funções em primeira
instância. Inclusive o compilador, quando encontrar erros, vai se referir
às variáveis como funções.
8. A função ’imprima()’ (printf()) exige que comecemos dizendo que tipo
de dados lhe fornecemos para imprimir: ”%s”, quer dizer que vem uma
’frase’, (string), para ser impressa. O sı́mbolo “\n” é um ’comando’:
mudança de linha. Você verá depois que “\n” não é um comando, agora
adianta pouco discutir esta diferença semântica.
9. ’ler()’ é a tradução de ’scanf()’ que é um comando muito rápido e pode
conduzı́-lo a erros. Use “leia()”(fgets()) como você verá nos próximos
programas. Usamos “ler()”para que o programa ficasse simples, mas é um
defeito.
Warning, Warning, Warning, Warning....!!!!
Rode prog04 2.c3 para ver o risco do scanf(). Leia os comentários,
dentro do programa.
10. Aqui ’imprima’ tem dois formatadores de dados, %d para inteiros, e %s
para frases (strings).
3 no
diretório BC este programa se chama prg04 2.c
2.1. PROGRAMAS E ERROS...
37
11. Toda função da linguagem C deve terminar com o comando “voltar()”
(return), e, com frequência, com um número. Mais a frente você vai ver
que isto é falso... Este número pode ser usado para fornecer ao sistema informações sobre o comportamento da função, erros cometidos por ela, (na
verdade pelo programador...). Devolvendo zero significa que tudo correu
bem.
12. Os algoritmos começam com “inicio”, “{”, e termina com “fim” “}”. De
forma mais precisa, Os “blocos lógicos” começam com “inicio” “{” e
terminam com “fim”, “}”.
Vocabulário: 2 Bloco lógico e variável local
• bloco lógico é um conjunto de ações, de comandos, que tenham um objetivo
especı́fico. Uma função é um bloco lógico, mas dentro de funções você pode
encontrar mais blocos lógicos. É um conceito difuso mas que aos poucos
você compreenderá.
Sempre que você encerrar um conjunto de comandos entre chaves você terá
criado, para a linguagem C, um bloco lógico.
Veja a importância deste fato: no inı́cio de um bloco lógico você pode
definir variáveis locais que deixam de existir à saı́da do bloco.
• variável local são variáveis criadas dentro4 de um bloco lógico. Elas tem
sua existência associadas ao bloco lógico em que forem criadas. O conceito
que se opõe a este é o de variável global. Podemos dizer que você deve
evitar o uso de variáveis globais e se habituar a usar apenas variáveis
locais.
• variável global São variáveis criadas fora de blocos lógicos e que portanto
ficam sendo reconhecidas por distintos blocos lógicos. Algumas vezes somos
forçados a criar este tipo de variável, entretanto devemos inclusive deixar
indicativos no cabeçalho do programa apontando a existência delas numa
tentativa de eliminá-las, se possı́vel.
Como estas variáveis tem uma
existência ampla, há riscos que no planejamento nos esqueçamos de suas
presenças e elas, assim, interfiram nos resultado de forma inexperada.
Variáveis globais são um risco a ser evitado. Quando você tiver que definir
uma variável global, indique isto no cabeçalho do programa como uma
forma de aviso de existe um problema no programa.
No disco que acompanha este livro, há um diretório chamado BC em que os
programas foram testados em ambiente Borland, BC ou TC. Também os nomes
dos programas ficam dentro do limite do DOS de oito caracteres. Programas que
fujam a este padrão tem seus nomes corrompidos pelo ambiente de programação
da Borland.
4 no
inı́cio de um bloco lógico
38
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
Exercı́cios: 4 Alterações no programa5 prog01.c
1. Compile e roda o programa6 prog01.c.
2. Se você digitou uma palavra, o programa guardou somente a primeira letra.
Experimente digitar números, analise o resultado.
3. Altere7 prog01.c, subsitua
palavra coisa;
// (3) declaracao de variavel
por
palavra coisa[30];
// (3) declaracao de variavel,
agora compile-o e veja que o resultado foi desconcertante, uma montanha
de erros foram anunciados.
gcc -Wall -oprog prog01.c
prog, para executar o programa.
Leia o relatório de erros e veja nos próximos exercı́cios a saı́da.
4. Substitua
ler("%c",&coisa); // (5)
imprima("%s%c\\n","A primeira letra foi -> ",coisa); // (6)
por
ler("%s",coisa); // (5)
imprima("%s%s\n","A primeira letra foi -> ",coisa); // (6)
e veja o resultado digitando uma palavra com até 29 letras.
5. Digite também uma palavra com mais de 30 letras. Digite duas palavras,
quer dizer, duas “strings” separadas por um espaço, por exemplo. Analise
por que dá errado.
6. Faça algumas experiências alternado as tres linhas aqui discutidas e veja
os resultados. Mas, somente rode os programas se o compilador não apontar erros ou “warnings” porque este programa usa vetores de caracteres
que são ponteiros. Ponteiros devem ser usados com cuidado porque fazem
acesso direto à memória da máquina.
5 no
diretorio BC este programa se chama prg01.c
BC prg01.c
7 no diretorio BC este programa se chama prg01.c
6 em
2.1. PROGRAMAS E ERROS...
39
Algo pode ter saı́do errado quando você rodou este programa. Vamos analisar o que pode ter acontecido. Primeiro você pode ter digitado um número.
Experimente, se não o fez.
Se você tiver digitado um número, o programa rodou, sem problemas, apesar
de que ele não tenha sido feito para isto. Observe que foi um erro8 , porque se
você desejasse que o programa lesse um número, você deveria ter dito isto. Foi
um erro do programador, não do programa. Programas não erram, eles fazem
apenas aquilo para o qual foram planejados9 .
Com o programa modificado, se você tiver escrito uma frase de verdade,
o programa só imprimiu a primeira palavra. Porque quando ele encontrou o
primeiro espaço considerou encerrada a leitura da variável coisa[30] e, naturalmente, somente imprimiu a primeira palavra.
Experimente colocar a frase entre aspas, veja o resultado.
Lição: 1 C roda aquilo que não se espera...
Um dos mitos por traz da linguagem C é que com ela se fazem programas
que rodam muito rápido. Isto pode ser verdade, e uma das razões se encontra no
fato de que o compilador espera que você não cometa erros e reduz ao mı́nimo
a verificação da lógica do programa.
É comum se dizer que um programa em C sempre faz alguma coisa...
mesmo que não seja o que se espera. Isto não é um defeito da linguagem,
acontece que C é considerada uma linguagem para programadores profissionais,
poristo não tem sido considerada uma linguagem para inciantes...
O programa acima foi feito para escrever frases, mas escreve também números.
Num programa grande e complexo isto poderia ser um desastre. Claro, o programa lhe pedia para escrever alguma coisa, isto não se faz! A comunicação
usuário-programador deve ser mais completa e sempre clara. Além disto o
próprio programa deve ter recursos de verificação do que o usuário está fazendo
e deve então orientá-lo a repetir a operação de forma correta. Na verdade “programas” só fazem aquilo para o qual foram planejados.
Observação: 4 Comentários dos exercı́cios
• Há uma diferença fundamental entre
palavra coisa;
// (3) declaracao de variavel
e
palavra coisa[30];
// (3) declaracao de variavel,
No primeiro caso, C entende que “coisa” é um simples caractere, um dos 256 caracteres
que você pode produzir com o teclado.
No segundo caso, C entende que “coisa[30]” é um “vetor” de caracteres.
C enumera os ı́ndices a partir de zero, que dizer que você tem direito de usar
coisa[0], coisa[1], . . . , coisa[29], coisa[30]
e se o vetor estiver construido corretamente, coisa[31]=’\0’ é o NULL, um caracter
especial que marca o fim dos vetores.
Você não tem o direito de fazer uso deste último espaço de forma diferente sem o risco
de erros no seu programa, é como se você guardasse uma garrafa cheia destampada...
8 É
preciso desmistificar os erros, errar é natural de quem está aprendendo, simplesmente.
gente que diz “desenhados”, que horror.
9 tem
40
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
Este último caractere se chama NULL e serve para marcar o fim dos vetores corretamente construidos. Claro que você pode construir vetores incorretamente, correndo
riscos de que seus dados se misturem produzindo erros.
• Veja a diferença entre estas duas linhas:
ler("%c",&coisa); // (5)
imprima("%s%c\\n","A primeira letra foi -> ",coisa); // (6)
e
ler("%s",coisa); // (5)
imprima("%s%s\n","A primeira letra foi -> ",coisa); // (6)
O formatador %s anuncia às funções “imprima()“ e “ler()“ que virá um “vetor de
caracteres” ou string.
Aqui também se encontra uma das dificuldades no uso de ’ler()’ (scanf()). Esta função
da linguagem C é muito sensı́vel... ela sempre guarda os dados através dos seus endereços. É o que se encontra expresso em
ler("%c",&coisa); // (5)
que poderı́amos traduzir como
“guarde o caracter que vem pelo teclado na variavel cujo endereço é &coisa”.
• Tudo muda quando declaramos “palavra coisa[30];” porque coisa[30], sendo um vetor,
é uma sucessão de 30 endereços, para C, um vetor de endereços. Antes chamamos de
vetor de caracteres. Mais a frente você vai ver que existem outros tipos de vetores.
Todo vetor é um vetor de endereços de um certo tipo de dados. Aqui estamos com um
vetor de caracteres. Leia a respeito de tipos de dados no capı́tulo 7, mas faça apenas
uma leitura rápida.
• Usar o redirecionador de endereços num endereço é errado. Vamos dizer isto de outra
forma, usando uma linguagem técnica.
1. Existem variáveis do tipo “endereço”, são os ponteiros.
2. Além disto precisamos declarar que tipo de ponteiro. Leia mais a respeito no
capı́tulo sobre ponteiros, veja no ı́ndice remissivo.
3. Quando uma variável for um ponteiro, então não será correto usar o direcionador “&” na frente desta variável em ler() scanf(). Seria o mesmo que dizer
guarde este dado no endereço endereço X.
• Isto justifica que deixemos de lado a funcao ’ler()’ (scanf()) nos primeiros passos do
uso da linguagem. Iremos fazer um uso de um método mais complicado, entretanto
mais seguro,evitando esta discussão inicial deixando-a para um momento em que o
estudante de C esteja mais a vontade com a linguagem.
Rode e leia o programa10 prob scanf.c. Sobretudo leia os comentários ao final do
programa.
10 dentro
do BC o nome aparece cortado, tem mais de oito caracteres. Troque o nome arquivo
para prbscanf.c
2.1. PROGRAMAS E ERROS...
41
• Existe um compilador para a linguagem C, chamado checker que faz uma verificação do
uso da memória pela variáveis do tipo ponteiro e pode alertar para problemas deixados
dentro de um programa.
Sintaxe: checker -gcc programa.c [comandos do gcc] Não pude encontrar um similar
para DOS, não sei se existe.
Observação: 5 Programas robustos.
Agora ficou claro que não se espera que você escreva números. Mas para frente você vai
aprender a criar estruturas de controle de entradas de dados que aconselharão o usuário a
re-escrever o que se pede, no caso de que ele tenha respondido com alguma inconveniência.
São métodos para fazer programas seguros, ou robustos. Está cedo, entretanto, para uma
discussão mais aprofundada sobre este assunto.
Vocabulário: 3 Dados, variável
• dados É complicado discutir o que são dados, um programa todo pode ser
um dado... mas se tentarmos simplificar as coisas para começar a discutir, programas servem para manipular dados, quer dizer transformar uma
informação bruta em uma informação tratada, manipulada, lapidada...
“processada”.
O programa prog01.c parece rı́diculo, pede um nome e volta a escrevê-lo
na tela. Mas ele poderia ter pedido o “seu nome” para comparar com os
dados de um banco interno de nomes afim de permitir-lhe ou negar-lhe a
entrada no sistema. Então o seu nome é uma informação que, comparada
com um banco de clientes, diz se você pode ou não ter acesso às outras
informações.
• variável Para guardar dados se criou um sistema engenhoso que usa tres
etapas.
– Uma tabela formada de palavras, chamadas identificadores, associadas aos endereços, uma tabela de alocação que é basicamente o que
cada usuário, ou programador, usa. Estas palavras são chamadas
“variáveis”. Em certas linguagens esta tabela se chama de “espaço
de nomes”.
– A cada tal variável se associa um endereço inicial no segmento de
memória reservado para tal onde se inicia o conteúdo da variável
e, pelo seu tipo, se calcula onde deverá terminar reservando-se o
próximo endereço inicial de outra variável. Por esta razão você precisa declarar o tipo da variável que você pretende usar.
– Uma associação dos elementos do espaço de nomes com seus respectivos enderecos iniciais, chamada ainda de “alocação”. Esta alocação
é dinâmica porque as variáveis são criadas e destruı́das portanto os
endereços iniciais mudam durante a execução de um programa. Você,
e qualquer outro programador, não precisa se preocupar com isto, está
é uma atribuição do sistema operacional.
Em C, o uso do endereço, pelo programador, é uma das caracterı́sticas
da linguagem havendo um tipo particular de variável que opera sobre o
42
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
endereço, as variáveis do tipo ponteiro. Você pode programar em C sem
usar ponteiros, mas o atrativo é que podemos acelerar os programas com
seu uso, como também torná-los mais perigosos. Como já dissemos, pegar
nos fios elétricos de mal jeito pode levar à morte, mas você não prefere
viver no escuro... aprenda a usar ponteiros. Mas deixe para fazer uso
deles quando tiver uma compreensão segura de como funcionam.
Veja um exemplo bem simples que mostra a importância do uso de endereços
para acelerar a manipulação da informação.
Exemplo: 3 Uso de ponteiros e a velocidade Pense no seguinte exemplo, um
enorme armazem em que uma instituição tem guardados todo o seu acervo,
tomemos o caso de um museu.
Sempre novos itens chegam e seria impossı́vel prever de antemão onde cada
um deles seria guardado, inclusive será preciso adquirir de vez em quando uma
nova casa para abrigar o acervo sempre crescente do museu, (imagine que os
governantes se preocupam com os museus e sempre estão liberando mais verba
para enriquecer a instituição...)
Como manter o acervo organizado? As peças vão chegando e simplesmente
recebem um número de ordem de chegada, e um endereço em que se encontram
guardados (pode ser a identificação de uma sala em um determinado prédio).
No fim do dia a lista que identifica os itens do acerto é novamente ordenada (por
ordem alfabética) o que significa que se re-orientam os ponteiros entre objetos e
endereços. É muito menos pesado trocar a indicação
piano21 → casa10 - sala 30
do que manter sempre o piano21 no mesmo lugar... quando houver um concerto e for preciso levar o piano21 para o auditório, o lugar dele não precisa
ficar reservado, outros objetos podem ocupar o seu lugar, e depois ele pode ser
guardado n’outro endereço e o cadastro vai simplesmente ser re-organizado com
a troca
piano21 → casa15 - sala 3
porque seria muito mais difı́cil estar mudando de lugar pianos.
A exemplificação acima se aplica literalmente a qualquer banco de dados,
quer dizer um programa que associe distintas informações como nomes de pessoas objetos, endereços, contas bancárias, por exemplo um catálogo telefônico.
Um banco de dados fica inútil se não estiver ordenado, porque então simplesmente os dados ficaram perdidos, imagine um catálogo telefônico em que os
nomes dos usuários não apareçam em ordem alfabética, seria inútil!
Mas os bancos de dados são dinâmicos no sentido de que sempre estamos
colocando novos nomes ou retirando nomes ou qualquer outro tipo de dados, consequentemente vivem desordenados. Como os dados podem ocupar muito espaço
na memória do computador, (igual pianos no acervo do museu), é preferivel ordenar os endereços que são relativamente pequenos. Aı́ entram os ponteiros para
acelerar o processamento.
Observação: 6 Abstração e variável
2.1. PROGRAMAS E ERROS...
43
Os computadores, atravez de vários sistemas de códigos, podem guardar informações
praticamente de quase todo tipo. A palavra “abstração” adquiriu um sentido novo com a
ciência da computação, ela distingue as informações pelo que se entende hoje como de nı́veis
de abstração. Antes abstração era sinônimo de difı́cil, hoje caracteriza o nı́vel de complexidade de um conceito no sentido de que ele comporte uma quantidade maior de informações.
Por exemplo, um número guarda um tipo de informação que podemos considerar como de
primeiro nı́vel de abstração.
Mas, um par ordenado de números tem um nı́vel maior de abstração porque pode guardar
informação não numéricas como endereços de apartamentos de um prédio de vários andares.
Em ternos ordenados de números poderiamos guardar a informação de quantos habitantes
existe por apartamento... Nos dois últimos casos os “números” deixaram de ser números e
passaram ser códigos.
Esta é evolução dos “números”, que vistos como códigos, criam novos tipos de dados e
sucessivos niveis de abstração. Veja que “número de telefone” não é número, e sim código...
você não somaria dois números de telefone, somaria ?
A memória de um computador tem um endereçamento “dinâmico” semelhante a de um
edifı́cio de apartamentos. Dinâmico porque a cada instante mudam
• os endereços;
• os habitantes;
• o tamanho dos apartamentos.
Os habitantes são as “variáveis”. Aqui, à diferença com o que ocorre num edifı́cio de
apartamentos, o tamanho dos apartamentos se adaptam ao tamanho das variáveis... Ao
definir uma variável se estabelece o endereço do ponto inicial de memória que ela vai ocupar
e do tipo de dado que ela vai representar, (ou “conter”, com se diz comumente), e assim
se marca o inı́cio de uma próxima “variável”. ou o outro endereço inicial. Se o sistema
operacional for bem feito, ele fica monitorando o uso das variáveis para realocar o espaço na
memória, levar para o disco, ou trazer de volta do disco, páginas de memória.
Veja a nova formulação de prog01.c → prog02.c.
Primeiro compile11 e rode prog02.c:
gcc -Wall -oprog prog02.c
prog, para executar o programa, ou ./prog
Exemplo: 4 prog02
/* Programa prog02.c
Assunto:le uma frase pelo teclado e a imprime
Programa errado, compile e corrija o erro. Ver exercicios.
por Tarcisio Praciano Pereira - 10 licoes para aprender C
Sobral, julho de 2000 - UVA
*/
#include <stdio.h>
#include <string.h>
#include "traducao.h"
palavra principal()
11 em
BC procure prg02.c
44
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
inicio
palavra coisa1[30], coisa2[30], coisa3[30]; //(0)
imprima("%s%\n", "escreva uma frase curta pelo teclado, ");// (1)
imprima("%s\n", "digamos, com tres palavras.. "); // (2)
imprima("%s\n"," pode ser o seu nome, por exemplo ");//(3)
ler("%s%s%s",coisa1,coisa2,coisa3); //(4) ainda usa ’scanf’
imprima("%s%s%s\n",coisa1,coisa2,coisa3); //(5)
fim
/* Comentarios:
0) declaracao de tres variaveis - vetores do tipo string.
1,2,3) mensagens orientando o usuario a fornecer os dados.
em (1) tem um erro que o compilador detecta.
4) Leitura de dados com ’ler’ (scanf) observe a ausencia do
direcionador de endereco &, desnecessario porque as
variaveis sao do tipo ponteiro, declaracao implicita.
5) Um unico ’imprima’ imprime todos os dados.
*/
Rode o programa para ver o que acontece. Programinha ruim, não é? Claro,
estamos apenas começando. Vejamos alguns defeitos e como poderı́amos corrigı́los.
Exercı́cios: 5 Alterando e entendendo prog02.c
1. Compile e rode o programa prog02.c.
2. Quando compilado, o compilador reclama:
prog02.c:16:
warning: unknown conversion type character 0xa in format
prog02.c:21:
warning: control reaches end of non-void function.
Verifique que na linha 16 tem % sem o caracter que caracteriza o tipo de
dados “conversion type”. O outro erro, linha 21, se deve à ausência de
um valor a ser devolvido, corrija estes erros. Observe que o programa,
mesmo errado, roda. Na maioria das linguagens modernas isto não se dá.
Corrija estes erros, (compare com prog02 1.c).
3. Rode prog02.c, digitando cada um dos nomes em uma linha diferente (separados por “enter”).
4. Refaça prog02.c para colocar as tres mensagens da entrada de dados num
único ’imprima’. Observe que cada mensagem é um parâmetro, veja o
último ’imprima’ para se inspirar.
2.1. PROGRAMAS E ERROS...
45
5. Melhore a saı́da de dados colocando um separador entre cada palavra escrita:
coisa1,’’ ’’,coisa2,’’ ’’,coisa3
não se esquecendo de incluir os formatadores %... Veja no exemplo abaixo
a solução.
solução leia os comentários no programa prog02.c
Depois vamos tornar este programa mais inteligente, deixando que ele mesmo
detecte quantas palavras o usuário quer escrever. No momento vamos ser mais
imperativos: Escreva uma frase com tres palavras.
Exemplo: 5 prog02 1.c
/* Programa prog02_1.c
Assunto:le uma palavra pelo teclado e a imprime
por Tarcisio Praciano Pereira - 10 licoes para aprender C
Sobral, julho de 2000 - UVA
*/
#include <stdio.h>
#include <string.h>
#include "traducao.h"
#include "ambiente.h"
palavra principal()
inicio
palavra coisa1[30], coisa2[30], coisa3[30]; //(0)
imprima("%s%s%s\n", "escreva uma frase curta pelo teclado",
"com tres palavras. ",
" Pode ser o seu nome, por exemplo "); // (1)
ler("%s%s%s",coisa1,coisa2,coisa3); //(2)
imprima("%s %s %s\n ",coisa1,coisa2,coisa3);//(3)
devolve 0; //(4)
fim
/* Comentarios:
0) Declaracao de variaveis com tamanho adequado para caber nomes.
1) Um unico ’imprima’ para as tres frases. Observe que as
frases podem ser dispostas em tres linhas diferentes, os
espacos entre os parametros nao tem significado.
2) Ainda usando ’ler’ (scanf)
3) Observe os espacos entre os formatadores de dados e veja
o resultado disto na impressao. Tire os espacos e veja o
resultado.
46
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
4) A ausencia de ’voltar’ provoca um erro.
*/
2.1.1
Análise do prog02 1.c
Começaremos por discutir os sı́mbolos estranhos %s etc.. que apareceram nos
programas.
Observação: 7 Formatadores de dados
• cabeçalho O sinal ”/*”marca o inı́cio de um comentário que é terminado
com o sinal ”*/”. O programa começa com um comentário que costumamos chamar de “cabeçalho”, nele colocamos as informações genéricas
sobre o programa. Se, por exemplo, estivermos trabalhando em equipe com
colaboradores, eles devem receber programas nossos para alterar, modificar,
melhorar. Alguns dos programas até funcionam, outros só trazem a idéia
daquilo que devem fazer... No cabeçalho colocamos estas informações, não
apenas para nós mesmo que escrevemos o programa, como também para
os outros que vão trabalhar com os programas.
Não duvide, se você for ler um programa uma semana depois que o escreveu, possivelmente não vai mais entendê-lo....
• comentários Os comentários podem ser escritos em diversos lugares dentro dos programas. Se por um lado você deve escrever muitos comentários,
também deve ter o cuidado para que eles não causem uma poluição visual
que depois atrapalhe a leitura do programa. Guarde a idéia de que um
programa deve ser um texto bonito, agradável para os olhos e de fácil
leitura.
• formatação de dados O sı́mbolo % informa ao C que se segue uma
formatação de saı́da de dados.
– Se forem frases, (strings), então fica: %s.
– Se for um número, depende do tipo de número:
∗ para números inteiros: %d;
∗ para números fracionários: %f
• O erro, na terceira versão de prog02.c, consiste em que colocamos poucos
“%s” uma vez que os espaços separadores são também caracteres. O
comando que imprime os dados deve ser assim:
imprima("%s%s%s%s%s\n ",coisa1,’’ ’’,coisa2,’’ ’’,coisa3);
se quisermos imprimir tres palavras com espaços entre elas.
Naturalmente, você deve estar horrorizado! Como ficaria se quisessmos
escrever 30 palavras... Se acalme, veremos uma solução mais inteligente
depois. Se quiser estudar o assunto agora, veja os programas texto.c,
texto01.c, texto02.c texto03.c.
2.1. PROGRAMAS E ERROS...
47
Chamamos estes sı́mbolos de formatadores de dados, mas eles tem diversos
nomes, porque também têm diversas funções, por exemplo, eles12 servem para
“traduzir dados de um tipo para outro”.
C parece ser uma linguagem contraditória. Por um lado relativamente livre,
por outro lado contendo restrições de formatação rigorosas. Toda vez que você
usar uma função de saı́da de dados, tem que informar a esta função que tipo
dados lhe vão ser passados. Porque, se não o fizer corretamente, C poderá seguir
em frente coletando erros atrás de erros.
Para começar, que é tipo de dados? Dedicamos um capı́tulo a este assunto,
veja no ı́ndice, e se você quiser pode dar um salto agora para lá, onde esta
questão está sendo discutida com mais detalhes. No momento vamos dizer que
em computação se distinguem três coisas bem claramente:
• caracteres e palavras;
– caracteres, em princı́pio, qualquer um dos sı́mbolos que você pode
obter apertando uma tecla. Há alguns poucos que não podem ser
obtidos desta forma. São caracteres especiais, como o caracter de
fim de linha.
– frases, ou aglomerados de caracteres os vetores de caracteres,
em inglês, strings
• números;
– número inteiro
– número fracionário, chamado real ou em inglés, float.
• vetores;
– vetores de caracteres (strings)
– vetores de inteiros ou de reais.
Caracteres são qualquer um dos duzentos e poucos sinais que você pode
produzir com o teclado do computador, como
A, a, / , % . . .
existe uma tabela americana chamada tabela ASCII 13 que registra umas duas
centenas de caracteres que são, no fundo, a base do modo de comunicação escrita
que usamos. Esta tabela já foi muito mais importante do que é hoje porque
os meios de comunicação evoluiram tanto que hoje já praticamente não mais
usamos “caracteres” para nos comunicar. Usamos cores... ou mais exatamente
bits.
Os números são agregados de caracteres tirados da coleção
1,2,3,4,5,6,7,8,9,0, “.”
12 Ver
cast a este respeito.
Standard for Communication and Information Interchange
13 American
48
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
que podem ser inteiros, se não usarmos “.” e se usarmos o “ponto” representam
números fracionários. Esta é uma explicação muito rasteira, mas é mais ou
menos a forma como Fibonacci explicou os números decimais no século 11, sem
incluir o “ponto”. Sem dúvida, seria ótimo que você não ficasse satisfeito com
ela e criticasse o autor chamando-o de superficial...
Depois podemos combinar estes dois tipos de dados,
caracteres , números
para criar tipos de dados bem mais complexos. Você pode ver isto a partir
do capı́tulo 5.
Dito isto, o programa começa com palavra14 , para indicar que coisa, coisa1,
etc... são do tipo caracter. Em ingles se usam duas palavras para isto, string,
character. character é um caracter, ao passo que string é um vetor de
caracteres, quer dizer um aglomerado de caracteres que pode inclusive conter
espaços, (“espaço” é também um caractere que você gera quando usa a “barra
de espaços que nada mais do que uma tecla...)
Um vetor de caracteres é, por exemplo
”Isto é um vetor de caracteres”
é um conjunto de caracteres delimitado por “aspas”. Observe que
’’Isto é um vetor de caracteres’’
é diferente de
’Isto é um vetor de caracteres’
A segunda expressão é um erro, porque C usa ’d
A primeira linha do programa indica que coisa1, coisa2, coisa3 são variáveis
que devem conter palavras. É uma declaração de tipo de dados.
Observação: 8 E o que significa variável ?
As linguagens de programação são exemplos de linguagens formais. Quer dizer que elas
tentam, e com relativo sucesso, estabelecer uma comunicação entre o homem e a máquina.
Na verdade entre programadores e aqueles que construiram os compiladores das linguagens...portanto entre homens presentes em frente ao teclado e homens ausentes representados
pelo compilador.
Consequentemente elas tem que satisfazer a um conjunto de regras lógicas que vão dar
sentido as frases de que se compõem os programas. Da mesma forma como eu não posso
me dirigir a você, querendo me referir a uma cadeira, dizer: me dê a mesa. Você nada vai
entender, sobretudo se na nossa frente não houver nenhuma mesa.
cadeira é uma variável da lingua portuguesa ocupada com um significado bem definido,
e naturalmente, imutável. Mas você já ouviu alguém dizer
esta coisa não serve para escrever
fazendo referência a:
• um lápis sem ponta;
• uma caneta quebrada;
• uma velha máquina de escrver.
coisa é uma variável da lingua portuguesa.
Quer dizer que há palavras livres para assumir distintos valores.
No presente caso temos
• palavra, imprima, ler que são palavras reservadas ”portuguesas”da linguagem C;
• em inglês seriam char, printf, scanf;
14 em
inglês seria char
2.1. PROGRAMAS E ERROS...
49
• coisa, coisa1, coisa2, coisa3 que escolhemos para guardar os fonemas que você resolver
guardar: lápis,caneta, máquina velha etc...
Examine o arquivo traducao.h onde vai você vai encontrar muitas das palavras reservadas
da linguagem C com a respectiva tradução que estou usando nos programas. Aı́ está o segredo
de programar em Português...
Depois da declaração de tipos de dados vieram os comandos imprime, ler.
Estes comandos podem receber uma quantidade indefinida de parâmetros, mas,
para cada parâmetro, deve vir indicado o tipo de dado que vai ser lido, é este o
significado de %s para ler ou escrever palavras.
Bom, faltou discutir o que significam as tres primeiras linhas do programa:
diretiva de compilaç~
ao
#include <stdio.h>
informa ao compilador que ele deve ler a biblioteca stdio.h que é um arquivo
onde se encontram definidas as funções prinf e scanf15 .
diretiva de compilaç~
ao
A linha
#include ’’traducao.h’’
diz ao compilador para ler o arquivo traducao.h. A diferença entre aspas ou
sinal de desigualdade reside no local onde se encontram os arquivos. Quando
o nome se encontra envolto por aspas, isto significa para o compilador que
o arquivo se encontra no mesmo diretório em que se está trabalhando com
programa, o “diretório corrente”. Quando se envolve o nome do arquivo com
<, >, o compilador sabe que deve procurar o arquivo no diretório padrão em
que se encontram todas as bibliotecas da linguagem C.
A linha
palavra principal()
é o inı́cio do programa... e palavra indica que a função principal() vai produzir uma saı́da de dados do tipo palavra. Isto é, principal é do tipo palavra.
Todo programa em C tem a estrutura básica dos programas acima.
• Primeiro vêm as diretivas de compilação marcadas pelo sı́mbolo #, como
include, que significa incluir.
• Depois vêm as definições das funções que serão usadas. Discutiremos logo
no próximo capı́tulo o que são funções.
• Depois a vem a funç~
ao principal(), em inglês se chama main(),é a
função que gerencia o programa.
Ela é responsável de colocar as coisas para andar, é o maestro que vai
comandar o espetáculo.
15 que
traduzimos por imprima e ler. Se você não gostar destes nomes, use outros...
50
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
Antes da palavra principal se encontra o tipo de dados que a função vai
produzir, na última linha do programa, com a função voltar, return.
Depois dos parêntesis, que podem conter parâmetros, vem uma chaveaberta contendo o algoritmo implementado ao final do qual se fecha a
chave terminando assim o programa.
• A ordem como as funções vierem dispostas no programa é irrelevante, mas
no inı́cio deve vir uma lista das funções que vào ser definidas no arquivo e
que faz parte integrande do cabeçalho do programa. Isto é o que se chama
de protótipos, é o planejamento do programa.
• Tome como exemplo o programa integral.c. Depois dos comentários
vem a lista de funções que vão ser definidas mais abaixo:
// Declaracao de funcoes *******************
real
Riemann(real ini, real _fim, real deltax);
real
f(real x);
• Em C, as chaves servem para definir as unidades lógicas. É o que se chama
um bloco, uma unidade lógica. Observe que dentro de um bloco pode ter
outro bloco... Aqui estamos usando “inicio” e “fim”, com esta finalidade,
como traduções das chaves {, }.
Os vocábulos que usamos nesta seção foram:
Vocabulário: 4 bloco lógico, imprima, include, escreva, função, palavra, ler,
principal, printf, protótipos, scanf, char, string
• bloco lógico é uma unidade de programação, conceito difuso que aos
poucos você irá dominar. As funções são blocos lógicos. Ao abrir e fechar
chaves você cria um bloco lógico.
• imprima() é tradução de printf() é a função da linguagem C para produzir uma saı́da de dados. Imprimir no video.
• include é uma diretiva de compilação para que o compilador veja informações em uma biblioteca. Há várias diretivas de compilação, elas
são assim chamadas porque dirigem o compilador para fazer tarefas bastante complicadas antes de criar o programa. Não as discutiremos neste
livro.
• escreva() outra tradução que fizemos de printf(), para mostrar-lhe que
podem ser diversas. Isto poderia ser considerado um defeito, por alguns,
um qualidade por outros (a diversidade...)
• funç~
ao As funções são as menores unidades lógicas. printf(), scanf()
são funções. Tudo em C é função16
16 nem
tudo, você vai ver depois..., mas quase tudo, digamos.
2.1. PROGRAMAS E ERROS...
51
As funções em C se assemelham um pouco com as funções da Matemática.
Depois você vai ver as diferenças, mas agora usemos as semelhanças. Em
C definimos uma função f e depois escrevemos f (a); C calcula qual é o
resultado de f aplicada em a. Como em Matemática...
Mas frequentemente escrevemos apenas f(); o que não se faz em Matemática, porque em C existem funções que não recebem parâmetros.
• palavra foi uma das traduções que demos para char que significa caractere, um tipo de dados de C.
• ler()é tradução de scanf() é a função da linguagem C para ler dados
pelo teclado. Esta função deve ser evitada, usar fgets(), ver prog04 3.c
prog03 8.c
• principal() é função gerente do programa, obrigatória, em inglês, main()
Observação: 9 Tradução da linguagem C
A tradução da linguagem que estamos introduzindo neste livro não é uma
brincadeira. Ou a nossa proposta ou a de outro grupo de pessoas um dia virá a
ser levada a sério. Faz parte de nossa identidade cultural sabermos programar
em nossa lingua. Sem dúvida é uma atitude anti-globalizante de defesa do
desenvolvimento regional.
Entretanto algum organismo, possivelmente a SBC, deve num certo momento
chamar um grupo de pesquisadores e programadores para estabelecer um padrão,
porque uma linguagem de programação deve ser padronizada afim de que os
programas possam rodar em qualquer lugar e ser entendidos por todos.
52
CAPÍTULO 2. O SEGUNDO PROGRAMA EM C
Capı́tulo 3
Números e Letras
Resumo.
A linguagem C não foi feita para trabalhar com números, ela foi feita para
trabalhar com caracteres, acessar memória, executar operações aritméticas
e operações lógicasa . Mesmo assim ela tem uma capacidade numérica limitada em seu formato original. As implementações modernas tornaram esta
capacidade bem mais avantajada, porque C nasceu dentro do ambiente que
hoje podemos caracterizar como de programas livres ou de domı́nio público,
que sempre foi tı́pico dos que programavam em Unixb . Uma conseqüência
disto é que a linguagem C cresceu e hoje até poderia ser considerada para
processamento numérico porque tem bibliotecas dirigidas para tal.
Neste capı́tulo vamos explorar um pouco da capacidade numérica da linguagem C. O capı́tulo 8, na verdade, se dedica à Matemática, aqui vamos apenas
brincar um pouco com os números. Veremos um pouco de suas limitações,
como estas podem ser alteradas, que você tem nas mãos os meios para produzir estas alterações e e que você vai aprender como fazê-lo...
Se o seu sistema for LinuX, você tem chances de fazer grandes alterações, mas
tenha cuidado, não vá fazer o que você não sabe sem tomar as precauções
adequadas. Esta frase não tem o objetivo de amendrontá-lo, nem de inibı́lo.Aprenda C a fundo e terá uma ferramenta imponente em suas mãos.
a porque
C foi feita para montar um sistema operacional.
interessante observar que a própria palavra Unix, é uma marca registrada, o sistema operacional Unix tem um dono. Mesmo assim Unix sempre
foi usado com liberalidade. LinuX é um clone do Unix, tem dono, Linus
Torvalds, mas está colocado sob o GPL
b É
3.1
Brincando com números em C.
Como o tı́tulo menciona, nesta seção vamos estudar um programa que efetua
operações com números.
O programa que lhe vamos propor, prog03.c, vem com erros. Algumas
correções e mais outros tantos erros se encontram na suite de programas
prog03 X.c
Estamos absolutamente convencidos que os erros são o instrumento mais
profı́cuo do aprendizado, tantos os nossos como os seus.
53
54
CAPÍTULO 3. NÚMEROS E LETRAS
Mas, não limite sua imaginação, altere os programas atendendo a todas as
possibilidades que lhe vierem à cabeça para “experimentar” outros resultados.
Encontre, você mesmo, outras alterações e as teste. Os exercı́cios propostos
são apenas um guia para despertar a sua curiosidade. Nao tema estragar o
computador ou o compilador, eles sao muito robustos.
Primeiro compile e rode prog03.c:
gcc -Wall -oprog prog03.c
digite, prog, para executar o programa.
Possivelmente digite ./prog, se o sistema não estiver
bem instalado, um defeito no path.
depois leia o programa para descobrir onde está errado, e o corrija.
Exercı́cios: 6 Alterando prog03.c
As soluções destes exercı́cios, são os programas prog03 X.c.
1. Altere prog03.c para escrever a soma dos dois números que lhe forem
fornecidos. Veja a solução proposta em prog03 1.c.
2. prog03.c imprime os números que você forneceu colados um no outro, feio!
Corrija isto. Ver solução em prog03 1.c
3. Torne o programa mais “verboso”, conversando mais detalhadamente com
o usuário, tanto na entrada de dados como na saı́da de dados.
4. Experimente com somas de números cada vez maiores para testar a precisão do sistema que você usa.
5. Altere prog03.c para somar números não inteiros. Solução prog03 8.c.
6. Faça um sistema generoso de mensagens para tornar sua “calculadora”
mais atraente, por exemplo, peça os números para somar um a um.
7. Altere prog03.c para fazer a multiplicação entre dois números.
8. Altere todos os programas da serie prog03*.c substituindo ler() (scanf())
pelo par de funções
leia(), converte palavra()
. Veja prog03 91.c. Você precisa declarar uma variável
palavra deposito[80]
para receber dados. O tamanho, “80”, porque cabe uma linha.
3.1. BRINCANDO COM NÚMEROS EM C.
55
9. Existem dois tipos de números nas linguagens de programação, inteiros
ou reais. Do ponto de vista de Matemática isto é uma aberração1 , mas
não estamos fazendo Matemática, aqui. Exemplifique o que é número
inteiro e o que é número real, em computação.
Resposta prog03 10.c
10. ** fora do contexto Descreva o que significa dizer-se que um número a
está representado na base 8, na base 10 ou na base 16.
Resposta base.c, compile este programa assim
gcc -Wall -oprog -lm base.c
a opção −lm instrui o compilador a fazer uso da biblioteca matemática.
Experimente omitir esta instrução, o compilador não saberá o que é pow,
a função potência.
Observação: 10 Comentando os exercı́cios
O programa prog03.c sugere que você use números inteiros muito grandes para ver o que pode acontecer. Não sabemos o que “muito grande” representa para você, por exemplo a soma dos custos dos rombos bancários pagos pelo
“proer”, quer dizer, por nós todos:
40.000.000.000 dólares !
Se isto já for muito, experimente definir a constante
inteiro BANCOS = 40000000000;
e a use nas suas alterações de prog03.c. Observe que o programa talvez não
suporte este valor2 .
Experimente somar números maiores que “BANCOS”. No C que roda em
LinuX você precisará experimentar com inteiros maiores que 2 mi para notar
alguma coisa extranha. Experimente, portanto. Na versão tradicional do C os
inteiros vão de -32768 até 32767. Isto quer dizer que se você pedir para somar
n = 32767; m = 1 ⇒ n + m = −32768
Veja as respostas que obtivemos:
A soma de 2147483647 com 1:
A soma de 2147483646 com 1:
-2147483648
2147483647
Quer dizer que os inteiros de C não suportam um “BANCOS”.
Mesmo assim, sob LinuX os inteiros chegam a casa dos bilhões. Isto nada
tem de estranho, nem LinuX é melhor que qualquer outro sistema apenas porque
os inteiros em C, sob LinuX, tem um espectro mais amplo. LinuX é melhor
porque você pode alterar este espectro, por exemplo. Com números inteiros muito
grandes, você pode estar gastando memória à toa, e talvez, para um uso especı́fico
seja interessante reduzir a largura deste espectro.
1 porque
os inteiros também são números reais
o Brasil ainda continua em pé, apesar deste roubo, e ainda dizem que somos um
paı́s pobre...claro, quando pedimos dinheiro para as Universidades, aı́ o paı́s é pobre... com
BANCOS se poderia pagar 8 anos do orçamento minguado das Universidades brasileiras.
2 mas
56
CAPÍTULO 3. NÚMEROS E LETRAS
Em LinuX você pode fazer facilmente isto, mas não se esqueça que o sistema
é multi-usuário e isto pode causar problemas... Observe que, mesmo que você
seja o único usuário cadastrado em uma máquina rodando LinuX você, ainda
assim, não é o único usuário...existe um monte de usuários (do sistema) trabalhando junto com você. Eles podem necessitar (quase certamente necessitam)
do C instalado na máquina...
Aprenda como fazer, antes de fazer. Estudando mais a fundo, não vai ser
ainda aqui, você irá aprender como alterar a linguagem C para um uso especı́fico
O segredo é, construa uma biblioteca adequada, para não atrapalhar os outros
usuários.
Outro: o uso das diretivas de compilaç~
ao.
Este programa é muito pequeno para oferecer espaço para muitas mensagens,
mas você pode incluir “setas” no local onde o usuário do programa irá digitar
números, por exemplo. Se não o tiver feito, faça-o agora.
Observação: 11 Setas - código ASCII
Já nos referimos antes à tabela ASCII, consultando-a você pode inserir alguns caracteres diferentes para embelezar seus programas.
Leia a biblioteca ambiente.h. Nela estão definidas as letras acentuadas do
alfabeto brasileiro. Se você quiser escrever “corretamente” a frase
“ A acao sistematica do modulo eh “
você deverá3 digitar:
imprima(‘‘A a%c%co sistem%ctica do m%cdulo %c’’,
cedi,atil,aagu,oagu,eagu);
Veja o programa4 acentuacao.c para uma alternativa de escrita com os
sinais diacrı́ticos da lingua portuguesa.
No lugar de cada %c aparecerá o valor de cada uma das macros cedi,atil,
aagu, oagu, eagu definidas na biblioteca ambiente.h.
Se você rodar o programa ascii.c, ele lhe vai imprimir alguns dos valores
da tabela ASCII. Nem todos podem ser impressos, um deles é o caracter de fim
de linha... outro irá produzir som no “alto-falante” do micro. Também existem
variações da tabela ASCII usadas por fabricantes diferentes e o resultado do
programa muda de um micro para outro. Consequentemente é difı́cil ter certeza
de que as palavras serão escritas corretamente quando o programa for executado.
Observe a estrutura da informação abaixo:
imprima("A soma de %d com %d eh %d\n",n,m, n+m);
A função imprima recebe dois tipos de parâmetros5 :
• Um vetor de caracteres, "A soma de %d com %d eh %d\n", na qual
se encontram presentes instruções de formatação de dados: %d, e
3 não
se assuste, esta é apenas uma forma de resolver o problema
também a suite de programas texto*.c
5 tipos, não quantidades
4 veja
3.1. BRINCANDO COM NÚMEROS EM C.
57
• os parâmetros que vão preencher o vetor de caracteres na mesma ordem em que se encontram as instruções de formatação. Se chama a isto
de expansão dos formatadores.
Exercı́cio: 1
1. Altere o programa prog03.c para conversar com o usuário
pedindo sucessivamente mais números para serem somados num total de
cinco parcelas. A função que faz leitura de dados, ler()6 , tem uma sintaxe
semelhante a da função imprima():
ler(‘‘%d’’,&num) em que
• ’’%d’’ é a instrução sobre que tipo de dado será lido.
• num é a variável que vai guardar o dado.
• & aponta para o local da memória que estiver associado com a variável
num.Quer dizer que vai ser feita uma atribuição de valores indireta:
guardar um dado na memória cujo endereço está sendo referido por
&num.
• Posteriormente, nas operações aritméticas, você vai usar num e não
&num.
2. Faça um programa que solicite do usuário cinco números inteiros, e calcule
a soma destes números. Ver prog03 2.c
3. O perigo de ’ler()’ (scanf()) Observamos que é um perigo, mas pode ser
usado de forma benigna, para quem já tenha adquirido experiência.
Quando rodar prog03 2.c ofereça todos os números de uma só vez, separados por espaços. Veja o que acontece. Rode novamente o programa,
porém, forneça os números um por um, separados por < enter >.
4. Melhore a versão de prog03.c usando a função controladora de fluxo
’enquanto()’ while(), sintaxe:
enquanto (condicao)
inicio
ler(”%d”,numero)
fim
faça “condicao” controlar se o usuário fornceu um número. As funções
de controle de fluxo são estudadas no capı́tulo 4, dê um pulo até lá.
5. Faça um programa que solicite do usuário dois números inteiros, e devolva
o produto destes números. Ver o programa prog03.c A multiplicação em
C se faz usando o operador *.
6 scanf()
58
CAPÍTULO 3. NÚMEROS E LETRAS
6. Será que o programa de multiplicação está mesmo fazendo as contas certas? Verifique! Se o resultado for do tamanho de
BANCOS,
provavelmente o programa está errando...
Observação: 12 Agilidade perigosa do scanf()
Em um dos exercı́cios acima vimos a agilidade do scanf() que pode ser
muito útil, mas que é uma função perigosa. Há autores que dizem “para solucionar os problemas de scanf(), o melhor método consiste em não usar scanf()”.
Preferimos dizer, “deixe o uso de scanf() para quando você tiver um domı́nio
adequado de todos os seus efeitos colaterais”.
Ao ler 5 números digitados, separadamente, com espaços, nos permite que
forneçamos todos os números de uma seqüência, de uma única vez. Isto, inclusive, nos permite fazer correções, se tivermos cometido algum erro de digitação.
Experimente.
Poristo você deverá aprender a usar ’ler()’ (scanf()), mas deve observar
que ela oferece riscos. Se você não quiser que ela leia dois valores seguidos,
ela ainda assim poderá lê-los. Mas não se esqueça, tudo que o programa fizer é
consequência direta do que o programador tiver escrito. Portanto, se você souber
usar corretamente a linguagem, seus programas funcionarão corretamente e sem
mistérios. Aqui não existem mistérios, pode haver incompetência apenas.
Veja os programas7 (rode e leia)
prog04 2.c, prog04 21.c, prob scanf.c
Observação: 13 Programar bem
Um dos segredos para ser um bom programador, e isto vale para qualquer
linguagem de programação, consiste em ser organizado.
Nunca é pouco insistir que os programas devem ser bem comentados (evitandose a poluição visual). Um programa deve ser consequência de uma criteriosa
modularização e isto vai ficar claro no capı́tulo 5 do qual você deve fazer agora
uma rápida leitura e inclusive usá-lo como um manual complementar na leitura
do texto anterior. No capı́tulo 5 discutimos função, vá até lá rapidamente (e
volte logo).
As funções são o método de modularização da linguagem C.
Observação: 14 Instruções de formatação de dados.
• Em Linux Uma lista parcial dos identificadores de tipos de dados em C é:
tipo de dado
real
f
inteiro
d
caractere
c
vetor de car.
s
not. cientif.
e
Consulte info libc em LinuX para ver uma descrição mais completa,
quando sentir necessidade. Procure por Formatted Output, Formatted
Output Basics onde se encontram informações sobre printf().
7 em
BC altere o nome do programa prob scanf.c para prbscanf.c
3.1. BRINCANDO COM NÚMEROS EM C.
59
O sistema de ajudas dos pacotes computacionais quase que exigem que o
usuário já tenha alguma prática. Não desista nas primeira tentativas.
• Em BC
Abra um programa e nele escolha algum comando sobre o qual você deseja
informações. Digamos que seja printf(), o noso imprima(). Escreva na
área de textos este comando e coloque o cursor sobre ele.
Procure o botão Help.
(busca por tópico).
Clicando, cai um menu, escolha Topic search
Você será conduzido à famı́lia de funções que imprimem:
cprintf, fprintf, printf, ....
Passe uma vista. Não tente entender tudo de uma só vez. Volte a ler
quando necessário, quando precisar de uma informação especı́fica. Mas,
sobre tudo, vá até o final desta página do help, descendo com a seta para
baixo. Desça até o final.
Você vai encontrar Examples, os exemplos. Coloque o cursor sobre
printf example
dê enter, e você verá um programa completo mostrando como funciona
printf().
Você poderá passar o ratinho sobre o programa, realçá-lo. Vá até até o
Edit e escolha copy. Abra um editor de textos e no editor cole o programa,
e grave com o nome que lhe parecer adequado.
Você tem um exemplo que funciona. Teste! Alguns exemplos tem excesso
de informação. Apague alguma coisa do exemplo e rode e leia. Querendo
copie de novo... O help do BC é um pequeno tutorial sobre a linguagem,
você ganhará muito ao usá-lo.
O Help do BC é muito rico, você terá que se acostumar a linguagem concisa
e técnica. Mas sempre terá exemplos para rodar e comparar com o que
tiver lido. Como dissemos sobre a ajuda em Linux, o sistema de ajuda
quase que exige uma prática computacional. É preciso persistir no uso.
3.1.1
Leitura de dados
Desde o inı́cio estivemos alertando o leitor sobre os erros potênciais no uso da
ágil e poderosa função ler() (scanf()) e mesmo assim fizemos uso dela todo
o tempo com a desculpa de que ela tornaria os programas mais simples.
É esta razão que pela qual ela é usada com frequência pelos programadores.
Apesar dos crı́ticos que chegam a dizer que a solução para o scanf() é não
usá-la, ela segue em uso porque é prática.
Vamos mostrar-lhe uma alternativa segura, e ao mesmo tempo prática, ao
uso de scanf() representada pelo par de funções
• leia() (fgets())
60
CAPÍTULO 3. NÚMEROS E LETRAS
• converte palavra() (sscanf() )
Observe que deixamos entre parêntesis as funções originais de C.
Veja os programas prog03 8.c,prog03 10.c, prog03 11.c, com erros, e o
prog03 12.c em que os erros do anterior se encontram corrigidos.
Este método seguro de leitura de dados consiste no uso seguido das duas
funções: leia() (fgets()) e converte palavra() (sscanf()). A sintaxe delas
é:
Sintaxe:
palavra deposito[80];
leia(deposito, tamanho de palavra(deposito), entrada pdr);
converte palavra(deposito, ”%f”, &numero);
em que a variável deposito deve ser declarada com espaço suficiente para a
leitura dos dados.
Depois a função
converte palavra()
irá ao “deposito” pegar o que estiver lá contido e transformar no tipo de
dados que se deseja, observe o formator de dados. No exemplo acima estamos
transformando os dados contidos em “deposito” em ponto flutuante que é
um dos tipos de números fracionários em C.
deposito é um vetor de caracteres, não se esqueça de que C tem uma origem
humilde e sabia apenas mexer com números e caracteres.
Então iniciamos a entrada de dados colocando num grande depósito os caracteres que compõem os dados que nos interessam, depois a função
converte palavra
transforma os dados no tipo adequado.
Observação: 15 E o prático ?
Estamos propondo a substituição de uma única função, scanf(), por duas,
e dissemos que tudo isto ainda seria prático.
De fato, como estão as coisas, estamos trocando uma por duas...nada prático.
Para tornar prático o uso destas duas funções, crie um arquivo
entrada dados
contendo o esqueleto contido no box acima. Este arquivo já existe, ele se chama
entra dados.c
se encontra no disco junto com os demais programas, edite-o agora para ver
como foi criado e modifique-o para suas necessidades.
Se habitue a sempre usar a mesma variável deposito para receber dados, o
nome é até sugestivo.
Quando precisar fazer uma entrada de dados, importe para o programa o
arquivo entrada dados e siga programando tranquilamente.
Não se esqueça de trocar a formação de dados na terceira linha e o nome da
variável que vai receber os dados.
3.1. BRINCANDO COM NÚMEROS EM C.
61
Se a variável que for receber os dados for tipo
ponteiro
não use o
redirecionador de endereços
No exemplo, no box acima, foi preciso usar porque numero não é do tipo ponteiro.
Serão necessários agora dois toques para construir a entrada de dados. Prático,
não?
Este método se aplica a programas inteiros. Quando vamos fazer um novo
programa, nunca começamos de zero, sempre fazemos uma busca de algum programa que se encontre próximo do nosso objetivo, para isto colocamos palavraschave nos comentários iniciais dos nossos programas.
Veja o arquivo
esqueleto.c
que, no mı́nimo, é o nosso ponto de partida.
Análise de prog03XX
Vamos olhar de perto a sintaxe de cada uma dessas funções.
Lembre-se que no fragmento de programa acima pode haver mais funções
antes de leia() e que a declaração de variáveis tem que vir no inı́cio do bloco,
ver prog03 10.c.
Vamos analisar os parâmetros de leia() (fgets())
leia(deposito, tamanho de palavra(deposito), entrada pdr); tem os seguinte parâmetros:
• deposito, é um vetor de caracteres suficientemente grande para receber o
que se espera. É sua responsabilidade definir isto tendo em mente não perder espaço nem deixá-lo curto demais. Se o espaço for insuficiente, dados
vão se perder e C segue andando lampeiro. Outras linguagens parariam e
emitiriam uma mensagem de erro.
• tamanho de palavra(deposito) poderia ser considerado uma falta de otimização da linguagem porque o compilador poderia deduzir este número
inteiro da declaração de deposito. Mas isto tornaria o compilador mais
lento.
• entrada pdr informa ao compilador de onde vem os dados. Neste momento estamos lendo os dados via teclado, se você estiver com pressa de
ver como se lêem dados de um arquivo em disco, veja os programas da
série 20, prog20 2.c, por exemplo, em que entrada pdr define um arquivo em disco onde os dados serão gravados e prog20 3.c em que se
lêem dados contidos num arquivo em disco.
Vamos analisar converte palavra(), (sscanf()):
converte palavra(deposito, ”%f”, &numero);
Ela exige tres parâmetros:
62
CAPÍTULO 3. NÚMEROS E LETRAS
• deposito A variável onde os dados se encontram guardados, no nosso
exemplo um vetor de caracteres;
• o formatador de dados “%f” que aqui funciona como um operador para
mudança de tipo de dados;
• destino definitivo a variável onde os dados serão guardados, (corrigindo: o endereço da variável onde os dados serão guardados). Ver abaixo.
• observe a possı́vel necessidade de usar o redirecionador de endereço “&”.
Falamos possı́vel porque se a variável já for do tipo ponteiro, o redirecionador de endereço não pode ser usado.
3.2
Brincando com as palavras em C.
Resumo.
Existem dois tipos de dados básicos em C e na maioria das linguagens de processamento
de dados: números e caracteres. Uma palavra é uma seqüência de caracteres entre “aspas”,
com algumas exceções: há alguns caracteres que tem significado especial e que para serem
incluı́dos nesta definição é preciso algum esforço extra, como o caracter “\”que serve para
indicar ao compilador que depois dele vem outro caracter especial. Por exemplo, para fazer
uma mudança de linha, como você já viu nos programas anteriores, usamos “\n” dentro de
uma mensagem.
Neste parágrafo vamos aprender a lidar com caracteres e vetores de caracteres.
3.2.1
Palavras, macros, caracteres.
Se é verdade que tudo, dentro do computador é formado de “zeros” e “uns”8 ,
para nós humanos, o “computador” cria uma facilidade extra e expande este
universo estreito com auxı́lio de uma codificação para caracteres, e ainda cria
uma partição muito útil:
• Caracteres numéricos: 0,1,2, . . . , 9
• Os outros... completando alguma coisa da ordem de 256 = 28 caracteres.
O teclado do computador, tem, por exemplo 101 teclas. Mas “a” e “A”
são dois caracteres diferentes obtidos apertando ou não “shif” junto com
a tecla “a”. Com a tecla “ctrl” você pode obter novos caracteres e assim
por diante. O que caracteriza um caracter é sua escritura entre ’aspas’
simples. Assim ’3’ é um caracter, como ’a’ e “3” é um vetor de caracteres.
Tenha cuidado com estas combinações,
ctrl-X, alt-X
alguns destes caracteres de controle podem, simplesmente, travar o programa que você estiver usando...
alt-X parece ser sempre inofensivo, mas não é o caso de ctrl-X.
8 Literalmente
falando isto é falso hoje. O que era zero hoje é baixa voltagem, cerca de 2.5
volts, e o que era um hoje é alta voltagem, cerca de 5 volts...
3.2. BRINCANDO COM AS PALAVRAS EM C.
63
• Aglomerados destes dois tipos de dados formam as macros, e os vetores
de caracteres, veja a observação a respeito destes dois tipos de dados. De
forma simplificada, o que estiver entre “aspas” é vetor de caracteres.
Alguns destes caracteres têm efeito especial, uns você pode usar diretamente,
outros ficam escondidos para uso interno do sistema operacional mas podem ser
acessados com alguma “sabedoria” desde que isto seja feito “sabendo-se o que
se está fazendo”. Você já sabe que “ctrl alt del” serve para desligar a máquina,
por exemplo...
Exercı́cios: 7 Verificando os caracteres
Os exercı́cios desta seção usam conceitos que somente vamos estudar no
capı́tulo 4, salte até este capı́tulo quando sentir necessidade, ou ignore os conceitos ainda não estudados. Basicamente as funções para(), se() e o conceito
de laço é que precisamos do capı́tulo 4, aqui.
1. Leia a biblioteca ambiente.h e analise o programa ascii 2.c, (rode e
leia o programa) para ver como se pode acentuar. Logo no começo de
ambiente.h se encontram as macros para definir as nossas letras acentuadas.
2. Leia o programa ascii.c e procure entender o comando de impressão.
Talvez você deva rodar o programa e depois voltar a lê-lo. Ignore por
enquanto a função para, ela vai ser estudada no próximo capı́tulo, ela é
responsável pela “evolução” da variável n desde 0 até 255. Este trecho do
programa se chama um laço.
3. Claro, você não conseguiu ver nada do que o programa imprimiu... 256
linha rodaram em frente aos seus olhos. Solução: faça uma restrição
dentro do para de modo que o programa imprima umas 10 linhas, (ver
ascii 1.c).
4. Melhore o programa ascii 2.c a seu gosto, incluindo mensagens na saı́da
de dados e na entrada de dados. Use muitas palavras acentuadas para
aprender a passar parâmetros para “imprima()”.
Veja que existe uma complicação9 extra. Para nos comunicarmos com a
máquina precisamos de uma linguagem, como C, ou mesmo antes das linguagens, se encontram os sistemas operacionais, LinuX, FreeBSD, DOS etc... Ora
as linguagens são feitas de palavras10 , quer dizer, “aglomerados” de caracteres
aos quais se fez uma associação com uma rotina da linguagem: é isto que você
usa quando digita dir e enter na linha de comandos. O mesmo se dá quando
você executa a linha
gcc -v numero.c -onumero
9 sem
10 veja
preconceitos, as complicações são necesárias, uma vida simples é insuportável...
observação a respeito
64
CAPÍTULO 3. NÚMEROS E LETRAS
criando uma nova palavra, numero que agora o sistema operacional reconhece
como incluida no seu vocubulário e associada a uma rotina gravada no HD,
provavelmente em sua área de trabalho.
Uma “nova”especificação se vem estabelecendo, um modismo. Palavras
deste tipo que acabamos de descrever passam a ser chamadas de “macros”.
As macros servem para definir variáveis, nomes de comandos e funções. A palavra macro é antiga, passou para o esquecimento, e agora volta a ser usada com
frequência como um sinônimo de variável.
Vamos usar macros com mais freqüência a partir do capı́tulo 5 quando
começaremos a criar funções. Agora vamos trabalhar com vetores de caracteres.
Vocabulário: 5 caracter, palavra, macro, vetor de caracteres, variável,
sı́mbolo, identificador
Nós usamos palavra num sentido pouco usual em computação, e foi proposital.
Aqui existe uma ambiguidade, “palavra” é usada também como medida da informação
junto com bit, byte, apenas mal definida, existem palavras de 16 bits e de 32 bytes. A idéia
era que
• um bit, seria a menor medida da informação,
• um aglomerado de 8 bits formaria um byte. Isto pode ser pensado fisicamente como
um integrado contendo 8 diódos que possam energisados criando um total de 28 possibilidades;
• e 16 bytes formaria uma word, que é palavra em inglês.
Mas a prepotência de certas firmas computacionais anarquizou esta convenção porque
lançaram no mercado máquinas, ou “sistemas”, arrogantemente dizendo, que a word era de
32 bytes...
Uma outra ambiguidade persiste, nas linguagens humanas se tem o conceito de palavra
reservado para um aglomerado de letras e possivelmente números, como “Pentium III”, para
representar os objetos ou as idéias que nos cercam e com os quais nos comunicamos formando
as frases de nossa linguagem.
Ainda existe o conceito de macro em computação, também difuso quanto à sua concepção.
Você pode encontrar o uso de macro, identificador, nome, sı́mbolo usados como sinônimos.
• caracter qualquer um dos sı́mbolos que é possivel acionando uma tecla (ou
um conjunto de teclas de uma só vez). Como o famoso ctrl-alt-del. O
programa ascii.c lhe mostra o que são caracteres, leia e rode o programa.
• identificador é usado para fazer referência ao nome de uma variável e
também de funções.
• macro é usada com freqüência para representar o nome de uma rotina,
de um programa.
• sı́mbolo é usado como sinônimo de identificador, mas pode ser encontrado
com como sinônimo de variável.
• variável tem uma concepção mais estável, tem o significado usado em
Matemática.
• Word Uma medida da informação. Ambı́gua, ora signifca 16 bytes, outras
vezes 32 bytes.
3.2. BRINCANDO COM AS PALAVRAS EM C.
65
• vetor de caracteres Um aglomerado de caracteres enfeixados entre aspas. Alguns dos caracteres de controle não podem ser incluidos porque eles
tem uso especial para comunicação com o sistema operacional, como “\n”
que significa “nova linha”.
Leia o arquivo traducao.h e verá alı́ um conjunto de macros. As que se
encontram a esquerda são as nossas que tem por definição aquelas que se se
encontram à esquerda, usando a macro define do C.
Quando você escrever
inteiro numero;
você informa ao C que está criando uma nova macro, designada pelo aglomerado
de letras {n, u, m, e, r, o}, classificada como variável, que deverá ser associada
a um endereço de memória e que deve guardar um número inteiro.
Mas quando você escrever “numero”, o compilador C vai identificar este
objeto como um vetor de caracteres e ignorar o que se encontra entre as aspas
mas fazendo-lhes uma associação de endereços sucessivos de memória se você
tiver definido tudo direitinho.... é isto que se chama um vetor de caracteres .
Há algumas regras para definir
macros, variáveis, sı́mbolos, identificadores
,
• a primeira é que elas não devem estar entre “aspas”, tudo que estiver entre
aspas é um vetor de caracteres;
• depois elas não devem começar com um número;
• finalmente os caracteres
+
- / * $ % # @ ! ^ \
devem ser evitados e algumas vezes são proibidos.
O caracter - não é em geral proibido, mas é um mau hábito usá-lo, porque
se confunde com a subtração.
O caracter _ também não é proibido, mas tem em um significado especial que
você ainda verá no momento certo, e deve também ser evitado como primeiro
elemento ou último.
Em C as dois identificadores Abracadabra e abracadabra são diferentes,
quer dizer que a linguagem é sensı́vel à diferença entre letra maiúscula e minúscula.
Vamos seguir, entretanto, o hábito linguı́stico, leia “jargão”, que prefere a
palavra “identificador” à palavra “macro”.
Um identificador será uma macro para nós apenas quando receber uma
definição(quando houver um algoritimo associado ao identificador).
66
CAPÍTULO 3. NÚMEROS E LETRAS
3.2.2
Vetores de caracteres.
Usamos vetores de caracteres para
• mensagens construir as mensagens de um programa.
• receber dados Ver a variável deposito que usamos em conjunto com
leia(),converte palavra() . Veja o arquivo entrada dados.
Há programas que se especilizam apenas em manipular mensagens, por
exemplo:
1. Um programa que gerencie um pacote computacional, basicamente irá
emitir mensagens para informar ao usuário quais são as possibilidades do
programa, exibir o menu.
2. Um módulo de pesquisa de assuntos numa página da Internet ou num
banco de dados especializado em “assuntos”, irá receber palavras que o
usuário queira fornecer para fazer uma busca dentro do programa site ou
nos sites que tenham ligações com este em que o usuário estiver conectado.
3. Você pode estar interessado em construir um programa que cadastre senhas. Uma senha seria um vetor de caracteres. Observe que senhas não
são macros. As macros devem receber dados ou conter dados.
Originalmente, em C, não havia este tipo de dados, ele foi adicionado posteriormente com a criação da biblioteca string.h, da qual fazem parte as seguintes
funções11 :
Vocabulário: 6 Algumas funções definidas em string.h copia de palavra
(strcpy()) ;concatena palavras() (strcat()) ;tamanho de palavra()
(strlen()) ;compara palavras() (strcmp()); compara()
• copia de palavra(), strcpy() Sintaxe:
copia de palavra(var, “palavra”);
Se var tiver sido definida com o tamanho adequado irá receber “palavra”.
Observe a diferença entre a atribuição numérica e esta entre variáveis do
tipo ponteiro. Para números vale
x = 3;
se x for uma variável de tipo numérico. Você terá feito a atribuição do
valor 3 à variável x. Para cadéias de caracteres não é assim que se faz
atribuição. Temos que usar copia de palavras() ou (strcpy()).
• concatena palavras(), strcat() Sintaxe:
concatena palavras(primeiro,segundo);
11 leia
comandos, se quiser
3.2. BRINCANDO COM AS PALAVRAS EM C.
67
Se a variável primeiro contiver a string “José” e a variável segundo
contiver a string “Maria” então depois da execução de
concatena palavras(primeiro,segundo);
a variável primeiro conterá o valor “JoseMaria”. Claro, se você desejar
um espaço entre os dois nomes, você deverá executar primeiro:
concatena palavras(primeiro,’’ ’’);
veja abaixo.
Esta função serve para inicializar variáveis também. O trecho de programa
seguinte fará com que primeiro contenha “Jose Maria”:
palavra primeiro[30];
concatena palavras(primeiro,’’Jose’’);
concatena palavras(primeiro,’’ ’’);
concatena palavras(primeiro,’’Maria’’);
Ver prog04 1.c.
• tamanho de palavra(), strlen() Sintaxe:
tamanho de palavra(var)
Se a variável var contiver uma string definidas por uma seqüência de n
caracteres, será o inteiro positivo n a resposta de
tamanho de palavra(var) − > n ∈ N
• compara palavras(), strcmp() Sintaxe: compara palavras(var1,var2);
com duas variáveis do tipo string. Esta função da linguagem C tem um
uma sintaxe perversa. O resultado de
compara palavras(primeiro,segundo)
será zero se os vetores de caracteres contidos nas variáveis primeiro,
segundo forem iguais. Veja o seguinte pedaço do programa prog04 2.c
palavra primeiro[30],segundo[50];
imprima(”A primeira palavra − − − >”);
leia(primeiro, tamanho do(primeiro), entrada pdr);
imprima(”A segunda palavra − − − >”);
leia(segundo, tamanho do(segundo), entrada pdr);
imprima(”% d\ n”, “O resultado da comparação é:”,
compara palavras(primeiro,segundo));
Vai resultar em zero se as variáveis primeiro e segundo contiverem a
mesma string, apesar da definição diferente das variáveis.
68
CAPÍTULO 3. NÚMEROS E LETRAS
• compara() definida em ambiente corrige a perversão de compara palavras()
(strcmp()).
compara(primeiro,segundo) será zero somente se o conteúdo de primeiro e segundo forem diferentes.
Aqui podemos aproveitar para sugerir-lhe um método que todo programador
experiente:
Observação: 16 Reutilização
Todo programador profissional, quando vai escrever um novo programa, começa
por examinar entre os seus programas um parecido com aquele que deseja produzir.
Afinal, porque começar tudo do nada!
Se tiver que comparar palavras, vá buscar em alguma biblioteca programas
que comparem palavras algum apropriado porque assim você vai se relembrar
qual é sintaxe de compara palavras() e sobretudo como foi que fez uso desta
função a última vez que precisou. Em geral apagamos quase todo o programa
para aproveitar quase que apenas o esqueleto...
É isto que se chama reutilização de programas.
Deveriamos usar a palavra reciclagem, embora os americanos venham usando
a palavra reutilização.
A reciclagem de programas que já foram testados e que ainda funcionam
bem, economiza tempo de trabalho.
Cabe relembrar o presença dos comentários nos programas... eles podem
criar condições para reciclagens eficientes de antigos programas.
Os nossos programas todos foram feitos com este objetivo em vista, inclusive
todos os mais recentes tem uma linha
Palavras chave
para faciliar a busca em LinuX. Se eu quiser procurar um programa que acesse
arquivos, na linha de comandos, executo:
grep arquivo *.c
e vou ter uma lista de programas que mexem com arquivos.
Leia a nossa biblioteca ambiente.h onde está definida a função compara()
que inverte a lógica perversa de strcmp(). Veja como é fácil corrigir aquilo que
não nos agrade na linguagem C.
Quando precisamos de comparar a igualdade entre palavras, usamos compara()
em vez de strcmp().
Observação: 17 Utilização correta de strcmp()
Mas tem lógica em compara palavras() strcmp().
Veja o seguinte trecho de programa
3.2. BRINCANDO COM AS PALAVRAS EM C.
primeiro = ’’aaa’’;
segundo = ”bbb”
terceiro = ”ccc”
pri = strcmp(segundo, primeiro)
seg = strcmp(terceiro, segundo)
ter = strcmp(primeiro, terceiro)
imprima(”%d”,pri);
imprima(”%d”,seg);
imprima(”%d”,ter);
69
”aaa”, ”bbb”
”bbb”, ”ccc”
”ccc”, ”aaa”
1
1
-1
Nas três últimas linhas você tem o valor das variáveis
pri,seg,ter
que guardam o resultado das comparações feitas.
Observe a ordem como as comparações são feitas por
strcmp(maior, menor)
para que o resultado seja positivo.
Quando você usar com este objetivo, em um programa, deixe um comentário
dizendo que é este o uso.
Exercı́cios: 8 Vetores de caracteres
1. No programa prog04 1.c a variável primeiro é definida com um valor
inicial, um vetor de caracteres vazio. Se não for assim C vai colocar
lixo não reciclável em seu interior. Experimente modificar o programa
eliminando a inicialização da variável primeiro e analise o resultado.
2. Explique (tente explicar!) onde, exatamente, e por que, no programa
prog04 1.c, o compilador enfia lixo se a variável não for inicializada na
declaração.
3. Rode o programa prog04 2.c e analise a diferença entre
tamanho de palavra(), strlen()
e
tamanho da(), sizeof().
Uma, conta o número de caracteres do conteúdo da variável, a outra, diz
qual é tamanho (tamanho planejado) da variável.
4. Análise, ao rodar o programa prog04 2.c um defeito: pede que você aperte
uma tecla para continuar, mas não espera... Responsável: ler() ou
scanf(). Veja a solução do problema em prog04 21.c. Leia o comentário
também.
5. O programa prog04 3.c compara duas variáveis para determinar se são
iguais. Verifique que isto é independente da definição das variáveis, claro,
desde que sejam do mesmo tipo.
6. Corrija prog01.c para que ele leia o seu nome completamente, (se ainda
não o tiver feito...)
70
CAPÍTULO 3. NÚMEROS E LETRAS
7. Leia e rode prog04 5.c , ele é um tutorial sobre o uso de strcmp(),
compara(). Você vai ver que strcmp() é uma importante função para
ficar em background12 , como fizemos em compara().
12 você
está vendo aqui uma forma importante de usar C, construindo outras ferramentas
com as poderosas funções da linguagem.
Capı́tulo 4
Controle lógico do fluxo
Aqui começa programação.
Vamos estudar a lógica por trás de qualquer programa, em qualquer linguagem
de programação do tipo imperativo.
As palavras chaves deste capı́tulo são:
se()
se()/ou entao
escolha()
enquanto()
para()
pare
voltar()
if()
if()/else
switch()
while()
for()
break
return()
Com estas palavras se fazem programas, portanto com elas podemos construir
as “frases” com que vamos instruir um computador a processar os dados que
lhe entregamos.
Vamos estudar a sintaxe desta comunicação, neste capı́tulo, é isto que muitas
vezes é chamado de lógica.
4.1
O condicional se() (if())
Vamos começar com duas estruturas de contrôle do fluxo:
1) se()
se(certo)
if()
if(certo)
faça();
faça()
2) se() ou entao
se(certo)
if() else
if(certo)
faça();
faça();
ou entao
else
faça outra coisa();
faça outra coisa();
Observe o uso do ponto-e-virgula no caso if() else, antes do else tem
ponto-e-virgula. Esta observação é importante para programadores em Pas71
72
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
cal porque (no caso do Pascal) não tem ponto-e-virgula antes do else. Em
C, tem !
A função se() recebe um parâmetro e o avalia. Se esta avaliação resultar em
verdadeiro1 o comando associado será executado. Veja, por exemplo, o seguinte
bloco
imprima(Forneça-me dois numeros );
imprima(O segundo numero deve ser maior do que o primeiro);
se(numero1 < numero2 )
{
imprima(OK ! voce obedeceu ao combinado);
}
Este bloco está implementado no programa prog05.c. Leia e rode o programa.
Esta comunicação está claramente incompleta. Falta uma alternativa: . . . se
o usuário do programa tiver fornecido um segundo número menor do que o
primeiro ?
Aqui entra o ou entao (else). Vamos completar o esquema.
imprima(Forneça-me dois numeros );
imprima(O segundo numero deve ser maior do que o primeiro);
se(numero1 < numero2 )
{
imprima(OK ! voce obedeceu ao combinado);
}
ou entao
imprima(Voce desobedeceu ao combinado ! )
Se a condição testada pelo se() se verificar falsa, o programa se conecta,
imediatamente, a um ou entao que se encontre depois se().
Se você, por engano, colocar algum comando entre o final do se() e o
ou entao haverá uma mensagem de erro indicando isto. O prog05 1.c está
preparado com este erro, basta apagar o comentário antes do ou entao. Experimente !
Em geral (nem sempre) usamos um par
se(condicao) ou entao
1 leia
observação sobre verdade e falsidade.
4.1. O CONDICIONAL SE() (IF())
73
rode o programa prog05 1.c que implementa o se() ou entao. Depois leia o
programa e volte a rodá-lo...
Exercı́cios: 9 se()/ou entao
1. Rode, leia, rode o programa prog05 1.c.
2. Apague o comentário da linha que precede o ou entao e compile o programa. Ele vai reclamar dizendo que há um erro antes do else (ou entao).
3. Altere o programa prog05 1.c para ele diga2 que sabe calcular o fatorial
de n e se o usuário apresentar um número maior do que 13 responda que
passou da capaciade da máquina... solução prog05 2.c
4. Estude prog05 2.c e veja que tem voltar duas vezes com valores diferentes. Por que ? Observe que prog05 2.c é um exemplo de planejamento
vazio... veja o próximo exercı́cio.
5. Planeje um programa para calcular a divisão exata de dois números inteiros, o dividendo pelo divisor. Se o usuário fornecer um divisor maior
do que o dividendo diga que é impossı́vel, ou entao que a o programa
ainda não sabe fazer esta conta, pedindo, gentilmente, que volte depois.
solução prog05 3.c
6. Altere o programa prog05 3.c pedindo que o usuário forneça mais alguns
números e faça mais alguns testes para treinar o uso de
se()/ou entao
experimente omitir voltar. Experimente voltar com outros valores inteiros.
Os programas prog05 1.c prog05 2.c prog05 3.c todos tem a estrutura
lógica da figura (fig. 4.1) página 74,
Há um teste, indicado na (fig. 4.1) como condição e, em qualquer hipótese,
Verdadeira ou Falsa, o programa deve parar. Então o comando voltar tem
que aparecer tanto dentro do se() como dentro do ou entao. Este é um erro
comum de lógica de programação, o programador esquecer de fazer uma análise
lógica do fluxo, e consequentemente, se perder nas saı́das do programa.
Experimente, apague um voltar nestes programas e compile.
Veja mais este exemplo, com fatorial3 .
if(n<10)
{
fatorial(n);
}
2 logo veremos um programa para calcular fatorial, por enquanto faça apenas o planejamento...
3 precisamos de enquanto() para calcular o fatorial. Por enquanto será apenas planejamento
vazio. . .
74
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
F
condição
pare();
V
pare();
Figura 4.1: se() ou entao
irá calcular o fatorial de n se o número n for menor do que 10. Como não existe
o fatorial de números negativos, este algoritmo precisa ser melhor especificado
pois ele não duvidaria em calcular o fatorial de −1 se o número n = −1 lhe
fosse apresentado.
Precisamos de mais uma estrutura de controle de fluxo para resolver o problema do fatorial. Se estiver curioso, leia o programa fatorial.c mas você pode
resolver os exercı́cioso desta seção sem resolver inteiramente o problema: planejamentos vazios. Se habitue a esta idéia que ela é essencial em programação.
Sempre fazemos planejamentos vazios numa primeira etapa, e esta seção traz
vários exemplos. Sem brincadeira, eles são importantes para uma a construção
da lógica do problema.
Exercı́cios: 10 Fatorial incompleto Escreva o programa fat inc.c que execute
o algoritmo incompleto acima. Veja “solução” no disco...
Vamos agora melhorar o planejamento do fatorial.
se(n >= 0)
inicio
fatorial(n);
fim
se(n<0)
inicio
imprima(‘‘nao existe o fatorial de %d’’,n);
fim
4.1. O CONDICIONAL SE() (IF())
75
Exercı́cios: 11 Melhorando o fatorial
Transforme o esquema gráfico acima no programa fat inc02.c, a segunda
versão do fatorial. Veja a solução no disco. O programa fat inc02.c faz uso
de funções, vá ao capı́tulo 5 para uma breve leitura sobre funções para entender
a solução.
Existe um esquema gráfico, chamado fluxograma, que nos permite uma
visualização do fluxo lógico.
As figuras (fig. 4.2),(fig. 4.3) (fig. 4.4) mostram a evolução de um programa,
ver páginas 75, 76, 77.
A figura 4.2 representa os dois primeiros retângulos acima: um único se().
A figura 4.3 contém o planejamento para o caso de n < 0 indicando que o
processo deve parar, com uma alternativa (else) para caso de que n! ultrapasse
a capacidade da máquina.
Observe que no primeiro caso o processo para, apenas se n > 10, mas a
“máquina” tentaria calcular (−1)!, o fatorial de número negativo.
A figura 4.4 contempla uma entrada de dados, é um programa completo.
Veja na figura (fig. 4.2) página 75,
faça alguma coisa;
faça outra coisa;
se (condicao)
{
pare();
}
faça mais alguma coisa;
continuando;
Figura 4.2: Fluxograma do se()
Quando for necessário usar dois se(), um completando o outro, então possivelmente é melhor usar se() ou entao():
se(verdadeiro) faça ou entao faça outra coisa
O exemplo do fatorial ficaria agora melhor descrito assim:
se(n >= 0)
imprima(“%d \ n “,fatorial(n));
ou entao
imprima(“%s \n”,
“nao ha fatorial de numeros negativos”);
76
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
F
F
(n < 0)
V
pare();
(n > 13000)
V
fatorial(n)
Figura 4.3: Fluxograma com dois se()
supondo que fatorial() seja uma função definida em alguma bilioteca.
Os fluxogramas nos trazem uma lição importante para o planejamento de
sistemas:
rapidamente uma folha de papel será insuficiente para fazer o fluxograma de
um problema.
Isto significa que você deve dar um “zoom” no problema... quer dizer, resolver as questões maiores do problema, as grandes decisões, estas sim representadas num primeiro fluxograma.
Um exemplo fala mais que mil palavras. Vamos fazer o fluxograma para
resolver uma equação do segundo grau.
• Uma equação do 2o grau tem tres coeficientes, logo tres entradas de dados;
quer dizer tres retângulos no fluxograma.
• Temos que calcular o “delta”, um cı́rculo no fluxograma, porque os cı́rculos
representam ações.
• Pontos de decisão, se o “delta” for positivo, nulo ou negativo, tres pontos
de decisão (dois diamantes),
• Tres cı́rculos representados as tres possibilidades finais: duas raizes, uma
única raiz, nenhuma raiz real.
Total 10 figuras geométricas, um desenho complicado difı́cil de ser colocado
numa folha de papel. Pior, a visualização seria pobre que é o contrário do
objetivo.
4.1. O CONDICIONAL SE() (IF())
77
n
(n < 0)
(n > 13000)
fatorial(n)
pare();
Figura 4.4: Fluxograma com dois se(), uma entrada e uma saı́da dados
Conclusão: devemos visualizar o problema em grandes linhas com um
fluxograma-genérico
e depois fazer
fluxogramas-locais
para cada um dos blocos do genérico.
No caso do problema das raizes o estudo genérico seria:
• Entrada de dados Primeiro uma única representação para entrada de dados.
• Decisão uma única, verifica se ∆ ≤ 0 e conclue se tem raiz, (sim ou não).
Remete ao cálculo das raizes ou a uma mensagem de impossibilidade.
Veja o fluxograma desta análise lógica na figura (fig. 4.5) página 78,
O bloco (lógico)
calcule as raı́zes
esconde vários cálculos, você está diante de um planejamento vazio... programamos em equipes. Este é a idéia inicial, depois cada um dos membros da
equipe irá tratar do seu módulo.
Há funções escondidas dentro do fluxograma, mas a idéia geral do processamento está expressa.
Existe uma função para calcular o ∆ escondida dentro do ponto de decisão.
Tem uma função para calcular as raizes escondida dentro da frase “calcula as
raizes”. Há seis linhas de entrada de dados escondidas na frase “entrada de
dados”.
Isto a gente resolve depois, primeiro vem o planejamento. Claro, tem muito
sistema que nunca passa do planejamento e da promessa.
Vamos escrever o planejamento do programa que corresponde a este fluxograma.
78
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
V
(a,b,c)
Delta > 0
F
Calcule
as raízes
pare();
Figura 4.5: Fluxograma da equação do segundo grau.
entra dados();
{
delta = calcula delta(a,b,c)
se(delta >= 0)
{
calcula raizes(a,b,c);
}
ou entao
{
imprima(“equacao impossivel”);
}
voltar 0;
}
Depois iremos escrever as tres funções
• entra dados(),
• calcula delta(),
• calcula raizes()
Quando começamos a produzir os programas fomos encontrando algumas
dificuldades o que nos levou a construir tres versões do programa, em cada
caso resolvendo uma parte do problema. Veja que em seg grau.c existem três
funcoes para entrar os dados, uma para cada um dos coeficientes. Seguramente
tem forma melhor de resolver este problema e você será convidade no próximo
bloco de exercı́cios, a ajudar o autor, com uma solução melhor.
4.1. O CONDICIONAL SE() (IF())
79
Veja os programas
seg grau01.c, seg grau02.c, seg grau03.c
e a versão final seg grau.c. Rode os programas para ver como foi sendo
feito planejamento, e depois leia cada um deles.
Exercı́cios: 12 Equações do segundo grau
1. Faça o programa equ seg01.c para executar o planejamento sugerido acima.
Solução no disco.
2. Complete o programa equ seg01.c para que ele resolva equações do segundo grau.
3. imprimindo o falso Experimente o programa logica01.c, rode-o e depois
leia-o. (No BC procure logi*.c)
4. imprimindo o falso Observe a expressão
(primeiro==segundo)
dada como parâmetro ao imprima. Substitua (primeiro==segundo) por
(primeiro=segundo), rode o programa e veja a diferença. Substitua também
como parâmetro do se e descubra que diferença isto faz.
5. Estude o programa fatorial04.c. Experimente retirar algumas chaves
(pares de chaves) para ver a diferença. Ignore por enquanto as outras
versões de fatorial.
6. Em logica01.c temos a função se() que irá avaliar uma expressão (observe que o parâmetro é abstrato...). Experimente fornecer ao programa
dados não numéricos, frases. Em BC ver logi01.c.
7. “(primeiro == segundo)” é uma operação lógica seguida de uma avaliação: o parêntesis. Coloque um alguns “imprimas” em logica01.c para
testar os valores de (primeiro == segundo).
8. Rode diversas vezes o programa e tente encontrar uma lei de formação
para o valor de (primeiro==segundo)
9. No programa logica01 1.c usamos
(primeiro = segundo)
como parâmetro do if(). Verifique que o compilador gcc vai avisá-lo do
risco, mas o programa vai rodar. Se você fornecer ao programa os números
1,0
nesta seqüência, ele responderá que os números são iguais. Por que ?
Veja a resposta nos comentários do programa.
10. erros que enganam o compilador. Rode um programa com o seguinte comando:
imprime(primeiro=segundo)
80
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
depois de definidas as variáveis, naturalmente, e observe o comentário
“absurdo” do compilador.
Objetivo do exercı́cio: prepará-lo para procurar erros em local diferente do
apontado pelo compilador...
11. efeitos colaterais. Escreva um programa que “rapidamente” iguale duas
variáveis inteiras, se elas forem diferentes, e nada faça se elas forem
iguais.
Lição: 2 Erros lógicos e de sintaxe. Num dos exercı́cios anteriores lhe pedimos
que substituisse (primeiro==segundo) por (primeiro=segundo). Aqui temos
um exemplo de erro lógico que não é erro de sintaxe.
A expressão (primeiro=segundo) se constitui de dois operadores:
• operador atribuição,
• operador avaliação4 .
No nosso sistema de computadores seqüenciais,(Von Neuman) a CPU tem
que fazer operações seguidas começando sempre da parte interna de uma expressão no modelo matemático de função composta:
(entrada) x :
x 7→ f (g(h(x))) = f (g(y)) = f (z) = w
(saı́da) w
(4.1)
(4.2)
(4.3)
A CPU se utiliza de um sistema de memória onde cálculos intermediários
são guardados. Se você tiver a curiosidade de ler um trecho de programa em
assembler, verá, uma lista de operações semelhante à sequência de equações
acima, apenas usando palavras como sto, abreviação de store, do inglês, que
significa guardar, seguida de um endereço de memória.
Hoje isto continua, apenas o programador escreve numa linguagem de alto
nı́vel que cria o programa em assembler, quando compila o código escrito pelo
programador.
Qualquer linguagem de processamento (do tipo imperativo) faz uso deste sistema, de acordo com o modelo acima descrito, no cálculo de w,
w é conseqüência dos cálculos intermediários sobre as variáveis y, z. Sendo
w a última, é passada para o “standard output”, (saı́da padrão) que pode ser
• o próximo comando a ser processado, (observe que x está sendo passado
para o próximo comando, assim como y)
• o vı́deo;
• a impressora;
4 alguns
autores traduzem ao pé da letra do inglês “evaluation”, que em português é “avaliação”, por “evaluação”, palavra inexistente...
4.1. O CONDICIONAL SE() (IF())
81
• um arquivo em disco, etc...
No caso (primeiro=segundo), o resultado desta operação composta é o valor de segundo. Porque a operação mais interna é a atribuição que, ao ser
executada, produz este valor (faz parte da engenharia do compilador C).
Todas as operações ao serem executadas produzem valores e este é o segredo
dos efeitos colaterais: nem sempre este valor produzido é claro, muitas vezes
sendo um valor secundário efetuado por uma operação primitiva pode dar um
ganho no programa, em velocidade5 .
Essencialmente verdadeiro, tudo isto. Muito bonito se não produzisse algoritmos difı́ceis de serem lidos e interpretados. Esta velocidade pode ser consumida depois durante o trabalho de manutenção de um programa cheio de atalhos
turtuosos.
Embora “se (primeiro=segundo)” possa ser considerado um erro de sintaxe, o gcc6 não tem condições de detectar isto, uma vez que o resultado desta
operação, sendo uma expressão válida para o gcc, pode ser passada como parâmetro
à função se().
Exı́mios programadores usam estes artifı́cios para conseguirem melhor desempenho em seus programas e infelizmente não conseguem mais entendê-los
duas ou tres semanas depois... (nem eles e nem os seus companheiros de equipe),
o que dá existência, provavelmente, a sistemas operacionais bonitos, mas emperrados, e cheios de erros... praticamente impossı́veis de serem encontrados,
até porque não são erros, são os chamados efeitos colaterais .
Os efeitos colaterais7 são um risco em programação e sem dúvida devem ser
evitados a todo custo.
Programação não precisa mais ser “econômica”, como nos primeiros dias
de sua existência. A economia a ser feita deve ser no trabalho do programador
que, é, digamos assim, o “item”caro nesta área de trabalho. Computadores são
relativamente baratos, tem bastante memória e velocidade, o que permite que os
programas possam ser mais legı́veis e fáceis de serem mantidos. Programação é
coisa séria, não é brincadeira, de um programa mal feito podem depender vidas
ou eleições.
Não podemos deixar de convidá-lo a fazer experiências diversas para descobrir detalhes sobre os efeitos colaterais ... eles são múltiplos e trazem preciosas
lições que podem evitar horas perdidas na correção de programas, muito em particular a troca de == por =. São exemplos dos tais insetos, (bugs), que infetam
programas enormes.
C é uma linguagem imperativa, quer dizer que nela existem apenas duas
opções: Dada uma relação, ela será:
• verdadeira ou, exclusivamente,
5 mas pode ser lida por uma função como scanf(), é preciso saber reconhecer isto ou evitar
de usar scanf()
6 vamos escrever sempre “gcc” em vez de “compilador da linguagem C de agora em diante
7 É difı́cil se livrar totalmente de efeitos colaterais, mas quando usados eles devem ser
documentados.
82
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
• falsa.
Para o gcc, falso fica caracterizado pelo zero, qualquer outro valor diferente
de zero representa o verdadeiro. Este é um ponto que você deve incorporar em
sua lógica pessoal ao se tornar um “C-programmer”...
Exercı́cios: 13 Efeitos colaterais
Programar sem “efeitos colaterais” é difı́cil. No momento em que você se tornar um bom programador, dificilmente vai evitar de fazê-lo, entretanto, não se
esqueça de que uma boa companhia para um “efeito colateral” é um comentário,
que explique o que está acontecendo, inclusive para você mesmo, algum tempo
depois...
Nos exercı́cios abaixo estamos lhe dando exemplos de “efeitos colaterais”,
habitue-se, aqui nos exercı́cios, a escrever o código incluindo os comentários
explicativos, mesmo que o exercı́cio não lhe peça para fazê-lo.
1. Escreva um pequeno programa, (reciclagem de logica01.c), para testar
que possı́veis combinações de dos operadores =, ==, tem sentido, como
em:
(primeiro==segundo=primeiro)
e determine o valor que resulta destas combinações. Use printf() para
ver o resultado. Solução logica02.c
2. Construa uma função que faça uso útil dos efeitos colaterais obtidos na
questão anterior, não se esquecendo da documentação que deixe claro o
que faz o programa. Solução logica02.c
3. Construa um programa que verifique se dois vetores de caracteres fornecidos (duas senhas) são iguais ou diferentes. Use strcmp() em vez de ==.
Solução logica03 1.c.
4. Faça uma nova versão do programa logica03 1.c usando a função compara()
definida em ambiente.h.
5. Construa uma função em que sempre o teste do se seja zero, (falso),
qualquer que sejam os valores atribuidos às duas variáveis que se peçam
ao usuário.
Estaremos enganando o usuário entao ?
6. Rode e leia sonho.c. Faça outros programas conversacionais, como sonho.c,
para adquirir prática com
se()/ou entao.
Veja sonho01.c.
4.2. MÚLTIPLAS ESCOLHAS.
4.2
83
Múltiplas escolhas.
Para escolhas múltiplas gcc tem, a estrutura:
escolha() - switch()
que vamos estudar neste parágrafo.
As palavras reservadas, em português e em inglês para construir
escolha() estão abaixo em correspondência:
escolha(variavel) inicio caso valor pare
fim
switch(variavel)
{
case valor break }
A função escolha() recebe uma variável e testa os seus valores contra uma
seqüência planejada pelo programador.
O modelo é o seguinte:
escolha(variavel)
inicio
caso valor1: comando11;... ;pare;
caso valor2: comando21;... ;pare;
padrao:
comandop1;... ;pare atenção
caso valorn: comandon1;... ;pare;
fim
A palavra-chave caso marca o inı́cio de um novo teste e de uma nova
seqüência de comandos que devem ser executados caso o teste se revele positivo.
Observação: 18 Uma cascata de execuções
A palavra-chave pare é essencial no uso desta estrutura, se ela não estiver
presente, os casos seguintes serão executados, depois do primeiro teste positivo.
Observe que você pode fazer uso desta facilidade quando quiser que depois
que uma opção verdadeira for identificada, uma seqüência de outras sejam executadas.
Neste caso elimine o pare, mas não se esqueça de colocar um comentário
indicando que deseja que todas as opções sejam executadas portanto eliminou o
pare.
Observação: 19 Sugestão: Arquivos lembrete
Crie um arquivo chamado “00escolha” ou outro nome qualquer sugestivo,
por exemplo, “00switch”, para reciclar esta estrutura.
Este método pode economisar um bom tempo de digitação, e vai ajudá-lo na
memorização da sintaxe.
Deixe no diretório de trabalho arquivos com os nomes das estruturas de fluxo,
depois chame-os e cole-os no ponto adequado quando estiver redigindo um programa.
84
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
Você pode usar este método com programas inteiros, é a reciclagem de programas.
Os dois zeros antes do nome têm o efeito de forçar que os seus arquivoslembrete fiquem no topo da árvore de arquivos, quando eles foram listados sem
uma ordem especı́fica. Também você pode fazer uma listagem dos arquivoslembrete existentes com
ls 00* , ou no DOS dir 00*
Exercı́cios: 14 Estudo da função escolha() (switch())
1. Rode o programa8 logica04.c, ele está errado e os erros estão indicados
no próprio programa, corrija o programa. logica04 1.c
2. Volte a rodar logica04.c e digite o último caso correto, 3. Verifique
que os anteriores não foram executados. Portanto escolha() age sequencialmente até encontrar uma primeira opção correta. Rode várias vezes
logica04.c até entender como funciona o switch() do C.
3. Melhore o programa logica04.c, acrescentando espaçamento entre as
mensagens, letras maı́usculas, etc... dê um jeito neste programa que está
muito feio.
4. limitação de escolha() Experimente usar escolha() de forma mais inteligente, ver logica05.c. Tente dar aos casos um valor lógico.
Solução em logica05 1.c
Observação: 20 C interpretado
Espero que os autores de calc jamais leiam este livro... porque estou diminuindo o a importância de calc, injustamente. Verifique isto.
Se você estiver estudando C em um ambiente LinuX, quase certamente existe
no sistema um programa chamado calc. Este programa é uma linguagem de
programação que se assemelha ao C, mas é um interpretador de comandos. Se
você digitar, dentro de calc,
(3==2)
terá como resultado, imediatamente
→ 0.
Experimente! Abra uma área9 de trabalho, (shell), digite calc < enter >, e
dentro do calc digite “(3==2)”.
Por que o resultado é zero?
calc é uma linguagem de programação bastante poderosa, mas exclusivamente voltada para Matemática e usa a estrutura lógica de C. calc usa comandos semelhantes ao da linguagem C, mas não é C, poristo é injusto o tı́tulo
acima.
calc não é C, certamente foi escrita em C como quase tudo que existe nos
computadores, mas calc é uma linguagem independente, interpretada, semelhante ao C, que você pode usar para aprender C.
8 em
BC logi*.c, logi4 1.c
que você tem o calc instalado...
9 supondo
4.2. MÚLTIPLAS ESCOLHAS.
85
“Interpretada” significa que ela lê um comando, faz sua análise e avaliação
e imprime o resultado ou uma mensagem de erro.
Outro exemplo, em calc. Digite a seguinte frase tı́pica de C :
if (3==2) printf("esta certo"); else printf("esta errado");
e você vai ver impresso na próxima linha,
esta errado.
Com calc você pode testar as expressões da linguagem C quando tiver dúvidas
quanto ao valor delas.
A função escolha() tem um uso limitado à verificação de constantes. É
excelente na construção de menus, por exemplos. Ver o programa logica06.c,
que (por acaso) está errado, como exemplo.
Exercı́cios: 15 Construção de um menu
1. Leia o programa logica06.c. É um exemplo de como se pode fazer um
menu em C.
2. Rode logica06.c e corrija os seus erros.
Solução logica06 1.c
3. Melhore o programa logica06 1.c incluindo espaçamentos entre as frases
para tornar sua leitura mais agradável.
4. Um defeito técnico, na linha em que se pede para digitar as opções, em
logica06 1.c, não deveria haver “mudança de linha”. Corrija isto, se já
não o tiver feito.
O programa logica06.c será expandido e completado mais a frente.
Para terminar a discussão do escolha(), veja que outros ’casos’ podem estar
presentes e serem ignorados. Por exemplo, no caso de logica06.c, podiamos
ter incluido algumas novas rotinas de contabilidade, que planejamos produzir
no futuro, mas sem incluir no menu a sua listagem.
Como o usuário não sabe de sua existência, não irá escolhê-las e assim poderiamos evitar que aparecesse a mensagem sobre as rotinas ainda não construı́das.
Utilidade: fica registrado no programa o que ainda pretendemos fazer, evitamos de dar explicanos que podem ficar sem a devida compreensão pelo público
leigo.
Mas o que interessa mesmo aqui é registrar que escolhe(opcao) despreza
os ’caso’s cujos valores não sejam contemplados pela variável opcao. Observe,
entretanto, que gcc aceitaria uma opção fora do menu e portanto a frase guardada para o planejamento apareceria na tela ... claro que tem meios para evitar
isto, veja como nos exercı́cios.
Exercı́cios: 16 Testando a capacidade de escolha()
86
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
1. Veja em logica06 2.c que ficou um “caso” escondido a tı́tulo de planejamento, mas que ele pode ser executado se 7 for digitado.
2. Experimente colocar um se() evitando que os casos escondidos do planejamento exponham os programadores ao ridı́culo...
Solução logica06 3.c
Observação: 21 Múltiplas escolhas e seus problemas
A função switch() é uma das múltiplas situações em que a linguagem C é
frouxa permitindo que aconteçam situações “inesperadas”.
Elas não são, absolutamente, “inesperadas”, são erros de avaliação do programador.
Aqui, como em outras tantas situações, lhe pedimos para reler o último
parágrafo da introdução, técnicas para programar bem. Uma dessas técnicas
consiste em nunca fazer grandes programas, em vez disto, contrua pequenas
funções que executem tarefas especı́ficas e cujo aglomerado seja um grande programa. As pequenas funções são fáceis de serem depuradas e situações, como
um item indesejado, ficam na frente dos olhos.
Além de fazer pequenos programas, se possı́vel constituidos de uma única
função, (projeto difı́cil...), comentários bem claros indicando possı́veis cascas
de banana devem ser incluı́dos.
Mais! Um programa contstituı́do de uma única funcao, certamente, é candidato a virar item de biblioteca.
Quando você constrói uma função deve cuidadosamente analisar as possı́veis
situações em que ela seria um risco e deixar isto registrado com um comentário.
Exercı́cios: 17 if-else - switch
1. Um estacionamento de aeroporto tem as seguintes regras para cobrança:
• A taxa mı́nima é $2,00 pelas duas primeiras horas, não importanto
se o usuário fique menos de duas horas.
• A cada hora que se passar, além das primeiras duas horas, o usuário
deve pagar $0,50 por hora, até o limite de 24 horas;
• Para estacionamentos que passem de 24 horas a cobrança passa a ser
feita por dia a razão de $13,00 por dia ou fração de dia.
Faça o programa para o cálculo das tarifas do estacionamento.
Solução: estacionamento.c
2. Melhore o programa estacionamento.c tornando mais justo a cobrança
quando o tempo ultrapassa 24 horas levando em consideração fração de
dia também.
4.3. ENQUANTO() WHILE()
4.3
87
enquanto() while()
Laços são um tipo de estrutura de fluxo de dados em que alguma coisa acontece durante algum tempo. Por exemplo,
enquanto (estiveres na minha frente)
eu vou te olhar nos olhos;
Claro que vou deixar de lhe olhar nos olhos quando o objeto já não mais
estiver na minha frente.
Vamos ver alguns exemplos menos sublimes e bem mais práticos.
Aproveitando a oportunidade, enquanto() sempre se escreve com letra minúscula,
mesmo no inı́cio de uma frase... com letra maiúscula gcc não entenderia
enquanto(), while().
Claro que você pode alterar isto no arquivo traducao.h. Mas se, no Brasil,
estabelecermos um padrão para programação em português, todos deveremos
adotar o mesmo padrão. Por enquanto programar em português é uma experiência.
Dizemos que gcc é sensı́vel à diferença entre letra maı́scula e mı́nuscula.
Portanto enquanto() é diferente de Enquanto(), e Enquanto() não existe em
traducao.h.
Mas você pode alterar isto, re-escrevendo o arquivo traducao.h e nele alterando
enquanto ---> Enquanto.
e neste caso gcc só entenderia Enquanto().
A função enquanto() analisa uma expressão e, se ela for verdadeira, executa
o comando que vem em seguida. Por exemplo, o seguinte algoritmo calcula a
soma dos 10 primeiros numeros inteiros estritamente positivos (a partir de 1).
Veja os comentários logo a seguir.
(1) soma = 0
(2) i = 1
(3) enquanto( i < 11)
(4)
inicio
(5)
soma = soma + i
(6)
i=i+1
(7)
fim
(1) soma = 0
(2) i = 1
(3) while( i < 10)
(4)
{
(5)
soma = soma + i
(6)
i=i+1
(7)
}
Comentários:
1. Inicializa a variável soma com zero; Aqui estamos començando a discutir
processamento de dados... soma é um buffer, um local onde se armazenam dados para uso posterior. Alguns diriam que soma é um acumulador.
Os valores a serem somados vão ser acumulados nesta variável, logo ela
tem que ser inicializada com zero. Se fossemos fazer multiplicações sucessivas, a inicialização do buffer se faria com 1.
88
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
Porque, zero é o elemento neutro da adição, e um é o elemento neutro da
multiplicação.
2. inicializa a variável i com 1, porque é um contador nós, normalmente,
contamos de um em diante. Não se esqueça de que C conta de zero em
diante... dizemos que C é de base 0. Consequentemente este programa
não está otimizado, está perdendo espaço na memória.
3. Teste, verifica se i passa de 10, quando isto acontecer encerra o laço. A
variável i é um contador.
4. inicio, marca o começo de um bloco lógico. Em C é a chave-abrindo: {.
5. Incrementa soma com o valor de i;
6. Incrementa i de uma unidade.
7. fim, marca o ponto final de um bloco lógico. Em C é a chave, }, fechando
que termina os blocos.
Uma das dificuldades iniciais em computação está aqui presente nos itens
(5) e (6). Não se tratam de igualdades matemáticas ou equações.
O operador “=”, em algumas linguagens de computador, significa uma “atribuição”. Há linguagens de computador que têm um sı́mbolo diferente para este
operador, em algumas é uma seta, em outras, como Pascal, Maple, MuPAD, é
o sı́mbolo
:=.
em C
soma = soma + i;
em Pascal
soma := soma + 1;
Assim
soma = soma + i
é “um comando” que se constitue da composição de dois operadores:
• primeira operação a ser executada soma + i que adiciona o valor de i ao
valor de soma;
• segunda operação a ser executada soma = soma + i que atribue o valor
calculado anteriormente, “soma + i”, à soma, alterando, assim, o valor
guardado em soma.
Quer dizer que o laço descrito acima vai ser executado 10 vezes,
enquanto( i < 11):
O quadro seguinte mostra, a cada passo, qual é o valor de soma, do acréscimo
e do valor atualizado de soma.
4.3. ENQUANTO() WHILE()
89
no.oper. soma acréscimo soma atualisada
status
1
0
1
1
CONTINUA
2
1
2
3
CONTINUA
3
3
3
6
CONTINUA
4
6
4
10
CONTINUA
5
10
5
15
CONTINUA
6
15
6
21
CONTINUA
7
21
7
28
CONTINUA
8
28
8
36
CONTINUA
9
36
9
45
CONTINUA
10
45
10
55
PARA
Observe que este laço calcula a soma dos termos de uma progressão artimética de razão 1, desde o primeiro termo 1 até o termo 10.
Exercı́cios: 18
1. Escreva um programa que imprima os termos de uma
p.a. dados o primeiro termo, a razão e o número de termos pelo teclado.
Solução logica07.c
2. Melhore o programa logica07.c criando mensagens e diálogos com o
usuário. Solução logica07 1.c
3. Melhore programa logica07.c para que ele imprima os dados da p.a. em
linhas separadas. Acrescente também linhas separadoras com
“———”
4. Use a função limpa janela() para limpar o terreno e tirar a poluição visual da tela. A função limpa janela() está definida na biblioteca ambiente.h.
5. Use a função apeteco2() para provocar paradas na execução do programa
e permitir que o usuário leia melhor o conteúdo das mensagens, prosseguindo quando lhe parecer adequado. A função apeteco() está definida
na biblioteca ambiente.h. Verifique que há vários tipos de apeteteco().
6. Re-utilize programa logica07.c para calcular os termos de uma p.g.
7. Re-utilize programa logica07.c para calcular as atualizações de sua poupança. Faça com o programa crie uma tabela para lhe mostrar a defasagem
da poupança relativamente ao crescimento do dolar, por exemplo.
8. Faça um programa que compare o que acontece, se você colocar 100 reais
na poupança ou pegar 100 no cheque especial. Use imprima() assim
imprima(’’este mes na poupanca %f ou no cheque especial %f\n’’,
poupanca, cheque);
observe que as variáveis terão que ser do tipo real ou em C, float. Sobre
tudo na poupança em que o incremento é de 0.05% ao mes...
90
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
Observe que logica07 1.c é segunda versão de logica07.c. O primeiro,
logica07.c, cabe inteiramente dentro da tela, sem os comentários. Ele foi cuidadosamente depurado. Depois, na segunda versão, foram incluidas mensagens
explicativas ou auxiliares para conduzir o usuário a fornecer os dados.
Como logica07.c é pequeno e cabe inteiramente na tela, facilmente pudemos encontrar os erros cometidos e corrigı́-los todos.
Depois que um programa estiver funcionando, sem erros, podemos fazer-lhe
uma segunda versão incluindo
• enfeites;
• mensagens de comunicação com o usuário;
• incluir os comentários.
Tudo isto é necessário, mesmo que o programa fique um pouco maior do que a
tela.
Agora estamos melhorando um programa que já funciona, mas teremos controle da situação. Muitas vezes é esta hora de incluir comentários porque o
programa está funcionando e já concluimos os truques para que ele ficasse menor (os efeitos colaterais).
Exercı́cios: 19 Mas, por enquanto()
1. Observe que se um laço iniciar com
enquanto(1)
ele certamente nunca irá parar sem uma ação mais forte
(Ctrl-C acionado no teclado...).
Use isto para alterar o programa estacionamento.c de modo que o operador não precise voltar a acionar o programa a cada cliente que sai. E
como o estacionamento é eterno o programa ficaria para sempre no ar.
Chamamos isto de loop infinito porque é um laço que nunca para de ser
executado. Um programa que deve ficar sempre no ar, geralmente, é gerenciado por um loop infinito que deixa na tela o menu de opções do
programa.
Em BC evite fazer isto. Primeiro aprenda a ligar nas options o acesso
ao Ctrl-C que pode estar desligado. Em Linux é fácil parar um programa
que esteja entalado... Em BC veja como fazer:
• Abra um programa no editor, qualquer um, por exemplo
estacionamento.c
e escreva Ctrl deixando o cursor sobre esta palavra;
• Clique no help e escolha topic search.
• Você vai parar em ctrlbrk. Dê enter;
• Você será conduzido à pagina das interrupções. Faça uma cópia do
exemplo que esta ao final da página -
4.3. ENQUANTO() WHILE()
91
• Copie para estacionamento.c o trecho
#define ABORT 0
int c_break(void)
{
printf("Control-Break pressed.
return (ABORT);
}
Program aborting ...\n");
Agora você pode criar um enquanto(1) que o programa irá parar ao você
apertar ctrl c.
Solução estacionamento01.c. Fora do contexto. Usa funções de tratamento do tempo. Ignore, inicialmente, a função ConverteTempo(). Usea. Este programa deve ser compilado em Linux com
gcc -Wall -oprog -lm estacionamento.c
a opção fará com que a biblioteca math.h seja usada.
2. Melhore estacionamento01.c uma vez que há uma tremenda poluição
visual. Coloque
apeteco2(), limpa janela()
em locais adequados para melhorar o visual do programa.
3. Se você já estiver estudando integrais, facilmente pode transformar programa logica07.c num programa que calcule integrais. Se não estiver
estudando integrais, esqueça esta questão, por enquanto...
Solução riemann04.c
esqueça por enquanto as outras versões de riemann.c que serão discutidas
mais a frente.
4. Melhore o programa logica06 3.c substituindo o se() por
enquanto(opcao > 6)
Solução logica06 4.c.
5. Como logica06 4.c suja, irremediavelmente, a tela, use a função
limpa janela()
definida em ambiente.h para melhorar a performance do programa, frente
aos clientes mais exigentes. Se inspire em prog04 2.c .
Solução logica06 5.c
6. Estude a função limpa janela() definida na biblioteca ambiente.h para
saber o que ela fez no programa anterior. Estude as funções definidas em
ambiente.h para entender o funcionamento de uma biblioteca.
92
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
7. Coloque uma mensagem de sucesso no programa logica06 5.c usando a
função sucesso() definida em ambiente.h. Altere a mensagem, como alı́
se sugere, para atender ao seu gosto.
Você foi conduzido a estudar a biblioteca ambiente.h. Quase todas as
funções da linguagem C se encontram definidas em alguma biblioteca. A linguagem mesmo, sem as bibliotecas é mı́nima. Experimente apagar # include <stdio.h>
que se encontra em todos os programas e depois compilá-lo, veja o que acontece.
4.4
Outro método para laços.
Um método alternativo de fazer laços é o para() (for()):
para(i = 0; i < 10; i = i + 1)
imprima("o termo %d da sucessao é --> %d",i + 1, 2 ∗ i + 3);
que vamos estudar neste parágrafo.
O efeito da expressão
para(i=0; i<10; i=i+1)
imprima("o termo %d da sucessao eh --> %d",i+1,2*i+3);
é o de imprimir sucessivamente os termos de uma sucessão, veja o resultado
obtido com calc .
for(i=0; i<10; i=i+1)
printf("o termo %d da sucessao eh
o termo 1 da sucessao eh --> 3
o termo 2 da sucessao eh --> 5
o termo 3 da sucessao eh --> 7
o termo 4 da sucessao eh --> 9
o termo 5 da sucessao eh --> 11
o termo 6 da sucessao eh --> 13
o termo 7 da sucessao eh --> 15
o termo 8 da sucessao eh --> 17
o termo 9 da sucessao eh --> 19
o termo 10 da sucessao eh --> 21
--> %d\n",i+1,2*i+3);
Exercı́cios: 20 Rode usando calc
1. Rode a expressão abaixo com calc
for(i=0; i<10; i=i+1)
printf("o termo %d da sucessao eh --> %d\n",i+1,2*i+3);
4.4. OUTRO MÉTODO PARA LAÇOS.
93
Basta chamar calc, marcar o texto acima com o ratinho, colá-lo na tela
do calc e dar um <enter> para ver o resultado que está apresentado
acima. Nos fizemos isto mesmo, depois colamos o resultado do calc aqui.
2. Porque imprime até o 10o e não apenas até o 9o ?
3. Usando calc apresente os termos de uma progressão aritmética de razão
0.5 com primeiro termo −3.
Solução:
> for(i=0; i<10; i=i+1)
>> printf("o termo %d da sucessao
--> %f\n",i+1,0.5*i-3);
Observe as diferenças entre enquanto() e para(). Todos os dados necessários ao funcionamento de para() devem ser fornecidos como parâmetros:
• o valor inicial do contador;
• o teste para finalizar o processo;
• o método para fazer incrementos.
em suma, para() exige tres parâmetros.
Em C existe um atalho para fazer incrementos do tipo i = i + 1:
for(i=0; i<10; i++)
printf("o termo %d da sucessao eh --> %d\n",
i+1,2*i+3);
quer dizer que as duas expressões
i = i+1 ; i++
são equivalentes. Há vários atalhos destes tipo reconhecidos pelo gcc que vamos
reunir no apêndice. Ao fazê-lo estamos claramente lhe dizendo que o seu uso
deve ser feito sob restrição. Obviamente que o atalho será executado com mais
rapidez, mas o programa ficará mais dificil de ser lido.
Os exercı́cios lhe darão mais experiência no uso de para() que mil palavras.
Exercı́cios: 21 Mais loops
1. Alerta Antes de fazer este exercı́cio, rodando C dentro do windows, veja
no manual como ativar o ctrl-c. Veja também, no ı́ndice remissivo Ctrlc. Nos ambientes integrados existem uma opção para configurar o C e
ativar o ctrl-c que vem desativado para dar maior rapidez ao compilador.
Isto pode ser feito com uma instrução de compilação, e esta forma de fazer
é melhor porque não altera a configuração do ambiente integrado.
Experimente compilar e rodar o programa logica06 6.c. Ele não para
nunca, a não ser que você o pare com ctrl-c. Leia o programa e analise
porque isto acontece. Verifique o primeiro enquanto().
94
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
2. Altere logica06 6.c trocando
enquanto(1) --> enquanto (t)
e definindo t como inteiro em linha anterior, inicializando com o valor
t = 1. Compile e rode o programa. A única maneira de pará-lo é com
ctrl-c, novamente.
3. Agora troque t por opcao e veja que o programa para digitando 0 como
opção não solicitada no menu. Experimente! Veja logica06 61.c e leia
ao final dos comentários.
4. Corrija o programa logica06 61.c para o menu informar que a saı́da do
programa se dá com
opcao = 0
solução logica06 62.c
5. Corrija agora o programa logica06 5.c para que ele fique no ar indefinidamente até que opcao = 0 seja escolhida.
Solução logica06 63.c
6. Corrija agora o programa logica06 5.c incluindo no menu a opção 0
para que o sistema pare.
Solução logica06 71.c
7. Porque não teria sentido resolver os exercı́cios deste bloco usando para()?
8. Traduza logica06 71.c para C.
Solução logica06 7.c
4.5
Parando no meio de um bloco.
Vamos estudar as funções
pare, (break), voltar, (return)
nesta seção.
Com frequência, dentro de um laço, precisamos de interrompê-lo, abruptamente, sem que as demais funções sejam executadas. Quem faz isto é a função
pare, (break), a mesma que estudamos junto com escolha(), (switch()).
Ao encontrar esta função, gcc encerra o processo que estiver sendo executado dentro de um bloco e passa o controle do fluxo para a próxima função
imediatamente após este bloco. É isto que ocorre com escolha(), switch().
A figura (fig. 4.6), página 95 mostra como isto se passa.
Veja uma situação-padrão:
4.5. PARANDO NO MEIO DE UM BLOCO.
95
para(i=0; i<100;i++)
inicio
valor = 3*i+1;
imprima(‘‘\%d’’, valor);
se($valor > 100$)
pare;
fim
continua aqui;
Figura 4.6: Ao encontrar pare() o fluxo é desviado para a próxima função externa ao bloco.
para(i=0; i < 100;i++)
inicio
valor = 3 ∗ i + 1;
imprima(“%d”, valor);
se(valor > 100)
pare;
fim
imprima(“%d”,i);
Vai sair do laço quando i = 34 sem executar os comandos internos para este
valor de i. Este laço resolve a equação (desigualdade)
3x + 1 > 100.
Exercı́cios: 22 Parando para resolver desigualdades
1. Verifique que laço acima resolve resolve a desigualde:
3 ∗ i + 1 > 100
no conjunto dos inteiros, imprimindo 34, o primeiro número inteiro i que
a torna verdadeira. Solução logica08.c.
2. Altere o programa que rodou o laço acima para resolver a desigualdade
3 ∗ i + 1 > 100
num intervalo de números racionais, obviamente sem apresentar todas as
soluções, por que?
96
CAPÍTULO 4. CONTROLE LÓGICO DO FLUXO
3. Melhore estacionamento01 1.c permitindo que o técnico em computação
pare o programa para fazer manutenção no micro.
Solução: estacionamento01 2.c
4. Em estacionamento01 2.c usamos um variável do tipo
vetor de caracteres
com tamanho 1. Talvez fosse mais simples usar variável do tipo caracter,
estacionamento01 3.c que está errado. Rode, veja as mensagens de
erro e procure entendere porque não funciona. “Solução” nos comentários
de estacionamento01 3.c .
5. Melhore o programa logica08.c incluindo mensagens adequadas de comunicação com usuário.
6. Escreva todos os comentários necessários à boa compreensão do programa
logica08.c.
7. Resolva as seguintes desigualdades, usando programas em C.
(a) 3k + 2 > −100; k ∈ Z;
(b) −10 < 3k + 2 < 200; k ∈ Z;
(c) −10.5 < 3x + 2 < 20.7; x ∈ R;
Solução: logica08 1.c; logica08 2.c; logica08 3.c Para resolver a última
desigualdade você vai precisar de usar real em vez de inteiro como tipo
de dado. Veja o capı́tulo 5 a respeito de tipos de dados.
8. Melhore os programas logica08 X.c escrevendo comentários e incluindo
os monólogos de comunicação com o usuário, paradas, limpezas de tela
etc...
9. Melhore os programas logica08 X.c fazendo que saiam deixando a tela
limpa, mas permitindo que o usuário consiga ler os dados, antes da limpeza
ser efetuada.
10. Altere o programa logica06 63.c para que o sistema saia do ar quando a
opção 0 for escolhida, mas usando pare (break), em vez de usar a opção
0 no enquanto(). Solução logica06 64.c.
No programa logica08 3.c fizemos uso dos atalhos k-=0.01, k+=0.01 que
gcc entende respectivamente como k=k-0.01, k=k+0.01.
Capı́tulo 5
Criando funções
Em Matemática, a expressão
y = f (x)
significa “executar sobre o objeto x as operações “compactadas” em f ”,
gerando um novo objeto que chamamos y ou f (x).
y é a imagem de x por f .
Resumimos no sı́mbolo f um conjunto de operaçõesa .
Em C, o signficado de função não é exatamente este que temos em Matemática, mas se encontra bem próximo no sentido de resumir um conjunto
de operações com um sı́mbolo. Em computação, como em Matemática, a definição de funções acrescenta novos vocábulos à linguagem, “novos comandos”,
dizemos em computação.
As func.ões são algoritmos. Em C, o menor módulo executável é uma função
e um programa se constitue de uma função principal() (main()) que pode
chamar uma lista de outras funções definidas no mesmo arquivo ou em alguma
biblioteca incluı́da no começo do arquivo.
a por razões práticas e teóricas, precisamos da função trivial f (x) = x,
também chamada de identidade.
Nos capı́tulos anteriores fizemos os nossos primeiros programas, quase todos
com o formato:
programa em português
tipo de dados principal()
inicio
comando1;
···
comandoN;
fim
97
programa em inglês
tipo de dados main()
{
comando1;
···
comandoN
}
98
CAPÍTULO 5. CRIANDO FUNÇÕES
Programas formados de uma única função. Esta é uma metodologia antiga
e perigosa para escrever programas, porque com ela se desenvolvem programas
longos. Quando um programa for longo, se desenvolvendo por várias páginas,
com dezenas, centenas ou milhões de linhas, é difı́cil de se verificar a sua correção.
Deve-se fazer o contrário: escrever programas pequenos que executem uma única tarefa de natureza bem simples. É o que se entende
por programação modularizada que se encontra a meio caminho
das técnicas mais recentes de programação, como programação
orientada a objeto, POO. No penúltimo capı́tulo faremos uma
breve apresentação desta técnica.
Em C isto se faz construindo funções.
Estivemos todo o tempo fazendo observações que o prepararam para chegar
ao meio do livro.
Você está chegando ao meio do livro.
Neste capı́tulo vamos aprender a modularizar os programas, de forma sistemática, usando o conceito de função. Usaremos como ponto de partida os
diversos exemplos que construimos nos primeiros capı́tulos e os iremos modificar. Eles serão assim a idéia motivadora na construção das funções ao mesmo
tempo que você verá, de uma forma inteiramente natural, como aparecem as
funções que precisamos e, inclusive, irá aprender o método inverso de trabalho:
construir primeiro as funções que realizam as pequenas tarefas que resolvem um
grande problema.
A teoria que necessitamos já foi praticamente toda desenvolvida nos 4 capı́tulos
anteriores o que nos resta agora é desenvolver técnicas para construir programas
seguros e interligar pequenos programas num projeto maior.
Ainda faremos mais um aprofundamento teórico nos capı́tulos 6 e 7 que se
encontram na outra metade do lı́vro, seguidos do estudo de alguns problemas
aplicados.
Mas as técnicas básicas de programação você as vai adquirir neste capı́tulo
completando a lógica do capı́tulo anterior.
A tecnologia que precisamos é surpreendentemente simples:
como construir funções.
Vamos mostrar-lhe com um exemplo. Considere o seguinte programa:
99
# include <stdlib.h>
# include <stdio.h> // (1) leitura da biblioteca padrao
inteira principal()
{
char deposito[80];
inteiro numero;
imprima(”Este programa lhe pede um numero maior do que 10 \n”);
imprima(”Testa se o numero eh maior do 10 e o informa \n”;
imprima(”se voce acertou. \n”);
imprima(”Me de um numero maior do que 10”);
leia(deposito, tamanho do(deposito), entrada pdr);
converte palavra(deposito, ”%d”, & numero);
se (numero > 10) imprima(”Voce acertou o combinado \n”);
ou entao imprima(”Voce errou ! \n”);
voltar(0);
}
Há três momentos no programa:
• Uma mensagem inicial;
• uma entrada de dados;
• um teste com as respectivas mensagens.
Vamos chamar a mensagem inicial de mensagens() e criar o bloco lógico
inteira mensagens()
{
imprima(”Este programa lhe pede um numero maior do que 10 \n”);
imprima(”Testa se o numero eh maior do 10 e o informa \n”;
imprima(”se voce acertou. \n”);
voltar(0);
}
Vamos criar outro bloco lógico formado pelo teste e suas mensagens sob o
nome: resposta(). Mas agora vamos colocar uma variável em
resposta(inteiro numero)
inteira resposta(inteiro numero)
{
se (numero > 10) imprima(”Voce acertou o combinado \n”);
ou entao imprima(”Voce errou ! \n”);
100
CAPÍTULO 5. CRIANDO FUNÇÕES
voltar(0);
}
Agora a função principal se resume a:
# include ¡stdlib.h¿
# include ¡stdio.h¿ // (1) biblioteca padrao
inteira principal()
{
char deposito[80];
inteiro numero;
mensagens();
imprima(”Me de um numero maior do que 10”);
leia(deposito, tamanho do(deposito), entrada pdr);
converte palavra(deposito, ”%d”, & numero);
resposta(numero);
voltar(0);
}
Ao passar por por mensagens() o programa vai buscar o conteúdo desta
função e o executa. Ao passar por resposta(numero) o programa vai buscar
o contúdo de resposta() e o executa com o valor que a variável numero tiver
recebido. Dizemos que o valor de numero foi passado para resposta().
Este programa se chama funcao.c, leia-o.
Este é o objetivo deste capı́tulo: funções. Vamos repetir o que fizemos com
o programa funcao.c mais uma vez e você vai ser convidado a fazer uma lista
de exercı́cios em que irá colocar as mãos para trabalhar na modularização de
alguns programas.
5.1
Verificador de senhas.
Vamos construir um verificador de senhas, por exemplo, para um
servidor de internet.
Observe que não estamos estabelendo um plano para montar um
servidor de internet, porque, para montar um servidor de internet, precisa mais do que apenas saber testar senhas... faremos
um pequeno módulo que será importante na montagem do grande
projeto.
Infelizmente não podemos garantir, ainda, que o conteúdo deste
capı́tulo possa ser imediatamente aplicado num grande projeto,
num servidor de rede, por exemplo, por favor, aguarde mais um
pouco, pelo menos até terminar o livro... quando você saberá
como encontrar o restante.
5.1. VERIFICADOR DE SENHAS.
101
A metodologia será a mesma que usamos nos capı́tulos anteriores.
Antes de discutir o signficado de “função” e qual é formato genérico de uma
função em C, vamos iniciar um processo de metamorfose do programa inicial,
incitá-lo a rodar cada uma das novas formas, analisar os comentários ilustrativos
e assim conduzindo-o à construção de programas mais completos.
Esta seção está baseada no programa funcao01.c1 e suas transformações:
funcao0X.c.
Exemplo: 6 Testando uma senha.
Veja o programa funcao01.c, compile-o, rode-o
gcc funcao01.c -Wall -oprog
prog2
e depois o leia, analisando na tela o texto porque lhe faltam as mensagens explicativas: um defeito do programa que será explorado nos exercı́cios.
Este programa contém a parte central do que seria um módulo verificador de
senhas de um sistema qualquer, de uma rede de computadores, ou de um terminal bancário. Obviamente ele ainda está muito rudimentar e o nosso objetivo
aqui é o de torná-lo mais sofisticado.
Depois que você tiver compreendido o seu funcionamento, passaremos à discussão das versões melhoradas.
Leia o programa e os comentários que se encontram no arquivo, chame-o para
uma tela do computador num editor de textos, enquanto você roda o programa
noutra tela.
5.1.1
Metamorfoses do Leitor de Palavras.
Evolução funcao01.c → funcao0X.c
Observe nossa mudança de comportamento, não colocamos o programa dentro
do texto, porque você tem os programas em um disco ou pode ir buscá-los num
site na internet. Porque não tem mesmo sentido você aprender uma linguagem
de programação apenas lendo um livro, você tem que estar sentado na frente do
micro e ai pode abrir o programa n’outra janela.
Analisando
funcao01.c
podemos observar que o algoritmo tem tres momentos:
• apresentação A entrada de dados, quando a senha que vai ser testada é
fornecida.
• análise O teste, comparação com o valor memorizado na variável senha.
• diagnóstico A saı́da de dados em que é emitida:
– mensagem de sucesso,
1 no
diretório do BC, procure func0X.c
que trocamos, propositadamente, a ordem dos parâmetros passados ao gcc, a ordem
é irrelevante. Talvez você precise digitar ./prog
2 Veja
102
CAPÍTULO 5. CRIANDO FUNÇÕES
– ou, alternativamente, a de insucesso.
Quer dizer que o programa poderia ter sido escrito assim:
{
apresentacao();
analise(sinal);
diagnostico();
}
em que
• apresentacao() é conjunto de mensagens iniciais que orientam o usuário
sobre o objetivo do programa;
• analise() representa tudo que se encontra dentro do “enquanto()” e
• diagnostico() é a parte final.
O planejamento acima transforma o programa num script, um roteiro,
como no teatro, o no cinema, o diretor chama os atores, em ordem para que
cada um execute o seu papel. Aqui é a função main() que vai representar o
trabalho do diretor do espetáculo.
A proxima lista de exercı́cios vai mostrar-lhe como você deve transformar
funcao01.c → funcao02.c
o programa que finalmente fizemos está ligeiramente diferente do planejamento
acima, são coisas do planejamento: entre a proposta inicial e a final sempre existe
uma diferença, mas deixamos registradas as duas para que você compreenda que
isto pode acontecer, sem maquilagens.
Você está sendo conduzido por trás da cena em vez de ficar sentado na
platéia.
Exercı́cios: 23 Construindo funções
1. Rode e leia o programa funcao01.c. Como você verificou, faltam mensagens. Coloque as mensagens de entrada e saı́da de dados. Rode o programa, depois de alterado.
2. Separe a análise de dados da função principal em funcao01.c, dentro do
editor de textos corte e cole a linhas de código, depois do fim abrindo uma
nova função chamada
inteira analise()
não se esquecendo do par inicio,fim ou abrindo e fechando chaves. Compile e rode o programa, se o compilador indicar erros, antes de ler a solução
procure entender as reclamações do compilador.
Solução funcao01 1.c
3. Rode o programa funcao01 1.c. Leia o programa para entender como ele
funciona e volte a rodar e ler até ficar claro o processamento.
5.1. VERIFICADOR DE SENHAS.
103
4. alarme.c Escreva uma função que receba um número inteiro e emita duas
possibilidades de mensagem
• Se o número for menor do que 3 dê boas vindas ao usuário e imprima
o número na tela.
• Se o número for maior ou igual do que 3, dê um beep de alarme, uma
mensagem de pesar, e imprima o número na tela.
5. Use o programa alarme.c e modifique funcao01 1.c.: separe uma função
diagonostico(sinal) que faça a análise do que aconteceu em função do
número de tentativas para entrar no sistema.
Solução funcao01 2.c
6. Melhore a saı́da de dados de funcao01 2.c com algumas trocas de linha
e espaços entre os dados.
7. O programa funcao01 2.c oferece ao usuário apenas duas possibilidades
para “errar”, apesar de anunciar que três possibilidades ficam garantidas,
não tem uma terceira possibilidade. Corrija isto dando-lhe tres possibilidades de erro.
8. Acrescente mensagens no programa funcao01 2.c informando ao usuário
que ele terá até 3 possibilidades para fornecer a senha e que esta é secreta
e está gravada no programa (você pode escrever uma mentirazianha, dizer
que a senha está em um arquivo secreto...)
9. Acrescente mensagens no programa funcao01 2.c informando ao usuário
que, após errar tres vezes, o sistema só lhe dará possibilidade de novas
tentativas depois de uma hora. Mas não vamos implementar este aspecto
agora...
Solução: funcao02.c
10. Rode o programa funcao02.c e veja como funciona. Depois leia o programa. Repita este processo até entender o programa completamente.
11. Melhore o programa funcao02.c, suas mensagens, lay-out, etc...
12. Defina duas funções: sucesso(), insucesso() que imprimam as mensagens acima de sucesso ou insucesso. Teste estas funções isoladamente
como descrevemos acima, depois inclua as funções em ambiente.h. Altere
funcao02.c para fazer uso das duas funções criadas agora.
Não se esqueça de incluir a linha
# include ambiente.h
13. Generalize, (aumente o grau de abstração) das funções sucesso(), insucesso()
deixando que elas recebam uma mensagem auxiliar indicando que tipo de
sucesso ou insucesso ocorreu.
Solução em ambiente.h.
104
CAPÍTULO 5. CRIANDO FUNÇÕES
Observação: 22 Como construir funções
As funções que foram objeto dos exercı́cios anteriores, foram testadas antes
de produzirmos o programa funcao02.c que é a modularização de funcao01.c.
Leia os comentários em funcao02.c em que explicamos “tecnicamente”
como se faz esta modularização. Aqui, mais abaixo, vamos retomar esta discussão em termos mais genéricos e menos “técnica”. Leia também os comentários contidos nos módulos de teste, funcao021.c, funcao022.c
O método
Tudo que fizemos foi copiar e colar o conteúdo dos programas primitivo colocando este conteúdo sob tres etiquetas
apresentacao(), leitora(), diagnostico().
Este é o uso mais imediato das funções na linguagem C.
Vantagens?
Observe que sim e quais:
1. trabalho limpo Primeiro uma vantagem psicológica: O programa tem uma
função principal() que é um resumo limpo do trabalho.
2. favorece metologias distintas A subdivisão em módulos não é única. Dois
programadores diferentes encontrariam certamente duas formas distintas
de definir os módulos.
3. criar móduloes reutilizáveis Neste caso temos um problema muito simples
e forçamos a divisão em tres módulos.
Se bem aproveitado, este método deve criar pequenos móduloes reutilizáveis
em outros programas. . Reciclagem de programas.
4. Facilita a verificação dos programas A função diagonostico() contém um
se() e geralmente é bom fazer um módulo separado para testar o se(),
verificar se seu comportamento é o esperado. As duas outras funções podiam formam um único módulo, mas as dividimos em dois para ilustrar
melhor o método.
5. main() é o planejamento do trabalho
mento vazio... programamos
Você está diante de um planeja-
A função principal() é uma espécie de listagem de operações. Na
“prática” as operações mesmo, serão construidas depois. Em outras palavras, a função principal() pode funcionar como um planejamento do
trabalho, em que apenas descrevemos suas etapas, “funções”.
6. teste de pequenos programas Cada uma dessas etapas pode ser testada separadamente, e veja como:
5.1. VERIFICADOR DE SENHAS.
105
• Escrevemos leitora(), em um arquivo separado, ver
funcao021.c
e lhe demos o nome de principal(), porque seria a única função
do programa funcao021.c. Compilamos e rodamos e pudemos ver
os erros que sempre se cometem. Corrigidos os erros, o módulo
leitora() ficou pronto.
• criação de bibliotecas Escrevemos diagnostico() em outro arquivo
separado, ver
funcao022.c .
Neste caso precisamos de uma outra função “principal()” para passar o valor da variável “resultado” que a função diagnostico()
analisaria para decidir qual frase seria emitida:
sucesso ou insucesso.
• programas, o embelezamento Não testamos o primeiro módulo porque
era uma simples lista de “imprima()s”, mas o certo seria também
fazê-lo porque poderia haver algum erro de formatação de dados, ou,
mais importante, poderiamos ter testado a beleza, a estética das
mensagens. Depois que um programa está funcionando podemos incluir nele alguma arte, tendo cuidado para não criar poluição visual.
Os exercı́cios abaixo exploram esta técnica. Rode os módulos de teste,
também, para ver o efeito que eles têm.
Observação: 23 Pequenos programas que façam pouca coisa.
Esta é a técnica onde o uso de funções entra,
funcao01 1.c, funcao02.c
rodaram da mesma forma como funcao01.c, porém visualmente mais limpas e
todas suas partes podem ser vistas na tela, logo, fáceis para corrigir.
Esta experiência deve levá-lo a concluir que é vital a programação modularizada. Também deve convencê-lo da importância de escrever pequenas
funções que possam ser reutilizadas facilmente, como as funções sucesso(),
insucesso(), apeteco() , definidas na biblioteca ambiente.h
A função leitora() mostra bem o que já comentamos anteriormente. Dividimos um problema em pequenas tarefas, cada tarefa executada por uma função
especı́fica. A função leitora() pode ser utilizada em muitos contextos, e você
vai ver isto acontecer aqui. Claro, não precisa ser como se encontra acima, com
alguma pequena modificação, por exemplo nas mensagens, (e até isto pode ser
generalizado...)3 , em vez de
Leitura de senha com sucesso.
poderiamos ter usado apenas
Sucesso.
3 esta
generalização é o que se chama de abstração, quer dizer tornar uma função menos
particular para que possa ser melhor reutilizada
106
CAPÍTULO 5. CRIANDO FUNÇÕES
que no presente contexto significa “sucesso de leitura da senha” mas em outra
contexto significaria outro tipo de sucesso. Ver o programa sucesso.c.
Observação: 24 Programas reutilizáveis
Esta é uma idéia central da programação, fazer programas simples, tão simples, que
possam ser reutilizados em muitas circunstâncias.
Além do mais, um programa simples é pequeno e difı́cil de ter erros, e se tiver, é fácil de
detectá-los.
Se um programa for pequeno e executar uma tarefa bem genêrica, os membros do grupo
de trabalho podem encontrar o que fazer com ele, isto é a chave do trabalho em grupo.
Onde escrevemos programa leia função...
Observação: 25 Roteiros e o teatro computacional.
Programar hoje se aproxima muito de tornar vivo um cenário.
• Primeiro construimos funções;
• quando você avançar mais em computação verá que em computação tudo
gira em torno de objetos e dos métodos que processam os dados definidos
por estes objetos. Veja o penúltimo capı́tulo.
• As funções, e mais do que elas os objetos, contém as informações sobre
um pequeno modelo computacional.
• Finalmente escrevemos a função principal, que é o roteiro, no mesmo
sentido que se usa em teatro, que chama os figurantes,
as outras funç~
oes,
fazendo rolar a “cena”,
rodar o programa ...
• Muitas vezes escrevemos primeiro a função principal() que então tem
o duplo papel, de planejamento do trabalho e de script ao final.
É o que você está vendo no verificador de senhas, funcao02.c, construimos
as funções que executam cada um dos pedaços isoladamente, depois a função
principal contém o script que executa o programa. É assim que se programa
bem.
A expressão linguagens de roteiros4 se refere às linguagens relativamente
recentes, e bem mais evoluidas, um exemplo é Python, outro Perl, mas em
qualquer linguagem moderna se pode programar da mesma forma. C++ é
um exemplo de linguagem em que se pode programar assim, esta última é uma
evolução do C.
Exercı́cios: 24 Melhorando o leitor de senhas
1. Visite a nossa biblioteca ambiente.h, estude as funções alı́ definidas. Inclua apeteco2(), apetecof() no leitor de senhas.
4 em
inglês, script languages.
5.1. VERIFICADOR DE SENHAS.
107
2. Para limpar a tela antes do programa começar, faça uso de
limpa janela().
Use esta função, junto com apetecof() para limpar a tela quando o programa terminar.
3. Altere a função mask() para que ela apresente os seus programas, (definida
em ambiente.h).
No espirito com que nos propusemos a escrever este capı́tulo, baseado em
exemplos, vamos agora construir mais outro exemplo. Vamos usar uma outra
técnica de trabalho que vai ser desenvolvida ao vivo, (porque você deverá estar
rodando os programas).
5.1.2
Sistema de contabilidade geral
metamorfose de contabilidade.c
No prefácio deixamos a pergunta sobre uma técnica de programar bem, com
uma resposta evasiva. Aqui você vai encontrar uma pequena concretização do
método.
Mas, programar é uma arte... e você, que está lendo este livro, pretende ser
um artista.
É difı́cil o ensino das artes pelos aspectos subjetivos que elas envolvem. O
fato de que programar seja uma arte não impede que tenha aspectos técnicos e
é preciso desmitificar as artes, também. Podemos mostrar a nossa arte, e você
desenvolver a sua. A grande regra é, seja independente e ético.
Obviamente, não espere encontrar aqui o programa definitivo para gerenciar
a contabilidade geral nem mesmo de uma pequena empresa. Aqui você vai encontrar um pequeno exemplo que pode ser o ponto de partida para o sistema
de contabilidade geral que você ainda vai construir. Não se esqueça que
não somos contadores, portanto, apesar de que tenhamos recebido instruções
de um contador sobre este programa, a construção definitiva de um programa
nesta área tem que ser supervisionado por um profissional da mesma. Os programadores resolvem problemas acompanhados e supervisionados por pessoal
especializado nas áreas de interesse dos programas.
Vamos produzir as funções que completem, pelo menos parcialmente, o programa logica06.c, como prometemos anteriormente.
Primeiro vamos fazer o que devemos: criar um outro arquivo com o nome,
contabil.c, deixando intacto logica06.c porque aquele tem uma função especı́fica para o trabalho do livro e não deve ser mexido, apesar de dizermos no
tı́tulo que faremos as metamorfoses dele, vamos deixá-lo em sua forma original.
Exemplo: 7 Reciclagem de programas
A reciclagem de programas economisa tempo de digitação e oferece mais
segurança e robustez aos sistemas. Um programa que esteja funcionando bem
deve ser re-utilizado (reciclado).
Para evitar de perder tempo, adquira algumas técnicas:
108
CAPÍTULO 5. CRIANDO FUNÇÕES
1. Só recicle programas que estejam funcionando bem.
2. Copie um programa que esteja funcionando bem para outro arquivo. Modifique o novo e não mexa no velho.
3. Todo programa deve ter um cabeçalho que identifique o que o programa faz,
quem o fez, etc... inclusive as dificuldades falhas e melhoras desejadas.
4. Ao reciclar mude o nome do programa internamente no cabeçalho, isto
pode ser crucial quando você fizer buscas para encontrar o programa certo,
que você precisa, para reciclar.
Esta maneira de agir deve ser uma regra de trabalho relativamente aos arquivos
que você recebeu em disco com a observação de que os pode passar em frente
desde que não os altere, a razão é esta descrita aqui. Se você alterar algum
programa, deve trocar-lhe o nome, até mesmo porque você não vai querer que
ganhemos a fama pelo que você fizer, vai? Entretanto, nós queremos ter o
respeito pelo nosso trabalho.
Como guardar o que esta feito e iniciar uma alteração ?
Em LinuX:
cp logica06.c contabil.c,
ou, usando algum gerenciador de arquivos de sua preferência, faça a cópia de
logica06 para o novo arquivo contabil.c.
Isto já deve estar feito no disco que você adquiriu com o livro, use outro nome
diferente de contabilidade.c para não perder as modificações que já fizemos
neste último programa, por exemplo, contabil.c que também se encontra no
disco.
Observação: 26 Delineando o método de trabalho
• Seguindo os princı́pios da Contabilidade, há dois arquivos diferentes que
devemos criar e manipular, deve, haver e os contadores tem suas esquisitices técnicas, tudo que se paga, está em “haver” e tudo que se recebe
fica em “deve”, vamos seguir estes princı́pios.
• Vamos criar a função deve(), ou melhor, o protótipo da função5 deve(),
no arquivo deve.c.
• A função principal() fará o seu papel de controladora de deve(), e
depois de testada e aprovada, será transferida para biblioteca
contabilidade.h
para ser chamada pelo programa contabil.c.
• Todas as funções que iremos aqui construir, serão transferidas para
contabilidade.h,
a nossa biblioteca de contabilidade geral, depois de serem testadas individualmente e estiverem funcionando a contento.
5a
palavra protótipo é usada com um significado especial, discutiremos isto no próximo
parágrafo
5.2. MÁQUINA DE CALCULAR.
5.1.3
109
Como registrar dinheiro
Vamos criar deve.c, leia o arquivo no disco. Este arquivo é semelhante ao
prog20 3.c e se quiser seguir a evolução do acesso ao disco, veja os programas
prog20 x.c e leia no capı́tulo 7, mais aprofundadamente, as regras para acesso
a arquivos em disco. Aqui nos vamos limitar aos comentários que se encontram dentro dos programas para não desviar a atenção do assunto principal, as
funções.
Exercı́cios: 25 Alterando o arquivo deve.c
Leia o arquivo deve.c
1. Identifique o local em se dá a leitura de dados da razão social de quem paga,
e elimine a variável intermediária “deposito”, use diretamente “nome”.
Solução deve02.c
2. Leia o arquivo “deve.dat” onde os dados estão sendo gravados. Altera
o registro no arquivo para que os dados fiquem separados por uma linha.
Solução deve02.c
3. Torne possı́vel que o nome do arquivo seja lido pelo teclado. Solução com
erro em deve03.c, ver deve04.c.
4. Substitua a versão do deve.c na biblioteca contabilidade.h e rode o
programa contabil.c
gcc -Wall -oprog contabil.c
o programa agora roda um pouco melhor do que antes.
5.2
Máquina de calcular.
Nesta seção vamos fazer algumas funções que efetuam cálculos e passam os dados para outras
funções.
Vamos criar uma biblioteca maquina.h deixando-a pronta para ser incluı́da em uma interface
gráfica, (que não será feita aqui) mas inteiramente funcional para ser usada no modo texto.
5.2.1
O menu de opções
A seguinte lista de exercı́cios vai conduzı́-lo a construção do planejamento inicial
do trabalho.
Exercı́cios: 26 Planejamento da calculadora
1. Em LinuX execute
grep menu *.c | more
e você vai ter uma listagem na tela de todos os programas contenham a
palavra “menu”. A mesma ferramenta existe também no Windows... ela
se chama
localizar
110
CAPÍTULO 5. CRIANDO FUNÇÕES
Selecione um deles para iniciar o programa calculadora.c.
Solução calculadora01 1.c. Outra solução calculadora01.c
Observe que não vamos começar com calculadora.c mas sim com calculadora01.c.
2. Tanto calculadora01.c, calculadora01 1.c têm um defeito estético:
primeiro permitem a leitura dos números, depois oferecem as opções do
sistema, icluindo “parar”. Corrija isto.
Solução calculadora02.c
3. calculadora01 1.c representa uma outra linha de programação. Analise
a diferença com calculadora01.c. O uso de getchar() oferece riscos
e sugiro que você evite esta função até que sua prática aumente. Vamos
seguir com o método de calculadora01.c.
4. calculadora03.c termina o planejamento, mas está funcionando muito
mal, está muito poluida, visualmente, rode, analise, e corrija.
Solução calculadora.c
5. Melhore o menu de opções em calculadora.c, se inspire em 162.c inclusive usando um único printf que torne o programa mais legı́vel.
Parte II
Aprofundando os
conhecimentos
111
113
Você deve ter visitado a maioria dos capı́tulos desta segunda parte por sugestão feitas no texto anterior.
Todos os capı́tulos desta segunda parte foram escritos como complemento ao
texto inicial. Rigorosamente falando, ao terminar a primeira parte, se você tiver
feito todos os exercı́cios, analisados todos os programas sugeridos, o volume de
trabalho feito foi grande e você já sabe programar em C.
Se você tiver feito isto, parabens. Certamente, para você, a leitura dos
capı́tulos desta segunda parte devem ser uma segunda leitura. Leia agora com
cuidado procurando compreender todos os detalhes. O objetivo, agora, é conduzı́-lo à construção de bibliotecas, domı́nio de técnicas mais avançadas da linguagem e prepará-lo para a produzir sistemas de maior porte. Você encontrará
aqui varios esboços de sistemas, inacabados, mas você deverá conseguir terminar
aqueles que se sintonizarem com seus interesses.
Uma regra importante para aprender qualquer coisa, inclusive programar,
se dedique a um problema para o qual você esteja motivado. Se
• você for professor, que tal usar a linguagem para construir um sistema de
controle acadêmico para os seus cursos ?
• se você tem uma grande coleção de músicas, de filmes, de livros, que tal
construir um sistema de controle da sua coleção ?
• se algum parente ou amigo tem um comêrcio, que tal fazer o sistema de
contabilidade e estoques deste comêrcio ?
Voê irá encontrar dicas para qualquer um desses projetos entre os exercı́cios
ou programas que lhe apresentaremos. Escolha um objetivo e se concentre nele.
Use o livro para ir em busca do que você precisar para produzir o seu projeto.
Você vai encontrar aqui pelo menos as indicações de onde encontrar o que você
poderá precisar para atingir o seu objetivo.
De agora em diante ignore a frase comum “estes capı́tulos foram pensados
para uma primeira leitura”. Esta frase valia apenas enquanto você se encontrava
ainda lendo a primeira parte...
Se você quiser continuar usando os comandos em Português você sabe como
fazer: usar e expandir o arquivo traducao.h e seguir programando em Português.
Nós o encorajariamos a fazê-lo, é a forma mı́nima, mais elementar de aprender a escrever outra linguagem de programação... e é tentando modificar que se
pode aprender a fazer coisas novas. Entretanto este é um livro sobre a linguagem C e não seria correto insistir nos programas em Português e manter este
tı́tulo para o livro. Mas você pode tomar o rumo que quiser, e deve.
O objetivo do livro nesta segunda parte é discutir com mais profundidade as
estruturas de dados, as estruturas de controle de fluxo, e estudar algumas das
bibliotecas padrão do C, enfim, deixá-lo em condição de mergulhar na linguagem
em profundidade ou escolher outra linguagem mais propı́cia para o seu trabalho
porque você já deve ser um programador.
114
Você está entrando na segunda metade do livro.
Como obter informações.
Em LinuX existe um sistema de manuais que podem ser acessados de diversas
maneiras. Uma delas consiste em digitar info. A primeira vez que você for usar
este sistema, digite
info info
para ter ler algumas informações iniciais de como funciona este sistema. Depois
apanhe um pouco até descobrir a imensidão de informações que ele guarda. Não
desisita ante as primeiras dificuldades.
Você sai do info digitando q e percorre suas página apertanto a barra de
espaços. Pode ser a seta para baixo ou para cima ou Page up Page down.
Outro método que se considera em extinção é
man
Este segundo sistema é um tradicional sistema de informações de máquinas
Unix que o info deve susbstituir aos poucos em LinuX.
Digitando man man você vai poder ler algumas páginas sobre este sistema de
informações.
Você sai do man digitando q e percorre suas página apertanto a barra de
espaços, como no info.
Digitando
man gcc ; info gcc
você vai poder ler uma série de páginas com inormação técnica sobre o compilador gcc.
Digitando
info glib
você vai poder ler as páginas sobre o sistema de bibliotecas do gcc.
Se você aprender a usar “emacs” você estará dentro de um poderoso editor
de textos, (entre outras coisas) e nele poderá escolher, com o mouse, a opção
“Info” que irá colocar na tela as opções do sistema de informações (manuais,
tutoriais etc...). O comando “m” abre um diálogo no pé da página onde você
pode digitar “Libc” e aı́ você se encontra no “manual de referência do gcc”.
ver “info” no ı́ndice remissivo ao final do livro
Outra forma de buscar informações, em LinuX, consiste em digitar ”info
nome palavra”. Por exemplo ”info scanf”irá chamar a página do Manual do
LinuX em que você poderá ler a sintaxe de uso do ”scanf()”. ver “info” no
ı́ndice remissivo ao final do livro
O ı́ndice remissivo alfabético
115
Este livro tem um ı́ndice remissivo alfabético que consideramos uma das
partes mais importantes do texto. Ele se propõe a fornecer informações e atalhos
para que você consiga rapidamente elaborar um pequeno programa. Uma outra
função que ele deve ter é a de permitir que você recupere uma informação que
ficou vaga. Inclusive o ı́ndice aponta para outras fontes de consulta. O trabalho
na construção de um ı́ndice remissivo é quase o mesmo de escrever um livro, nos
justificamos assim, se ele não conseguir atender às suas expectativas em toda
sua extensão, mas neste caso reclame, bata duro nos autores, e claro, traga suas
sugestões também, elas serão sempre benvindas para a próxima edição.
Procure no ı́ndice remissivo ”informação”para ver onde pode procurar mais
informações.
116
Capı́tulo 6
Variável global e local
As linguagens “modernas” de programaçãoa tem um conceito especial de variável local que
se opõe ao conceito de variável global.
Os exemplos vão lhe deixar claro o que significam estes conceitos, e vamos partir da análise
de exemplos para construir o conceito.
As primeiras secções fazem análise do assunto e levantam a poeira, na última fixamos os
detalhes, mas sugerimos que a última seja lida numa segunda leitura.
a Definição
6.1
de “moderno”? é aquilo que usamos e atualizamos hoje...
Variável global e local
Sem tentar definir logo no inı́cio, deixe-nos apenas dizer que antes todas as
variáveis eram globais, quer dizer, podiam ser vistas, (pior, poderiam exercer
influência) em qualquer parte do programa.
Com a modularização, as variáveis podem agora pertencer ao interior de um
módulo de programação, e C joga forte com este conceito:
Quando você abrir um par de chaves, criou um módulo e, pode,
no começo deste novo bloco lógico, criar variáveis que serão destruidas fora das chaves, (quando este bloco lógico estiver fora de
uso).
Não se trata apenas de existência e destruição, que são as idéias centrais
da frase anterior. Claro isto faz parte do próprio conceito de variável local. Se
trata de um mecanismo para evitar que dados fiquem sem controle dentro de
um sistema.
Esta “localização” protege o programa contra a existência de variáveis que
fiquem zanzando, quais “zumbis”, perdidas dentro do programa...
Em suma, mesmo antes de entrarmos no assunto, é preciso convencê-lo de que
as variáveis globais podem representar riscos muito grandes e devem ser evitadas
117
118
CAPÍTULO 6. VARIÁVEL GLOBAL E LOCAL
a todo custo. Já dissemos antes, estamos repetindo, quando você precisar definir
uma variável global, deixe um comentário a respeito no cabeçalho do programa
e ao lado da definição da variável
int numero; // variavel global !!!
que lhe sirva de alerta para que, se possı́vel, alterar o status de variável global.
A figura (fig. 6.1), página 118 mostra a relação entre uma variável local,
uma variável global definidas em um módulo e um submódulo do programa.
int numero=2;
int numero=20;
int numero=30;
Aqui numero
vale 2
Aqui numero
vale 20
Figura 6.1: Variável global e variável local.
Na figura (fig. 6.1) você pode identificar tres regiões,
• O ambiente do programa todo, designado como externo;
• um módulo;
• um submódulo.
O módulo interno é chamado pelo externo, está é uma situação comum, e
pode ser uma única “linha” de programa, um loop, por exemplo.
Há diversos fatos que podemos salientar com respeito à variável numero:
• numero no bloco interno, nada tem o que ver com numero no bloco intermediário. As duas podem até ter tipos diferentes, o que não seria nada
aconselhável, neste caso deveriam ter identificadores diferentes.
• O valor que numero tem no bloco intermediário, consequentemente, é diferente do que a outra tem no bloco interno.
6.1. VARIÁVEL GLOBAL E LOCAL
119
• Há razões fortes para que você tenha variáveis tão distintas, mas com o
mesmo nome. Se você estiver calculando uma soma, gostará, certamente
de chamar sempre a variável que acumula os valores de soma.
• É preciso ser cuidadoso com esta facilidade para não pecar contra a legibilidade do programa, a sua compreensão. Comentários sempre ajudam.
Sufixos permitem que você use o nome adequado adaptado ao módulo em
que estiver a variável, por exemplo soma interna, soma externa.
Resumindo, você pode fazer o que a figura (fig. 6.1) sugere, mas não deve.
Inclusive o compilador irá advertı́-lo dizendo que o valor de numero ensobreia1
o valor de numero, quando analisar o sub-módulo. Embora as três variáveis
numero sejam três variáveis diferentes, evite de fazer isto. Use
numero1, numero2, numero3
ou mesmo nomes mais sugestivos
numero de fora, numero do meio, numero de dentro
nunca esquecendo que a regra é que o programa fique legı́vel. Se numero representa a quantidade de grãos de feijão que o programa está contabilizando,
porque não chamar esta variável de
numero grao feijao ?
Veja o seguinte exemplo que podemos etiquetar como grotesco, leia e rode o
programa grotesco.c.
O programa grotesco.c ilustra inclusive os riscos que fazem da linguagem
C um ambiente de programação delicioso...
Rode o programa grotesco.c e depois o leia. Faça isto diversas vezes até
que fique claro o significado de variável local e global.
Exercı́cios: 27 Tres variáveis com o mesmo nome
Considere o programa, no disco, chamado global 01.c. Ele mostra como
se pode usar de forma abusiva a definição de variáveis locais que é um
fato positivo nas linguagens modernas, mas que pode se voltar contra o
programador.
1. Leia global 01.c e justifique os comentários (41), (42), (43)
2. Altere a variável, escolha os nomes, de modo que o programa saia do loop.
O exemplo, no exercı́cio anterior é uma representação perfeita da figura (fig.
6.1) em que uma variável designada pelo nome numero existe em tres ambientes
distintos.
Neste exemplo temos três ambientes dentro do macro ambiente representado
pelo programa, quero me referir ao arquivo onde está esta função e o cabeçalho
do programa. Veja global 01.c, a variável numero está definida em tres ambientes. Rigorosamente falando o singular está errado, são tres variáveis, definidas
em tres espaços de nomes, com endereços diferentes na memória.
1 shadows
120
CAPÍTULO 6. VARIÁVEL GLOBAL E LOCAL
Dentro do while() a variável tem um valor que se encontra em permanente
alteração e nada tem o que ver com a variável que controla o laço.
Consequentemente este programa não para nunca a não ser com Ctrl-C. Rode
e veja os valores se alterando dentro do laço.
Observe outro fato, no bloco interno a variável numero não foi incializada e
o compilador lhe atribui um valor sobre o qual o programador não tem controle.
Exercı́cios: 28 Variável global e local
1. Modifique o programa global 01.c para que ele pare sozinho, acrecentando uma variável contador para controlar o while().
2. Altere global 02.c para que você possa ver as mensagens fora do laço e
verificar assim que as variáveis numero são diferentes. Solução global 03.c
3. Por que o 9 é impresso seguidamente por global 03.c ?
6.1.1
Comentários sobre os exercı́cios
Em global 01.c, no ı́nicio do ambiente interno, definimos a variável local numero, que se encontra representada pelo mesmo nome que a variável global
definida no ambiente externo.
Para C se tratam de duas variáveis diferentes. Quando o compilador inicia
o processamento do ambiente interno, cria um novo espaço de nomes onde
registra a nova variável numero que acontece ser designada pelo mesmo nome
que a variável definida anteriormente.
Até este momento nenhum problema. A coisa até pode ser assim mesmo.
Podemos e até devemos usar o mesmo nome para variáveis que venham a ter a
mesma função em um módulo interno de um programa. Isto pode tornar as coisas menos claras, mas tem suas razões, e um comentário resolve este problema.
Esta é uma elipse da arte de programar.
Mas não podemos considerar esta maneira de fazer como um método de
bem programar. A maneira de programar bem sugere que no bloco interno se
acrescente um sufixo ao identificador da variável.
Muito melhor do que esta confusão provocada com o uso do mesmo identificador, seria usar uma pequena variante do nome:
numero, numero local, numeroL
por exemplo. Com a nova variante fica fácil de identificar “quem faz o que”
e se guarda o espirito do nome: soma, numero, contador. Uma variável que
deve contar, tem que ser chamada de contador, mas uma variável local que
for contar pode ser chamada de contador local.
Afinal, as mesmas facilidades que temos nos permitem
• construir um número grande de variáveis;
6.1. VARIÁVEL GLOBAL E LOCAL
121
• com nomes parecidos, mas indicadores da funcionalidade da variável;
• diferenciados por sufixos que indiquem em espaço de nomes as variáveis
tem seus endereços.
O objetivo aqui, entretanto, é discutir o conceito de variável local em oposição
à variável global.
O exemplo, embora grotesco, ilustra o fato. Ao entrar em um bloco interno,
C permite que novas variáveis sejam criadas. As variáveis criadas em um bloco
interno são locais, como dissemos acima, novo espaço de nomes é criado e nova
referência é assim feita entre estas variáveis e seus valores.
Assim que o programa abandona o bloco, as variáveis alı́ criadas são destruidas. Deixam de existir.
Referências a “nomes” iguais, como no programa acima, vão ter valores diferentes dentro do bloco lógico, onde vale o valor local, ou fora do bloco lógico
onde vale o valor global. É o que você pode ver ao rodar global 01.c.
Quando você abrir uma chave, num programa em C, uma novo espaço de
nomes será criado. Para definir novas variáveis neste bloco, isto tem que acontecer logo no ı́nicio do bloco, antes de qualquer comando do bloco. Outro local é
ilegal e produzirá algum tipo de erro, (nem sempre o mesmo), inclusive gcc não
lhe vai alertar que as variáveis foram definidas em local errado, ele simplesmente
vai se perder apresentando outros tipos de erro.
Aliás, adquira um hábito. Sempre que for
abrir uma nova chave, se pergunte se não deveria definir uma nova função...
Claro, o conceito global é relativo.
Imagine tres blocos de programa, um inserido no seguinte. A figura (fig.
6.2) página 122, mostra isto.
• Variáveis definidas no bloco médio serão globais relativamente ao bloco
“mais interno”,
• e locais relativamente ao bloco “mais externo”.
Ainda tomando como referência a figura (fig. 6.2), uma variável definida
no bloco lógico A é visı́vel nos blocos lógicos B,C,D a não ser que haja outra
variável com mesmo nome em alguma destas regiões interiores. Se houver uma
variável com o mesmo nome, definida num bloco interno, os valores desta nova
variável vão se sobrepor ao da variável externa, dentro do bloco.
122
A
CAPÍTULO 6. VARIÁVEL GLOBAL E LOCAL
int numero=2;
B
int numero=20;
D int numero=300;
C int numero=30;
Figura 6.2: Variável global e local
Sublinhando a última frase. Uma variável numero definida em A será sobreposta por outra, de nome numero definida em B. O programa global 02.c
ilustra isto muito bem.
Uma variável definida na bloco lógico D não existe nos blocos lógicos A,B,C.
Ela será destruida quando o programa sair do bloco D.
Um exemplo desta situação pode ser vista no programa global 04.c. Rode
o programa, e o leia para verificar que confere.
A situação descrita no exemplo acima, de variáveis com exatamente o mesmo
nome, em módulos encaixados, deve ser evitada. Não conseguimos imaginar
nenhuma situação interessante em que este exemplo possa ser usado, sem riscos.
A única utilidade deste exemplo, e você deve rodar programa global 01.c
para ver o que acontece, é alertá-lo para não fazer esta bobagem.
Mas importante do que discutir o exemplo, é compreender a importância do
conceito e da metodologia que ele encerra. Vejamos algumas consequn̂cias que
podemos deduzir do experimento feito acima.
• As variáveis locais têm vida limitada, relativamente curta, produzem, portanto, economia de processamento, importante em grandes programas;
• as variáveis locais tendo vida local, permitem um controle mais efetivo
dos seus valores, sabemos exatamente em que módulo, programa, elas se
encontram, (ou pelo menos poderemos saber...).
• uma grave confusão pode se originar de variáveis locais com nomes idênticos
aos de variáveis globais.
• O uso de sufixos local ou L nos nomes das variáveis, tornam os nomes
diferentes, guardando o sentido que estes nomes tiverem.
6.2. TÉCNICAS COM O USO DE VARIÁVEIS LOCAIS
123
Se evitarmos a identidade de nomes entre variáveis locais e globais, podemos
concluir que o uso deste tipo de variável é recomendado. Devemos tomar como
metodologia, sempre que for possı́vel adotar variáveis locais e evitar o uso de
variáveis globais.
Exercı́cios: 29
1. Faça um diagrama, como na figura (fig. 6.1) para representar o programas dos exercı́cios.
2. Chamamos nı́vel de profundidade de um programa, ao número inteiro que
meça a maior quantidade blocos encaixados num programa. A figura (fig.
6.1) mostra um programa cujo nı́vel de profundidade é dois. Cálcule o
nı́vel de profundidade dos programas dos exercı́cios.
3. Calcule o nı́vel de profundidade dos programas prog*.c que você deve ter
em disco, (se não tiver, solicite pelo endereço [email protected]).
O nı́vel de profundidade é uma medida para analisar a complexidade de
programas.
6.2
Técnicas com o uso de variáveis locais
Na introdução, que por sinal estaria no momento oportuno para ser lida, insistimos numa técnica de bem programar. A seguinte frase pertence a um outro
autor e é repetida, com nuances diferentes, em todo livro de programação: “é
tão difı́cil corrigir-se um programa ruim, que é melhor voltar a aprender a programar e fazer outro programa.”
Não há nada mais verdadeiro, apenas é preciso não cair no mau hábito de
sempre querer estar fazendo tudo de novo. É preciso aprender as fazer coisas
corretamente, desde o princı́pio, em particular na arte de programar computadores2 .
Programar é uma arte, inserida na Ciência dos Computadores. Um dos
parâmetros para medir se um programa é bom, indiscutivelmente é a beleza.
Observe que não nos refirimos ao resultado do programa na tela, e isto também
é importante. O código do programa deve
• ser bonito;
• deve ser fácil de ler;
• deve ser escrito de tal forma que outras pessoas o consigam entender e
possam alterá-lo com facilidade.
• deve ser escrito de tal forma que possa ser reciclado.
O contrário desta lista de caracterı́sticas seria um programa que somente
o autor consiga ler. O resultado, necessáriamente, será um código que nem o
autor conseguirá ler algumas semanas depois.
2 tı́tulo
de um livro de Donald Knutt
124
CAPÍTULO 6. VARIÁVEL GLOBAL E LOCAL
Como dissemos acima, na companhia de um autor renomado, programar
computadores é uma arte, e consequentemente é difı́cil ensinar esta arte a outras pessoas. Como arte, é uma questão pessoal, no sentido de que duas pessoas
que resolvam o mesmo problema, computacionalmente, quase com certeza, vão
escrever códigos distintos. Quase com certeza, também, os programas vão coincidir nos aspectos fundamentais.
Você vai ter que desenvolver sua própria arte em programação como cada
programador desenvolveu a sua. Isto não nos impede, e muito pelo contrário, é
necessário, lermos os programas feitos por outros programadores para com eles
dominar os detalhes iniciais e os avançados.
• Quando ainda não soubermos, temos que acompanhar os mestres;
• quando já formos mestres, temos que ficar atentos nas dificuldades dos
discı́pulos para manter acesa a capacidade de crı́tica de nossos próprios
métodos.
A arrogância, sem dúvida, é um indı́cio de corrupção! e tome a palavra
corrupção no sentido que você quiser. Funciona!
Ao final desta seção, vamos transformar um programa extenso em módulos
mostrando-lhe como eliminar as variáveis globais.
Vejamos um exemplo de programa que executa um item de um menu. O
programa está incompleto, falta a implemtação das funções
executa item do menu(inteiro item), apresentacao().
Abaixo vamos transformá-lo eliminando a variável global.
Exemplo: 8 Programa com um única variável global
1) tipo principal()
2){
3)
inteiro item;
4)
apresentacao();
5)
item = menu do programa();
6)
executa item do menu(item);
7)
volta tipo de dados;
8) }
Este programa tem apenas uma variável global, item, e se possı́vel, devemos
eliminá-la.
Este é um padrão básico de programas. Digamos que ele representa o planejamento inicial de qualquer projeto. Vamos analisar cada item tendo sempre
em mente a questão das variáveis.
• tipo principal() Porque todo programa, todo projeto, falando de forma
mais correta, tem uma função principal que gerencia o sistema de programas que formam o projeto. No meio dos que programam em C é comum
a afirmação de que o tipo da função principal deve ser inteiro.
6.2. TÉCNICAS COM O USO DE VARIÁVEIS LOCAIS
125
• inteiro item; Uma variável global que vai servir de ligação entre o
menu do programa()
e a função que executa as opções do menu. É a única variável global do
sistema. A função
menu do programa()
apresenta as possibilidades do projeto e recebe do usuário a sua intenção
sob forma de um número inteiro que vai passar para
executa item do menu(item).
• apresentacao() Faz uma descrição do sistema.
• menu do programa(); O menu já descrito.
• executa item do menu(item); quem faz o trabalho.
• volta tipo de dados; Todo programa deve terminar com este comando.
Num “autêntico” programa em C deve ser “sempre” um inteiro, seguindo a
tradição. Este número deve informar ao sistema operacional se a execução
do programa foi feita corretamente, e, em caso contrário, informar o nı́vel
de falha na execução do programa.
Não precisamos, para um grande programa, mais do que uma variável global,
e mesmo assim, uma, é muito. Todos os dados serão gerenciados localmente por
funções especı́ficas chamadas a partir do menu que resolverão todas as questões
com as variáveis enquanto elas estiverem no ar, deixarão todos os dados gravados
em disco antes de saı́rem do ar, de modo que não haverá nenhum valor zanzando
pela memória sem controle.
Abaixo as variáveis globais! é a regra.
Vamos agora reformular o “programa”acima mostrando-lhe como podemos
eliminar a única variável global. Veja como se faz e acompanhe as justificativas
que apresentaremos depois.
Exemplo: 9 Eliminando a variável global
Compare o programa do exemplo (ex. 8). Observe que numeramos as linhas e
agora algumas linhas vão ficar faltando, porque foram eliminadas de um exemplo
para o outro.
1) inteira principal()
2){
4)
apresentacao();
6)
executa item do menu(menu do programa());
7)
volta 0;
8) }
126
CAPÍTULO 6. VARIÁVEL GLOBAL E LOCAL
• Na linha (1), definimos o tipo de dados da função principal() como
inteira. Observe que na linha (7) agora está retornando 0. Não precisava ser zero, poderia ser um número calculado dentro do programa que
indicasse ao sistema operacional o nı́vel de funcionamento do programa,
este é um detalhe mais especializado...
• Desapareceu a linha 3, porque não precisamos de variável global.
• Desapareceu a linha 5, porque a funcao menu do programa() foi parar
dentro da área de parâmetros de
executa item do menu().
Desta forma, em vez de guardar a resposta de
menu do programa()
em uma variável global, repassamos esta resposta diretamente para a função
executa item do menu()
sendo assim desnessário o uso de variáveis globais.
• É preciso observar que o programa ficou menos legı́vel. Agora estamos
usando o conceito “função composta” para eliminar variáveis globais. Em
computação este assunto é registrado sob a rubrica passagem de valor .
Isto deixa a lógica do programa mais difı́cil de entender. Tem duas formas
de resolver este novo problema:
1. Comentários explicativos colocados nos pontos crı́ticos do programa
2. Uso de identificadores mais claros. Isto fizemos no “programa”
acima: executa item do menu(), diz, com o seu nome, que ela recebe a escolha feita em menu do programa() e vai executá-la.
O tamanho dos identificadores é praticamente ilimitado: 256 caracteres (três linhas). Ninguém precisa de algo tão grande, mas podem ser
frases inteiras como executa item do menu().
Este programa existe, veja pensionato.c3 , leia-o !
Eliminamos a variável global.
Abaixo as variáveis globais! é a regra.
E esta regra se propaga para dentro das funções particulares do sistema. Um
bom sistema é feito de uma multidão de pequenas funções cuja redação deve
caber na tela e que tenha controle completo de todas as variáveis envolvidas.
Se algum dado for enviado para outra função devemos nos arranjar para a
função interessada receba este dado diretamente e você está vendo aqui uma
das principais funções do “comando” return ao final de cada função.
Eis um sistema seguro, eis uma forma avançada de programar.
Vamos discutir na secçào final deste capı́tulo, como C transfere valores entre
as funções.
3 no
diretório para DOS, procure pension.c
6.3. PASSANDO VALORES ENTRE FUNÇÕES
127
Exercı́cios: 30 Transformando global em local
1. Leia e rode o programa padrao.c.
2. Transforme padrao.c num programa que faça alguma coisa.
3. Faça uma cópia do programa contabilidade.c em outro arquivo, por
exemplo teste.c e transforme as etapas do programa em funções, pelo
menos tres:
• apresentacao()
• menu contabilidade()
• executa contabilidade()
passe o valor de opcao para uma variável inteira da função principal e
passe esta variável como parâmetro para executa contabilidade()
4. Elimine a variável que recebe a opção de menu contabilidade usando esta
função diretamente como parâmetro de executa contabilidade().
solução: pensionato.c
5. erro de compilação No programa pensionato.c, identifique na função ”principal()”a linha
fim = executa pensionato(menu pensionato()); e nela troque
menu pensionato()
por
menu pensionato
rode o programa e analise a mensagem de erro produzida. Tente dar uma
explicação para a mensagem de erro.
solução: ver ı́ndice remissivo, ”ponteiros,tutorial”.
6.3
Passando valores entre funções
Nesta seção vamos trabalhar com o assunto: passando valores entre funções.
Esperamos que você rode e leia os programas na ordem como eles são sugeridos
no texto, inclusive os comentários contidos nos programas.
Observe que os programas são parte integrante do texto do livro, embora distribuidos eletronicamente, em separado. Ninguém aprende a programar lendo
livros...apenas lendo livros!
Rigorosamente falando, em C existem apenas dois tipos de dados:
128
CAPÍTULO 6. VARIÁVEL GLOBAL E LOCAL
1. números e sua variantes, inteiros, fracionários (float), (o que inclue caracteres, short int ...), e
2. vetores que são os ponteiros, os objetos diretamente associados com um
endereço inicial e outro final (calculado a partir do tipo de dado).
Consequentemente, a passagem de dados entre funções usando os ponteiros
tem que ser um pouco mais traumática porque na verdade se está passando um
endereço e o valor tem que ser lido indiretamente.
O exercı́cio 1, abaixo, merece sua atenção, sem dúvida, mas talvez você deve
tentá-lo diversas vezes, algumas vezes deixando-o para depois. É um desafio.
Exercı́cios: 31 Passagem de dados
1. Desafio Transforme o programa 162.c para fazer a passagem de dados entre menu() e executa() usando string, “d”, “h”, “c” em vez de caracteres,
como esta projetado atualmente, ‘d’, ‘h’, ‘c’.
Solução programa 163.c.
2. Os programas 163.c e 162.c tem uma péssima apresentação. Melhore a
apresentação dos programas e envie uma cópia para o autor.
3. O programa 165.c é uma alteração de 163.c para que seja impresso o
endereço da variável. Analise o programa e idenfique onde se faz isto.
4. Desafio, outro? Transforme o programa 163.c para que sucessivamente as
funções apresentacao(), menu() passem dados para executa().
Solução programa 164.c, veja também o programa pensionato.c
5. Leia os comentários no programa 164.c
Se você conseguir resolver o problema, de forma diferente de 163.c, me
envie uma cópia, eu irei colocar sua solução no livro, com seu nome, mas se
você conseguir fazê-lo sem variáveis globais...!
O objetivo do exercı́cio 1 é mostrar a dificuldade de passar valores em C
quando estes valores não forem inteiros. Observe também que switch() somente
aceita parâmetros inteiros, o que forçou que fosse substituido por uma cascata
de if-elses.
Numa comparação, quando se passam valores usando variáveis do tipo ponteiro
o que se faz é dizer em que endereço XXX o valor se encontra. Veja 164.c.
Vamos analisar o programa sexto.c para produzir uma versão melhorada
do mesmo.
Exercı́cios: 32
1. Rode o programa sexto.c. Compile,
gcc -Wall sexto.c -oprog
e rode
prog em alguns sistemas, ./prog
6.3. PASSANDO VALORES ENTRE FUNÇÕES
129
2. Leia sexto.c procurando entender cada uma das suas tres funções. Quantas variáveis globais o programa tem?
3. Há uma mensagem de advertência no rótulo do programa sexto.c, leia e
identifique no programa as variáveis a que ela se refere.
4. Suite sexto01,sexto02,sexto03
(a) Leia e rode o programa sexto01.c
(b) Verifique onde é usada a variável sinal na função
principal()
e qual sua razão.
(c) Verifique que a variável sinal é desnecessária, você pode colocar diretamente
verificacao(senha)
como parm̂etro do se(). Experimente isto e elimine esta variável
global. Solução sexto02.c
(d) Verifique onde a variável senha é utilizada na função
principal().
Torne esta variável local usando-a exclusivamente na função recepcao().
Solução sexto03.c
(e) Observe que na compilação de sexto03.c há uma advertência (warning). Conclua que “solução” encontrada no programa sexto03.c
para tornar local a variável senha não é boa. Ela deve ser global
no âmbito do prgrama, “absolutamente” global. Corrija o programa
sexto03.c
Solução sexto04.c
(f ) Leia as observações presentes em todas as versões de sextoX.c e
resolva os problemas propostos.
130
CAPÍTULO 6. VARIÁVEL GLOBAL E LOCAL
Capı́tulo 7
Os tipos básicos de dados
Neste capı́tulo vamos discutir os tipos de dados básicos de C. Entre eles existe
um de particular importância que deixamos para a última seção, ponteiros,
porque ele tem o que ver com todos os demais e também porque ele é de uso
intenso nos programas em C. A seção sobre ponteiros foi escrita a parte e
imagino que deve ser lida independentemente das demais, inclusive há “ponteiros”a de outras partes do livro para ela.
Um agregado como 132345665, para a linguagem C, é semelhante ao agregado
hf gf abccb. C manipula tais agregados segundo certas regras incluidas no compilador de uma forma tal que podemos chamar C de linguagem, porque, como
as linguagens naturais C obedece a uma sintaxe e tem alguma semântica de
muito baixo nı́vel.
Na medida em que você declarar corretamente os dados, C irá aplicar a estes
dados as regras sintáticas correspondentes e, com igual importância, irá separar espaço de memória do tamanho certo para guardar estes dados. É esta
a importância do tipo de dado na declaração de uma variável: determinar o
espaço que ela vai ocupar na memória e portanto o segmento de memória que
será usado.
a Este
uso da palavra “ponteiro” é técnico, mas nada tem a ver com seu
significado dentro da linguagem C. Aqui a palavra ponteiro quer dizer “link”,
uma ligação entre duas idéias como num texto em html...
7.1
Os números em C
Este livro não pretende discutir questões matemáticas, aqui, os números são
tipos de dado de uma determinada linguagem de programação e isto é diferente
do conceito de número em Matemática, embora, sob algum aspecto, os inteiros
em C tenham aspectos que somente se atinge em cursos mais avançados de
Matemática, quando se estuda congruência, em Álgebra.
131
132
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
7.1.1
Os números inteiros
Vamos começar com os números inteiros.
A forma de encarar os números, numa linguagem de programação, difere daquela com que
um matemático vê este tipo de objeto. Há linguagens de programação em que a visão matemática de número chega a ser aproximada, Python, LISP, calc por exemplo, relativamente
a números inteiros. Em Python ou em LISP o limite para se escrever um número inteiro fica
por conta da memória da máquina.
Em C, os números formam um conjunto finito e usa uma aritmética apropriada para um
conjunto finito de números, a da congruência módulo p. Este poderia ser um tópico para
um livro inteiro, de modo que vamos ter que cortar os horizontes para escrever dentro do
escopo deste livro, mas é preciso que o leitor saiba disto. O local onde você pode se expandir
a respeito deste assunto é num livro de Álgebra.
Um número inteiro é um objeto que vai ocupar um determinado espaço da
memória do computador e ao qual certas regras serão aplicadas. Este é um dos
pontos em que a linguagem C difere de máquina para máquina. Em LinuX1 o
maior número inteiro positivo que o gcc reconhece é 2147483647.
No BC, se você escrever int na área do editor (dentro de um programa
editado), e colocar o cursor sobre esta palavra, apertanto Ctr-l F12 , você vai
receber uma ajuda no contexto, especı́fica, sobre int na qual lhe vai ser dito
qual é o maior inteiro que o BC reconhece.
Como eu uso somente LinuX, me vejo na impossibilitade de verificar qual
é o maior inteiro reconhecido pela linguagem C em outros sistemas. Porém o
método indicado acima vai lhe mostrar qual é a capacida numérica do BC e,
certamente, o método funciona em outros sistemas.
Veja o resultado da operação aritmética para números finitos
2147483647 + 1 = −2147483648.
Voce pode verificar isto rodando o programa
#include <stdio.h>
int
{
main()
printf("%d", 2147483647+1);
1 Você
pode
encontrar
esta
informação
em
/usr/lib/gcc-lib/i486linux/2.7.2.3/include/limits.h
2 se não funcionar, deixe o cursor sobre a palavra e, acionando o help escolha Topic search
7.1. OS NÚMEROS EM C
133
return 0;
}
apesar do aviso que o compilador lhe vai dar de que o programa produz um
“overflow”, (estouro de dados). Você pode ignorar este mensagem, tranquilamente. A mensagem de erro ocorre porque, embora C saiba bastante Álgebra,
não se encontra convencido de que Álgebra é verdadeira... a linguagem calcula
corretamente o valor mas a operação está ultrapassando o limite de memória
reservado para números inteiros, eis a razão do overflow que significa “está derramando”...
Um programa que executa tarefa semelhante é inteiros.c. Leia o programa, rode-o, volte a lê-lo.
Por exemplo observe que
232 = 4294967296 ; 231 = 2147483648
entretanto isto não parece estar ligado ao programa que você acabou de rodar.
Vamos pensar de outra forma. Lembrando a Análise combinatória, se você
tiver dois caracteres, {0, 1}, para arranjar com repetição em uma matriz com
32 posições, o número de arranjos (com repetição) seria
232 = 4294967296.
Agora separe um bit para indicar se o número é positivo ou negativo.
Falei de bit, entenda que é uma posição na matriz de 32 posições acima.
Separar um bit lhe faz perder a possibilidade de usar {0, 1} uma vez, portanto o conjunto dos números inteiros que se podem assim representar é
232 /2 = 4294967296/2 = 231 = 2147483648.
Ainda tem o zero, e assim a aritmética de inteiros em C vai de
−2147483648 . . . − 1, 0, 1 . . . 2147483647
sendo esta a razão do resultado que você obteve com o programa inteiros.c.
Quando pedirmos 2147483647 + 1 C responderá com −2147483648 e sucessivamente −2147483648 + 1 = −2147483647 . . .
Como um byte corresponde a oito bits então os inteiros no gcc ocupam 4
bytes de memória:4 x 8 = 32.
Uma outra forma de obter a mesma informação junto com outras informações
ligadas aos tipos de dados “escalares” pode ser consultando o manual, ver no
ı́ndice remissivo “Libc”. Veja também o último capı́tulo.
Se você quiser saber o espaço ocupado na memória por um inteiro, rode o
programa inteiros.c.
e para compreender exatamente o que significa o
“tamanho” de um tipo de dado, altere manualmente o número que aparece no
programa inteiros.c, por exemplo, retirando alguns algarismos3, e voltando
3A
versão do programa, no disco, é a mais recente e pode ser diferente da que se encontra
no livro.
134
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
a rodar o programa. Não tire muitos algarismo porque o programa poderá
demorar muito rodando...mas, neste caso, tem o Ctrl-C para pará-lo.
Exercı́cios: 33 Experiência com inteiros
1. Faça um programa que adicione dois inteiros fornecidos pelo teclado.
Solução: inteiros.c
2. Altere o programa inteiros.c para que um número seja lido pelo teclado
e seja testado.
Solução: inteiros01.c
3. Se você tiver usado números muito grandes ao rodar inteiros01.c, vai
aparecer uma conta meio estranha. Tente encontrar uma explicação. Rode
inteiros02.c e não dê muita importância à reclamação do compilador
sobre o tamanho das constantes dentro do programa. Neste caso pode ir
em frente.
4. Introduza em inteiros.c um teste para verificar se uma soma de dois
inteiros positivos ainda é positivo na aritmética finita do gcc.
Solução: inteiros03.c
5. Altere o programa inteiros.c para que ele rode a partir de um inteiro
grande acrescentando mais uma unidade até chegar ao maior inteiro do
seu sistema. Ver sugestões em inteiros01.c
6. Faça um programa que receba dois inteiros e depois lhe pergunte qual a
operação aritmética para executar com eles e que ela seja executada.
7. Evolução de um programa
(a) Quebre o programa4 quatro operacoes.c em quatro módulos que
devem ser chamados a partir de um “menu”.
(b) Torne o programa quatro operacoes.c numa máquina de calcular
permanente na memória.
(c) O programa quatro operacoes01.c tem um “lay-out” de baixa qualidade, reforme-o.
(d) O programa quatro operacoes01.c não usa memória para guardar
as operações executadas, altere isto.
(e) Inclua no programa
quatro operacoes02.c
uma informação de como parar o programa:
“Ctrl-c”
naturalmente...com o cursor na shell em que você tiver rodado o
programa.
4 no
diretório do DOS, procure qua opr*.c
7.1. OS NÚMEROS EM C
135
(f ) Ofereça um meio mais evoluido para parar o programa
quatro operacoes02.c
Solução: quatro operacoes03.c.
(g) Infelizmente o usuário pode ser obrigado a digitar números antes que
o programe pare... defeito do quatro operacoes03.c. Corrija isto.
(h) O programa quatro operacoes04.c tem um péssimo “lay-out”, corrija isto se ainda não o houver feito.
(i) Refaça o programa quatro operacoes04.c usando a função switch().
Solução:quatro operacoes05.c
Sugestões:
1. Quando os inteiros crescerem além do limite, tornam-se inteiros negativos, um teste
para detectar isto pode ser num < 0.
2. Uma variável do tipo char pode receber os itens de um menu. Verifique que se char é
aceito como tido de dados do switch
3. Máquina de calcular: ver o programa quatro operacoes.c.
4. Modularização? analise a suite quatro operacoesXX.c Use grep modulo quatro* para
encontrar em que programas aparece a palavra chave “modulo” (sem acento).
5. Máquina de calcular permanente? Loop infinito...
quatro operacoes01.c
ver
6. Use
grep ‘assunto’’ quatro*.c
para encontrar o programa que você deseja, (os nossos programas tem um sistema de
palavras-chave para buscas deste tipo):
grep ’assunto’ *.c | more
Uma das experiências feitas nos exercı́cios do bloco anterior foi a do tamanho
ocupado por um inteiro. No gcc é 32 bits o espaço necessário. Pode ser até
mesmo o inteiro 0, ele ocupa o mesmo espaço do inteiro máximo. Isto significa
que gcc “planeja”5 um espaço adequado para guardar números inteiros. Se o
problema que você for resolver não precisar de números tão grandes, escolha um
tipo de dados mais modesto. Caso contrário você vai gastar memória a toa.
A biblioteca “limits.h”, que pode ser encontrada no diretório
‘/usr/lib/gcc-lib/i386-linux/2.9XX/include/” 6 ,
contém as definições dos tamanhos de dados. As letras XX devem ser substituı́das para bater com a versão atual do gcc. Você pode obter a versão digitando
gcc -v
no meu caso o resultado é
tarcisio@linux:~/tex/c$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-linux/2.95.2/specs
gcc version 2.95.2 20000220 (Debian GNU/Linux)
5 Na
verdade quem planeja é o programador!
caminho pode variar de máquina para máquina, com algum programa de busca de
arquivos, mc, por exemplo, você pode localizar esta biblioteca.
6 Este
136
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
XX = 5.2 porque a parte final, 20000220, é a data da distribuição.
Lá você pode encontrar, por exemplo, no caso do gcc:
• LONG MIN é o espaço ocupado por um ‘signed long int’. é também o
menor número inteiro do tipo “longo” reconhecido pelo gcc. Dito de outra
forma, é também o menor número deste tipo que você deve usar para não
gastar espaço à toa na memória. Ainda repetindo de outra forma, se você
precisar apenas de números menores que 32 bits, então não deve escolher
este tipo de dados.
é o mesmo tamanho do tipo de dados int.
• LONG MAX é o valor máximo que pode assumir um “inteiro longo”.
• ULONG MAX é o valor máximo que gcc entende para um inteiro sem
sinal. Tem a mesma capacidade dos inteiros longos com sinal.
• LONG LONG MIN é o menor valor que pode ser alcançado por um inteiro
longo com sinal. De outra forma, representa o espaço ocupado por este
tipo de dados: 64 bits que é menor do que 264 .
Retomando o que diziamos no inı́cio desta seção, os números no computador
formam um conjunto finito, como não podia deixar de ser. Em algumas linguagens, LISP, Python, é possı́vel manter esta barreira limitada pela memória
do computador e até “pensar” que não existem barreiras... por um artifı́cio
especial que estas linguagens têm para trabalhar com aritmética. Não é o caso
do C embora estas linguagens tenham sido construidas em C.
É, lembre-se da introdução, há coisas que não podemos fazer com C mas que
podemos fazer com linguagens ou pacotes que foram feitos em C . . .
Observação: 27 A conta de dividir
A conta de dividir em quatro operacoesXX.c não está errada, apenas não
é a divisão habitual. Quando você escrever em C, “a/b”, estará calculando
apenas o quociente da divisão entre os dois inteiros. Assim
3/4 → 0
porque o quociente na divisão de 3 por 4 é o inteiro 0. Nesta divisão o resto é
ignorado. No gcc há funções para recuperar o resto na divisão inteira de modo
que se possa escrever os dados do algoritmo da divisão euclidiana
d ividendo = d ivisor ∗ q uociente + r esto
O programa numeros02.c7 escreve os dados deste algoritmo.
Há outras funções que executam tarefas semelhantes, mas com números reais. Leia mais a este respeito na próxima subseção sob o tı́tulo, funções que
analisam números reais.
7 no
diretorio do DOS, procurar numer*.c
7.1. OS NÚMEROS EM C
7.1.2
137
Os números reais
Esta seção se dedica aos números não inteiros. Vou estar me referindo, nesta seção, aos
números reais e estarei assim super-adjetivando os números de que tratarei aqui, que nada
mais são do que números racionais. Em inglês se usa a expressão float para se referir ao
que chamaremos de reais. Em Português existe a expressão “número com ponto flutuante”
que corresponde ao float americano. Vou adotar “real” por ser mais curto, mas quero dizer
mesmo “número com ponto flutuante”.
Como eu já disse antes, em C, os números formam um conjunto finito, e êste é também o
caso dos números reais. Existe uma “aritmética” apropriada para estes números contida
num padrão elaborado e divulgado pela IEEE. É bom lembrar que poderiamos escrever 100
páginas sobre este assunto...você tem aqui um resumo.
Há muitos anos atrás havia nas tabernas e sobretudo nas lojas de material
de construção, umas máquinas mecânicas próprias para o cálculo com números
reais8 . As tais máquinas tinham duas teclas com setas para esquerda ou para
direita, ao lado do teclado, que permitiam deslizar a escala para direita ou para
a esquerda, era o ponto flutuando, e correspondia a multiplicar por 10 ou dividir
por 10. Veja na figura as teclas vermelha, à esquerda, com as setas. Apertando
as chaves laterais com o polegar e o apontador se limpava a “memória”... e
rodando a manivela, à direita, se fazia a multiplicação (soma repetida) ou divisão
(subtração repetida) conforme o sentido da rotação. Uma máquina mecânica
de multiplicar. (fig. 7.1).
Figura 7.1: Máquina do balcão do comércio, coleção do autor.
Quando você escrever
173737823.
8 ponto
flutuante...
138
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
observe que o ponto não foi um engano, gcc irá entender que não se trata de
um inteiro e fará diferença entre
173737823., 17373782.3, 173737.823, 17373.7823
entretanto
173737823. = 173737823.0 = 173737823.00
Observe que o ponto “flutuou”, era o que a máquina mecânica, de que falamos acima, fazia.
O gcc usa os seguintes valores para definir a fronteira do conjunto finito de
números reais:
1. reais com precisão simples
(a) FLT MIN 1.17549435E-38F
(b) FLT MAX 3.40282347E+38F
(c) FLT EPSILON 1.19209290E-07F
quer dizer com 9 algarismos significativos.
2. reais com precisão dupla
(a) DBL MAX 1.7976931348623157E+308
(b) DBL MIN 2.2250738585072014E-308
(c) DBL EPSILON 2.2204460492503131E-016 portanto com 16 algarismos significativos.
para que você possa ter uma idéia do que esta precisão pode significar, analise
o seguinte exemplo, sem levá-lo muito a sério...
Exemplo: 10 Erro no acesso à estação espacial
Por favor, não leve a sério este exemplo. Dê-lhe a importância indicada pelo tamanho da
letra. Um problema deste tipo se resolve com instrumentos bem mais avançados. Comentários
ao final.
Suponha que um programa monitorando o envio de uma nave espacial calcule o ponto de
encontro da mesma com à estação espacial internacional que se encontra em órbita terreste
num raio de 326 Km (ou 326.000 m)9 a partir do centro da Terra.
Simplificando o processo de condução da nave, como se nada mais houvesse entre seu
lançamento e sua chegada à estação espacial internacional, e seu o ponto de encontro estivesse
no centro da estação que deverá medir cerca de 120 metros quando estiver toda montada no
ano 200510 , vamos calcular o erro relativamente ao ponto de encontro com a precisão que
temos no gcc. Nossa simplificação vai ao ponto de supor que tudo se encontra estático
e portanto que a nave parte em linha reta de encontro à estação se dirigindo ao ponto
central. Quer dizer que estamos calculando a base de um triângulo isósceles cuja altura seria
326Km. A base deste triângulo é região de erro, a nave deveria chegar ao ponto central
onde se encontraria o acesso. Com erro de F LT EP SILON = 1.19209290E − 07 teriamos
erro = F LTE P SILON ∗ 326000 = .03886222854 m isto é, 3cm de erro.
Obviamente nada neste exemplo é real, a não ser aproximadamente as dimensões,
9 os
valores não são precisos, o raio terrestre não é examente 6 km, por exemplo
ao Prof. Emerson do Dep. de Fı́sica da UeVA por estas informações, ver
http:www.nasa.gov
10 grato
7.1. OS NÚMEROS EM C
139
• Não se enviam espaçonaves numa rota perpendicular à superficie da terra, apenas o
lançamento é feito numa perpendicular para diminuir o gasto de energia com a saı́da
da gravidade;
• As naves seguem rotas em forma de espiral para permitir a entrada em orbita perto do
ponto de interesse e para melhor aproveitar a rotação da terra e a força de gravidade
dos planetas, da lua ou do sol, por exemplo;
• A rota em espiral permite uma aproximação tangencial da órbita da estação espacial;
• As naves possuem um controle feito por computador que corrige a rota a cada ciclo do
computador, quer dizer a cada milhonésimo de segundo;
• O instrumento matemático usado neste tipo de problemas são as equações diferenciais
ordinárias vetoriais e não semelhança de triângulos ...
O exemplo serve apenas para mostrar que o erro F LT EP SILON = 1.19209290E − 07
entre dois números reais consecutivos é suficientemente pequeno para resolver um problema da
magnitude deste, envio de uma espaçonave para se acoplar com outra, com uma aproximação
aceitável. Muito mais exatos ficam os resultados com a precisão dupla.
Entretanto, para salvar o exemplo, as aproximações feitas a cada milhonésimo de segundo, podem usar “semelhança de trinângulos” e neste caso as alturas dos triângulos serão
tão pequenas que a precisão final será de milhonésimos de centı́metro (e não os 3cm calculados acima).
Finalmente, para resolver problemas crı́ticos, computadores são especialmente planejados
com quantidade grande de memória e neste momento, sim, a precisão da linguagem C pode
ser alterada, se for necessário. Mas, muito mais provável é que se construa uma linguagem
apropriada para o problema, veja o que já dissemos na introdução. Leia também a observação
em real01.c.
Os programas realXX.c são um complemento do presente texto. Eles vão
ser objeto dos exercı́cios seguintes.
Exercı́cios: 34 Experiências com os reais
1. Leia e rode real.c. Veja os comentários do compilador e procure entender quais são os erros encontrados. Há comentários sobre os erros no
programa corrigido real01.c.
2. Altere o programa real01.c para com ele fazer cálculo de áreas de triângulos
e quadrados. Corrija os erros em real01.c, leia os comentários do compilador.
Solução: real04.c
3. O programa real04.c tem diversos defeitos na saı́da de dados, rode-o,
observe os defeitos e corrija o programa. Solução: real05.c
4. Modularize real04.c de modo que o usuário possa calcular áreas de triângulos,
retângulos ou cı́rculos separadamente. Faça observação sobre a área de
quadrados semelhante a que se faz sobre cı́rculos. Sugestão: reutilize o
programa menu.c. Veja também o programa vazio.c.
Solução: real06.c
5. Corrija os diversos defeitos de lay-out das saı́das de dados do programa
real06.c.
140
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
6. Crie uma biblioteca, “aritmetica.h”, nela coloque as funções de real06.c.
Solução: real07.c, aritmetica.h
7. Deixe o programa real07 no tamanho da tela.
Solução: real08.c, aritmetica.h
8. O programa real08.c tem ainda alguns defeitos de apresentação, corrijaos e expanda o programa para que ele execute mais operações. Tente
manter a função principal toda visı́vel na tela. Saı́da, crie uma função
executa() na biblioteca aritmetica.h chmada de real09.c... e elimine
o switch de real08.c, claro, esconda, se você quiser pensar assim, o
switch em aritmetica.h
9. Faça um programa, chamado Matematica que execute algumas operações
geométricas e aritméticas. Depois, distribua seu programa na rede, ele
pode ser um tutorial.
Não se pode dizer que fizemos muita matemática nos programas acima, de
fato não. Entretanto você pode ver como se podem construir programas mais
complicados a partir de programas mais simples.
A suite de programas realXX.c foi construida na sequência definida pelos
exercı́cios do bloco anterior. Se você analisar com atenção irá encontrar aqui
uma resposta para aquela pergunta que ficou no ar desde o inı́cio:
Observação: 28 Existe alguma técnica para programar bem?
Uma resposta simples para esta pergunta não existe. Conseguir responder
a esta pergunta equivaleria a criar uma receita simples para resolver qualquer
problema... obviamente isto é um absurdo.
Entretanto nós lhe mostramos aqui um método que ajuda a começar:
1. Quebre o problema em pedacinhos e resolva cada um destes pedacinhos;
2. Não aceite que um programa fique maior do que a tela do computador...
Um programa que ficar todo na tela é fácil de ser analisado. Os insetos
se escondem facilmente em programas que ocupam várias telas ou milhões
de linhas.
3. Se você estiver precisando de técnicas para detetização (debug), certamente
seus programas são muito grandes. Quebre-os. Refaça tudo a partir do
começo.
4. Aprenda a programar de forma simples e refaça os seus programas. Adianta pouco corrigir programas quilométricos...
5. Crie funções pequenas que resolvam tarefas interessantes e pequenas. Veja
menu.c, vazio.c, aritmetica.h e ambiente.h.
7.1. OS NÚMEROS EM C
141
Funções de variável real
Vamos terminar esta seção analisando algumas funções da biblioteca11 glib.Você
vai encontrar mais informações consultando info do LinuX. Use os comandos
• Numa área de trabalho, digite info
• Dentro do info digite “m” e responda glib ou libc.
Vamos nos fixar nas funções que usaremos mais a frente e para isto colocaremos aqui indexação para facilitar sua busca de informações imediata. Mas
aprenda a consultar info, vale a pena.
As funções que escolhemos para descrever aqui, executam tarefas pouco usuais mas de grande importância na solução de diversos problemas. Elas fazem o
truncamento de números ou situam um determinado número entre dois inteiros.
Fazem também a conversão de real em inteiro.
Elas estão definidas na biblioteca math.h.
Apresentamos as funções com sua sintaxe explicitada, por exemplo, a primeira é
double ceil(double X)
apresentada no formato como apareceria se estivessemos declarando esta função
dentro de um programa. É assim que você irá encontrar a informação em libc.
• A função: double ceil (double X) calcula o inteiro mais próximo que seja
maior do que X. Assim ceil(X) será um número do tipo real, mas inteiro,
e maior ou igual que X.
Quer dizer que
– ceil(X) ≥ X;
– ceil(X) é um número inteiro do tipo real.
– Por exemplo
ceil(1.5) = 2.0
A palavra “ceil” vem de “ceiling”, (teto). Quer dizer que estamos calculando o “teto inteiro de 1.5”.
• A função:
double floor (double X)
calcula o inteiro mais próximo, e abaixo de X:
– floor(1.5) = 1.0
– floor(x) ≤ x;
– floor(x) é um inteiro, do tipo real.
11 Mais
pelo BC.
abaixo vamos lhe mostrar como você pode pesquisar as bibliotecas disponibilizadas
142
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
A palavra “floor” é a palavra inglesa para (piso). 1.0 é o número inteiro
(real) que está logo abaixo de 1.5. Escrevemos: “número inteiro (real)”
porque o resultado de floor() é um número real. Isto é importante, ao
calcular
como o resultado é um real: f loor(3.5)/4 → 0.75
(7.1)
se o resultado fosse inteiro: f loor(3.5)/4 → 0
(7.2)
São detalhes que esquecemos quando temos que fazer contas...e que se
perdem em programas com centenas de linhas.
• A função:
double rint (double X)
arredonda X para um inteiro guardando o tipo ’double’, de acordo com o
método de arredondamento definido para C. Veja em glib com info.
’Floating Point Parameters’ os vários tipos de arredondamento existentes.
O método default12 , entretanto, é “para o mais próximo inteiro”. Assim, se no C que você estiver usando, estiver preservada a especificação
de fábrica, rint(X) será o inteiro mais próximo de X. Será a alternativa
otimizada de escolha entre ceil(x), floor(x).
• A função: double modf (double VALOR, double *PARTE-INTEIRA) Esta
função quebra o argumento VALOR em duas partes, A, B tal que:
– A ∈ [−1, 1];
– B é inteiro mais próximo de zero;
– A, B tem o mesmo sinal de VALOR.
– Guarda o inteiro B em *PARTE-INTEIRA.
– Devolve A na linha de comando13 .
Por exemplo, ‘modf (-2.8, &parte inteira)’ devolve ‘-0.8’ e guarda ‘-2.0’
em ‘parte inteira’, dois números negativos porque V ALOR = −2.8 < 0.
É preciso terminar dizendo, estamos longe de ter esgotado as funções definidas em math.c. Consulte libc com auxı́lio de info para se dar contas disto.
Dentro de info use o comando “m” e digite glib ou libc.
7.1.3
Bibliotecas do BC
É através do help que você vai descobrir e analisar as biliotecas disponı́veis com
o BC. Abra um programa qualquer e coloque o cursor sobre floor e clique no
botão help. Ao cair o menu, escolha Topic search e você vai cair num help
sobre a função floor().
12 a
palavra “default” significa “padrão”
C não é uma linguagem interpretada, não tem sentido falar em “valores na linha
de comando”... e sim, simplesmente, devolve A.
13 Como
7.1. OS NÚMEROS EM C
143
Observe no canto direito superior MATH.H, possivelmente em amarelo. É
um indicativo de que esta função está na biblioteca math.h. Se você clicar
em MATH.H o help vai levá-lo para ver as funções desta biblioteca. Tudo que
dissemos acima sobre glib vale aqui para help do BC
Siga tela abaixo e você vai encontrar um programa-exemplo ilustrando como
funciona esta função.
Em math.h você vai encontrar as outras funções cuja referência fizemos
acima. Aproveite para passar os olhos sobre os nomes das funções e você vai
encontrar ceil() entre muitas outras. Desça até o final da página e você irá
encontrar List of all header files.
Header file é o que chamo biblioteca. O texto está enfatizado, coloque o
cursor sobre ele e dê enter. Você vai encontrar a mesma listagem que existe
sob Linux. Escolha alguma dessas bibliotecas para fazer uma analise rápida, e
pronto, você já sabe onde pode encontrar as informações. Saia e volte quando
precisar.
Cast - transformando tipos de dados
Há uma operação em C chamada cast que serve para transformação entre dois
tipos de dados (de um “maior” para um “menor”).
Por exemplo, você pode transformar um número do tipo real em outro do
tipo inteiro “jogando” o real “dentro” do inteiro e naturalmente perdendo
alguma informação: a parte fracionária.
Mas também você pode “jogar” inteiros nos reais, com rint(), ver cast.c).
Exercı́cios: 35 float, ceil(), floor()
1. Rode e leia o programa floor.c.
2. Altere floor.c para calcular rint().
Veja o seguinte exemplo que é bem comum.
Exemplo: 11 Transformando dados
Considere um programa que calcule percentuais de dados inteiros, é o caso
de uma pesquisa de opnião.
Os dados que vão ser usados são
• Total de pessoal entrevistadas, um inteiro;
• número de pessoas que adota uma certa opinião, outro inteiro
entretanto vamos querer calcular
opinião
total
e a conta vai ficar errada, porque, você já viu, no inı́cio deste capı́tulo, que
3/4 = 0
144
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
em C.
A saı́da é escrever
((f loat)3)/4 = 0.75
A expressão, ((tipo) var) se chama
• em, C, “cast”,
ao do tipo de dados.
• em português, transformaç~
Observe que basta fazer ((f loat)3) porque se um dos “fatores” for tipo float o
resultado será deste tipo.
Utilidade desta operação? Inteiros ocupam menos espaço de memória que os
reais! Como o programa vai receber dados inteiros, é melhor definir as variáveis
que vão receber estes dados como inteiras ganhando espaço na memória e
tempo de processamento! No cálculo final se faz a transformaç~
ao do tipo de
dados, para conseguir o resultado corrreto.
Observe que se var for do tipo float então
((int)var) ≡ f loor(var)
Rode o programa cast.c para ver as limitações da transformação de dados e
a perda de informações que ela pode acarretar. O seu uso fica restrito a número
pequenos.
O programa cast.c vai lhe mostrar que a equivalência acima se torna discutı́vel quando os números forem grandes.
7.2
Caracteres e vetores de caracteres.
Os caracteres
A palavra chave da linguagem C, para caracteres é char. Veja o programa14
quatro operacoes02.c
onde a variável operador está definida como
char operador;
e deve receber um dos seguintes valores
+, ∗, /, −
Veja que o método, internamente (dentro do programa), consiste em escrever
0
+0 ,0 ∗0 ,0 /0 ,0 −0
e não como escrevemos acima, nem
” + ”, ” ∗ ”, ”/”, −”.
14 no
diretório do DOS, procure qua opr*.c
7.2. CARACTERES E VETORES DE CARACTERES.
145
Há uma diferença substâncial entre
” + ”,0 +0 , +
• a é um sı́mbolo que pode (ou não) ter um valor associado. Então + é um
sı́mbolo associado ao qual se encontra o algoritmo da adição;
• ’a’ é um caractere, é um dos 256 caracteres reconhecidos no teclado.
• ”a” é um vetor de caracteres, neste caso um vetor de tamanho 1.
Exercı́cios: 36 Diferença - caracteres-strings
1. Altere o programa quatro operacoes02.c usando “x” em vez de ’x’ em
cada ocorrência dos elementos de
{0 +0 ,0 ∗0 ,0 /0 ,0 −0 }
e analise o resultado.
2. Altere o programa15 quatro operacoes02.c usando x em vez de ’x’ em
cada ocorrência dos elementos de
{0 +0 ,0 ∗0 ,0 /0 ,0 −0 }
e analise o resultado.
O resultado da experiência no primeiro exercı́cio foi a seguinte:
warning: comparison between pointer and integer
e a justificativa é:
• “x” é um vetor (ponteiro...), é a diferença anunciada acima:
”x” é um vetor de caracteres
’x’ é um caractere
e os vetores são naturalmente ponteiros em C.
• A variável operador foi definida para receber um caractere;
• Caracteres, junto com os números são os dados básicos, (leia: os valores
básicos) da linguagem C
• Vetor é uma variável indexada e isto se faz com (ponteiro) em C.
Vamos aprofundar mais esta questão na seção sobre ponteiro, que é a
próxima, e você, sem nenhum preconceito, pode lê-la agora e depois retornar a
esta. Mas faça uma leitura rápida porque a presente discussão é fundamental.
15 no
diretório do DOS, procure qua opr*.c
146
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
Observação: 29 Valores básicos
Vamos rapidamente insistir na expressão valores básicos da linguagem. Digamos que C
foi projetada para lidar com números e caracteres. Seria pouco dizer isto, ela foi projetada
para trabalhar com os sı́mbolos que você pode encontrar no teclado de sua máquina, como por
exemplo *, 2,ˆ.
Seu objetivo seria criar outras linguagens, (originalmente um sistema operacional) que
soubesse lidar com estes objetos, os caracteres, de modo a criar expressões que tivessem
signficado tanto para a máquina como para o ser humano que a fosse manipular.
Uma linguagem de baixo nı́vel, se dizia, (tem gente que ainda diz...)
Hoje a linguagem C se projetou além deste objetivo estrito e podemos com ela fazer outros
tipos de trabalho, (de alto nı́vel...).
A solução mais prática, para manter a linguagem com sua especificação inicial foi a de
criar os vetores para entender “aglomerados de caracteres” como este:
A = “aglomerados de caracteres”.
então A é um vetor, quer dizer uma variável formada de uma sucessão de “caracteres”,
numerados sequencialmente. Voltaremos logo abaixo a esta questão.
O segundo exercı́cio no bloco acima produziu outra reclamação por parte do
gcc. Agora o compilador se perdeu totalmente... a lista de reclamações passou
de uma página porque o erro (operador == ∗) confundiu o resto da análise. ∗
agora é o nome de uma função que tem uma sintaxe particular: a ∗ b, deve estar
entre dois caracteres que gcc possa identificar como “números”.
A sintaxe é algo extremamente importante para as linguagens de programação.
A precisão tem que ser total. Cada sı́mbolo tem que estar colocado exatamente
nas condições especificadas. ’*’ é diferente de ∗, também
0 0
1 6= 1 ; 0 20 6= 2.
Temos assim dois grandes tipos de dados em C com os quais podemos construir todos os outros:
• caracteres: ’1’, ’2’, . . . , ’a’, ’b’, . . . ;
• números que são formados a partir dos caracteres especiais 1,2,..,9,0
Vamos passar a discutir logo os vetores de caracteres e ao final faremos
comparações e exercı́cios que terminarão por completar as ideias.
Caracteres especiais
Há vários caracteres especiais, a lista de exercı́cios seguinte é um tutorial sobre
caracteres.
Exercı́cios: 37
1. Leia, rode e leia o programa ascii.c e, claro, você nada
viu. Leia os comentários no programa.
2. Leia, rode e leia agora o programa ascii 1.c. Este programa imprime
um trecho da tabela ASCII, a partir de um ponto inicial que lhe vai ser
solicitado.
3. Em ascii 1.c responda inicialmente com o número 1 quando isto lhe for
pedido, e veja que nada será impresso. Veja a partir de quando alguma
coisa “útil” é impressa respondendo ao programa outros pontos iniciais.
7.2. CARACTERES E VETORES DE CARACTERES.
147
4. O programa ascii 2.c é uma pequena variante de ascii 1.c. Veja qual
a diferença.
5. Leia, rode e leia ascii 3.c. Este programa usa uma variável, pausa para
dar saltos de páginas formadas de 60 elementos da tabela ASCII. Mas
verifique que a impressão fica mal feita em alguns casos, descubra por
que.
6. Altere ascii 3.c para que cada página contenha 80 elementos da tabela.
Solução: ascii 4.c
7. Veja em qualquer dos programas asciiX.c como imprimir caracteres,
usando o formatador %c Faça um programa para imprimir
printf(‘‘%c’’,7);
o ascii 7 que aciona a campinha.
Solução: apeteco2() em ambiente.h
ASCII é uma sigla que significa American Standard for Communication and
Information Interchange e foi criado, como o nome o indica, para criar um
padrão para comunicações, possivelmente caminhando para se tornar obsoleto,
ou de uso restrito ao núcleo interno do processamento de dados (como a definição
dos códigos de teclados).
Os primeiros códigos ASCII são “especiais” servem para passar página, linhas, controlam a campainha do sistema, etc... não sendo porisso “visı́veis”
(possı́veis de serem impressos).
Vetores de caracteres
A palavra string significa um objeto do tipo
‘‘asdfafeqerr rere qerqe weqrqrerer’’
um conjunto de caracteres, o que pode incluir espaço em branco, enfeixados por
aspas duplas. Em C este objeto é um vetor de caracteres, quer dizer, uma
matriz com uma linha e várias colunas.
O primeiro elemento da matriz, ’a’ é indexado por zero e assim sucessivamente os demais, por 1,2,etc...
Se dermos um nome ao vetor
frase = ‘‘asdfafeqerr rere qerqe weqrqrerer’’
(e observe que a sintaxe acima não será aceita por C a não ser na inicialização
e definição da variável) então
f rase[0] =0 a0 , f rase[1] =0 s0 , ...
A forma de fazer atribuição a um vetor de caracteres, fora da área de inicialização, é usando a função strcpy(). A igualdade acima seria:
strcpy(frase,‘‘asdfafeqerr rere qerqe weqrqrerer’’)
depois do que, em algum ponto do ponto posterior do programa, f rase[2] faz
referência ao caracter ’d’.
148
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
Um caracter especial, o nulo, (NULL), ’\0’, fecha um vetor de caracteres.
Assim, no exemplo acima, temos:
frase[31]=’e’, frase[32]=’r’, frase[33]=’\0’
entretanto, na declaração de variáveis se pode ter:
char frase[80]
gerando as seguintes respostas:
sizeof(frase) -> 80; strlen(frase) -> 32
Veja mais a respeito de vetores de caracteres e as funções que os manipulam em info, use o comando m dando-lhe como resposta libc. Você vai
encontrar, no ı́ndice Character Handling, a lista das funções para manipular
strings.
Exercı́cios: 38 Caracteres e vetores de caracteres
1. Rode (não leia...) o programa carac01.c. Ele explica caract1.c. Acompanhe com caract1.c aberto em uma janela.
2. Leia e rode o programa caract1.c . Altere caract1.c para fazer uma
busca de um caractere qualquer fornecido pelo teclado.
3. Rode o programa caract7.c para ver o tamanho do espaço ocupado, na
memória, por um caractere. Leia o programa também.
4. Leia e rode o programa caract11.c , verifique que o programa não se
explica. Mofique-o para que o programa diga ao usuário o que vai ser
feito.
5. Leia e rode o programa caract12.c , novamente este programa é executado sem grandes explicações. Inclua as mensagens necessárias.
6. Em caract12.c Troque o valor de busca para verificar segunda possibilidade do programa.
7. O programa caract13.c tem um erro, mesmo assim merece ser estudado.
Leia o programa e descubra o erro.
Solução caract14.c
7.3
Ponteiros.
Ponteiro é um “super tipo”de variável em C no sentido de que é um tipo de variável de
qualquer outro tipo... tem ponteiro do tipo inteiro, tem ponteiro do tipo real (float) etc...
Mas claro, esta não é a melhor forma de iniciar a discutir este assunto.
As variáveis do tipo ponteiro guardam endereços na memória que serão associados às outra
variáveis. Os exemplos a seguir vão deixar bem claro o que é isto. O importante nesta
introdução, é desmistificar (e ao mesmo tempo mostrar a importância), deste tipo de dados.
É dizer que ponteiro aponta para um endereço inicial de memória para guardar um tipo de
dado: inteiro, float, etc...
7.3. PONTEIROS.
149
Você pode criar uma variável to tipo ponteiro com a seguinte declaração:
tipo
outro\_nome {\tt nome}
*{\tt nome}ptr;
// uma variavel do tipo ponteiro.
Com esta declaração, você
• nomeprt Criou uma variável do tipo ponteiro para guardar o endereço
de um determinado tipo de variável, por exemplo int. O nome da variável
é arbitrário, os programadores tem o hábito de acrecentar ”ptr”ao final
do nome para facilitar a identificação, no programa, das variáveis do tipo
ponteiro.
• criou uma variável “outro nome”do mesmo tipo que a variável do tipo
ponteiro. Não é necessário fazer isto, mas com frequência é conveniente,
inclusve assim (insistindo):
tipo outro nome, nomeptr;
• separou na memória espaço suficiente e necessário para associar com a
variável nome. Por exemplo, se nome for do tipo int haverá 4 bytes separados a partir de um ponto inicial de memória;
• em algum momento, no código do programa, você deverá incluir o seguinte
comando:
nomeptr
= &nome;
que fará a associação entre nome e os endereços reservados por nomeptr.
Exemplo: 12 Um tutorial sobre ponteiros
Os programas
pont.c, pont1.c,...pont16.c
representam alguns tutoriais sobre ponteiros. Vamos apresentar alguns deles
agora.
1. Primeira etapa.
Leia e rode os programas
pont.c, pont1.c, pont2.c
nesta ordem. Se possı́vel abra duas áreas de trabalho (shells). Numa rode
o programa pontXX.c e na outra edite o programa para que você possa
acompanhar o que ele está fazendo.
2. Segunda etapa.
Vamos comentar o que você viu. Se alguma coisa do presente comentário
lhe parecer confuso, repita a primeira etapa e retorne ao ponto em que a
explicação lhe pareceu obscura.
A discussão está baseada em cada um dos programas.
150
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
• pont.c Criamos duas variáveis de tipo int. Uma delas é um ponteiro
para um inteiro, numptr. Veja a forma como se declaram ponteiros:
int *numptr;
Inicialmente a variável num nada tinha o que ver com numptr. Ao
executar
num = *numptr
foi estabelecida uma ligação. Experimente alterar a ordem dos dois
comandos:
num=*numptr; num=6;
e você vai ver que o resultado é o mesmo. Mas o comando
num=*numptr;
é o que estabelece a ligação entre as duas variáveis. A partir de sua
execução, alterações na variável num serão registrada por *numptr.
A variável numptr guarda um endereço de memória que pode ser associado a um inteiro, isto quer dizer, ela guarda o primeiro endereço
de um segmento de memória que pode guardar uma variável do tipo
inteiro: 4 bytes de memória.
• pont1.c O programa começa lhe pedindo um valor, mas não se deixe
envolver...
Observe que o programa cometeu um erro: não indicou que tipo de
dado espera, deveria ter dito: “me forneça um valor inteiro para a
variável.”
Veja outra forma de associar variável e variável do tipo ponteiro (ou
variável com endereço). O efeito é o mesmo que o obtido com a
método de pont.c, é apenas outro método.
Neste ponto você pode ver o uso dos operadores
*, &
para acessar
– * valor associado ao ponteiro;
– & endereço do ponteiro.
• pont2.c Como toda outra variável, podemos declarar um ponteiro
inicializado. Isto foi feito neste programa.
Você pode ver a sequência de números
01234...01234
indicando o ı́ndice de cada um dos valores, caracteres, dentro do vetor
de caracteres apontado pelo ponteiro.
Novamente vemos aqui o uso dos operadores
*, &
para acessar valor ou endereço.
O programa lhe mostra o tamanho do vetor, e você pode ver uma
caracteristica da linguagem C que toma como ı́ndice inicial o 0.
7.3. PONTEIROS.
151
Depois o programa percorre um laço usando os ı́ndices do vetor de
caracteres e imprimindo o valor de cada novo endereço e mostrando o
endereço invariável, sempre mostrado, o endereço inicial do segmento
de memória em que se encontra guardada o valor “isto é um teste”.
O último valor de vetor é o NUL.
Exercı́cios: 39 Laboratório com ponteiros Vamos trabalhar com os programas
pontXX.c
1. Experimente imprimir com printf(), o nome de alguma função e analise
o resultado. Por exemplo, altere a função main(), dentro do programa
menu.c para que ela contenha apenas o comando:
printf(executa);
Se der erro16 ... complete os parâmetros de printf() com a formatação
adequada!
2. Altere o programa pont1.c incluindo novas variáveis (com os respectivos
ponteiros) para que você entenda como está utilizando “ponteiros”.
3. Melhore pont1.c incluindo mensagem indicativa de que tipo de dado o
programa espera. Não se esqueça de alterar o nome do programa, os “programas errados”têm uma função especı́fica de laboratório e não devem ser
alterados.
4. Altere pont1.c solicitando outros tipos de dados, como caracteres, número
real, etc... Não se esqueça de alterar o nome do programa.
5. Leia, rode e leia o programa pont2.c. Acrescente novas variáveis, rode
novamente o programa até que fique claro o uso dos operadores &, *.
Observação: 30 Identificadores de funções
Os identificadores das funções são variáveis do tipo ponteiro.
Observação: 31 Virtude e castigo.
Se pode dizer que o uso de ponteiros é tanto uma das dificuldades básicas da linguagem
C, por um lado, e por outro lado, a sua principal virtude.
Com a capacidade de acessar e manipular os dados diretamente na memória, a linguagem
ganha uma rapidez tı́pica da linguagem assembler que faz exclusivamente isto. Linguagens
mais evoluidas dentro da escala da abstração não permitem que o programador acesse diretamente a memória, elas fazem isto por eles. Com isto se ganha em segurança que falta ao
C, (a segurança nos programas em C tem que ser conquistada centı́metro a centı́metro...).
Melhor seria dizer, em vez de segurança, abstração, porque é possı́vel programar em C com
grande segurança
• deixando de usar ponteiros e perdendo assim uma das caracterı́sticas da linguagem;
• aprendendo a dominar os ponteiros e fazendo registro (comentários) do seu uso quando
eles se tornarem decisivos.
16 compre
outro livro...
152
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
Como já dissemos anteriormente, se você declarar uma variável como inteiro, e nela
guardar um número em formato de ponto flutuante, o risco mais elementar que o seu programa pode correr é que o espaço de nomes reservado ao C pelo sistema operacional fique
inútil por superposição de dados, e consequentemente o seu programa deixe de rodar. Um
risco maior é que o setor de memória reservado ao C seja estourado e outros programas do
sistema venham a ser corrompidos e consequentemente o computador fique pendurado.
Portanto, todo cuidado é pouco no uso de ponteiros, e por outro lado, se perde uma
grande possibilidade da linguagem se eliminarmos o uso dos ponteiros, ao programar em C.
Mas você pode, literalmente, programar em C sem usar ponteiros. Tem autores que
sugerem fortemente esta atitude. Eu evito o uso de ponteiros, mas em geral não preciso deles
para o meu trabalho.
Observe que numa mesma declaração é possı́vel, com o uso da vı́rgula, declarar variáveis do tipo ponteiro e do tipo comum:
int *n,m ;
n é do tipo ponteiro apontando para um número inteiro, e m é do tipo inteiro,
guardando o valor de um número inteiro. Mas se trata de um mau hábito,
porque as duas declarações, a que está acima, e a que vem logo abaixo
int *nptr;
int m;
ocupam o mesmo espaço quando o programa for compilado e diferem de uma
pequena quantidade bytes17 quando guardadas em disco, entretanto a segunda é
mais legı́vel que a primeira e deve ser esta a forma de escrever-se um programa,
da forma mais legı́vel, para que os humanos consigam lê-los com mais facilidade,
uma vez que, para as máquinas, as duas formas são idênticas.
Nós escrevemos programas para que outras pessoas, que trabalham conosco,
na mesma equipe, possam ler com mais facilida e assim o trabalho em equipe
possa fluir.
As seguintes declarações podem ser encontradas nos programas:
/* duas variaveis do tipo ponteiro para guardar enderecos
de memoria onde esta\~ao guardados numeros inteiros. */
int *n,*m;
/* variaveis de tipo inteiro. */
int l,p;
char palavra;
/* variavel de tipo ponteiro apontando para local de memoria onde
se encontra guardada uma variavel de tipo char */
char *frase;
/* variavel de tipo ponteiro apontando para local de memoria onde
se encontra guardada uma variavel de tipo ponto flutuante. */
float *vetor;
significando respectivamente que se criaram variáveis de tipos diversos algumas
do tipo ponteiro, como está indicado nas observações.
17 um
byte é formado de 8 bits, e um bit é um zero ou um 1
7.3. PONTEIROS.
7.3.1
153
Operações com ponteiros.
Crucial é o processo operatório com ponteiros. Vamos discutir isto em dois
momentos:
• acesso às variáveis do tipo ponteiro
• operações aritméticas com ponteiros
Acesso aos ponteiros.
As operações de acesso aos ponteiros são:
• atribuir um valor ao endereço apontado. Isto pode ser feito de duas formas:
1. Veja pont1.c
numptr = &num;
se houver algum valor atribuido a num, o que sempre há, então fica
indiretamente atribuido um valor ao endereço &numptr
2. Veja pont.c
num = *numptr
Em ambos os caso, mais do que atribuir um valor a um endereço
o que está sendo feito é associar um endereço a uma variável. De
agora em diante mudanças de valores em num são automaticamente
associadas com o endereço &numptr.
• explicitar o endereço.
É feito com o operador &.
• ir buscar um valor associado ao endereço apontado.
É feito com o operador *
Operações aritméticas com ponteiros.
Por razões obvias, só há duas operações que se podem fazer com ponteiros:
somar ou subtrair um número inteiro.
Ponteiro é endereço, e da mesma forma como não teria sentido somar endereços de casas, não tem sentido somar ponteiros, mas tem sentido somar um
número inteiro18 a um ponteiro, ou subtrair.
Como um endereço é um número inteiro, C permite somar dois ponteiros.
Quando isto é feito, na verdade está sendo somado o espaço ocupado por uma
variável a um deteminado endereço.
Também a única operação lógica natural com ponteiros é a comparação para
determinar a igualdade ou uma desigualdade entre os valores para os quais eles
apontam.
18 como
teria sentido somar ao endereço de uma casa 10 ou 20 metros para indicar onde
começa a próxima casa, o mesmo se passa com os ponteiros e o tamanho do tipo de dado...
154
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
Exercı́cios: 40 Ponteiros
1. Rode e leia pont3.c, e faça o que o programa pede, e volte a rodar o
programa.
2. O programa pont4.c não pode ser compilado. Veja qual o erro e o corrija.
Depois de corrigido, rode o programa. Não altere o programa, mude-lhe o
nome.
3. Altere o valor da variável em pont4.c e volte a rodar o programa. Altere
o valor da variável colocando nela uma frase que tenha sentido, volte a
rodar o programa.
4. Acrescente a mudança de linha no comando de impressão de pont4.c,
dentro do loop, e volte a rodar o programa.
5. Verifique porque pont5.c não roda, corrija-o e depois rode o programa. A
solução se encontra no comentario do programa.
6. Traduza para o inglês todos os programas da suite pontXX.c.
7.4
Manipulando arquivos em disco
Arquivos são um objeto ao qual estão associados cinco métodos mais importantes:
• fopen() abrir
• fclose() fechar
• fclose(‘‘arquivo’’,’’r’’) abrir para ler (read ’r’)
• fclose(‘‘arquivo’’,’’w’’) abrir para escrever (write ’w’)
• fclose(‘‘arquivo’’,’’a’’) abrir para acréscimos (append ’a’)
• fprintf() é a irmão de printf() para enviar dados para arquivo em
disco.
• fgets() faz leitura vinda de um arquivo. Observe a mudança na sintaxe.
A função fopen() tem um papel especial aqui, veja os detalhes técnicos nos
programas
• leitura com fgets() prog20 X.c
• escrita com fprintf() em agend4.c
Há duas formas de usar fopen(). Uma simples:
fopen(nome do arquivo, ‘‘w|r|a’’)
você deve escolher um dos métodos w,r,a e uma outra forma mais algébrica
em que nome do arquivo é uma variável e contém o nome do arquivo.
Se habitue de escrever sempre a companheira de de fopen(), a função
fclose(nome do arquivo) na linha de baixo, assim que usar fopen(), para
evitar de esquecer.
7.5. MATRIZ, (ARRAY)
155
Não fechar um arquivo pode deixá-lo corrompido.
A sequência de programas prog20 X.c mostra a evolução de tentativas para
ler dados gravados num arquivo em disco, você deve rodá-los e ler os comentários
que explicam porque os programas falharam. Sempre, o programa de maior
ı́ndice é o programa “mais correto” da suite considerada.
Exercı́cios: 41 Uso de arquivos em disco
1. O programa prog20.c está errado, ignore isto inicialmente. Leia, rode
e leia o programa para entender como ele funciona. Leia os comentários
e se precisar, faça pequenas modificações com o objetivo de entender o
programa e fazê-lo funcionar (não se esqueça de trocar-lhe o nome).
2. prog20.c não diz quantas ocorrências foram encontradas da palavra escolhida. Altere isto para ficar sabendo quantas vezes aparece a palavra
escolhida, é apenas um erro de portugues...e claro, o resultado errado está
também neste ponto!
Solução prog20 7.c
3. O programa prog20.c está fazendo uma estatı́stica errada. Tente descobrir o erro.
7.5
Matriz, (array)
Tabela de dados.
Há dois tipos de dados em C para tabular dados:
1. As matrizes, (arrays);
2. As estruturas (structs).
Neste parágrafo vamos estudar as matrizes, e no seguinte as estruturas.
As matrizes são uma das invenções mais antigas da matemática moderna19 .
Veja um exemplo:

1 2 3
A =  2 −1 3
2 −1 3

−1
2 
2
(7.3)
A matriz A tem 12 elementos distribuidos em tres linhas e quatro colunas.
Cada linha tem quatro colunas, ou vice-versa cada coluna tem tres linhas.
Esta “indecisão” sob a forma de ver a distribuição dos elementos de A forçou
uma convenção chamada “lico” (linha-coluna). Por esta convenção vamos ver
uma matriz como formada por linhas e as linhas formadas por colunas. Desta
forma o “endereçamento” dos elementos na matriz fica definido:
19 Se
você de fato exigir uma definição de “matemática moderna”, que tal dizer que é aquela
que estamos usando e construindo hoje...
156
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
A3
enquanto que que A4
Em C diremos:
3
4
=2
não existe nesta matriz. A notação acima é matemática.
A[3][4] = 2;
ou melhor, podemos20 atribuir o valor 2 à posição 3, 4 da matriz A.
C “entende” as matrizes de modo diferente do que fazem as demais linguagens
de programação. Compare com os vetores de caracteres (strings) que
também são matrizes.
As matrizes em C são “endereços”, portanto ponteiros. A declaração de
uma variável do tipo matriz se faz assim:
float A[30][20];
ou mais genericamente:
tipo_de_dado identificador[num_lin][num_col];
Veja no programa-errado texto.c a definição da variável palavras, observe como falamos: a variável “palavras”. O identificador é “palavras” e não
“palavras[80][5]”.
Vamos insistir na observação que fizemos acima, de que as matrizes em C
são ponteiros e corrigir o erro acima a respeito da atribuição.
Veja as duas linhas seguintes de código para fazer atribuições de valores às
matrizes. Suponha que palavras represente uma matriz definida assim:
int palavras[10][5];
Vejamos agora como se fazem atribuições de dados nos elementos das matrizes:
• Forma errada de atribuir valor ao endereco [i][j] :
fgets(deposito, sizeof(deposito), stdin);
sscanf(deposito, "%s", &palavras[i][k]);
• Forma certa de atribuir valor ao endereco [i][j] :
fgets(deposito, sizeof(deposito), stdin);
sscanf(deposito, "%s", palavras[i][k]);
sem o redirecionador de endereço porque
palavras[i][k]
20 falso...,
veja logo abaixo como é que se fazem atribuições!
7.5. MATRIZ, (ARRAY)
157
já é um endereço (ou um ponteiro).
Observação: 32 Índices em C
Se fossemos definir ı́ndices terminariamos por escrever outro livro21 ...
Falando levianamente, digamos que ı́ndexação serve para estabelecer a correspondência
entre elementos de dois conjuntos, (seria portanto uma espécie de função? resposta: sim).
Mas em geral não queremos ver os ı́ndices desta forma e sim como uma espécie de “contagem”22 .
Veja a matriz A da matemática. Dissemos acima que o elemento 3, 4 era o dois:
A3
2
= 2.
Exatamente como os apartamentos de um prédio de 3 andares em que houvesse 4 apartamentos por andar, 3, 4 é o número de um apartamento. A vı́rgula define uma “sintaxe”
separando o indicativo da linha do indicativo da coluna.
As matrizes são a estrutura de dados das tabelas retângulares com mútiplas entradas em
que todas as entradas são do mesmo tipo:
1. apartamentos;
2. números
nos exemplos que demos acima. Na próxima seção veremos tabelas em que os elementos são
de formato e tipo diferentes, as estruturas.
Vamos terminar esta observação falando de uma peculiaridade da linguagem C. Os ı́ndices
em C começam de zero. Quer dizer, quando definirmos
f loatA[4][7]
teremos os seguintes endereços disponı́veis:
"
a0,0
a1,0
a2,0
a0,1
a1,1
a2,1
a0,2
a1,2
a2,2
a0, 3
a1, 3
a2, 3
a0,4
a1,4
a2,4
a0,5
a1,5
a2,5
a0,6
a1,6
a2,6
#
(7.4)
E o programador deve tormar cuidado para otimizar seu uso da memória.
Se não usar os ı́ndices a partir de zero, estará deixando memória desocupada, mas “reservada”.
A declaração
int a[4][7];
separa na memória 28 espaços para números reais, portanto 28 x 4 bytes =
112 bytes vai ocupar a matriz a na memória do computador ou no disco.
Existe uma maneira de falar, resumida, que caracteriza o “tamanho” de uma
matriz e a disposição de suas linhas e colunas: dizemos que a é matriz 4 x 7,
que se lê “4 por 7”.
Definição: 1 Dimensão de uma matriz De forma mais geral, diremos que uma
matriz a é n x m, ou “n por m” se ela tiver n linhas e m colunas. Isto é
também o que se chama de “dimensão” de uma matriz.
Exercı́cios: 42 Matrizes em C
21 que
esta observação não o assuste, mas também fique certo de que ı́ndice é um assunto
complexo
22 ora, isto volta a ser função...
158
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
1. Escreva um programa criando uma matriz 3 x 4 de inteiros. Comentário:
você vai precisar de um while.
2. Quanto de memória ocupa uma matriz 3 x 4 de inteiros. Resp 48 bytes.
3. Quanto de memória ocupa uma matriz 3 x 4 de reais. Resp 384 bytes =
12 x 32 bytes.
4. Use um programa que avalie o uso da memória no computador que você
estiver usando e veja qual é maior matriz de números reais que um programa poderia manipular nesta máquina. A resposta não é única, veja
uma questão, nesta lista, sobre disquetes.
5. Quais são os tamanhos máximos de matriz que podemos guardar num
disquete de 1.4kb ?
Solução: 1 fatorando 1440, temos 1440 = 2 x 2 x 2 x 2 x 2 x 3 x 3 x 5
que pode gerar os seguintes pares de fatores:
2 x 720; 4 x 360; 8 x 180; 16 x 90; 32 x 45
96 x 15; 288 x 5; . . .
Isto é, todos os pares x x y; x ∗ y = 1440.
6. Usando matrizes para textos, que horror!
(a) Analise o programa “errado”texto.c, rode-o.
(b) O erro do programa texto.c se encontra demonstrado ao final
do mesmo, no último laço. Analise o que está acontecendo
e tente a correç~
ao.
correç~
ao em texto01.c.
(c) atribuiç~
ao de valor Troque, em texto.c
palavras[1]=
palavras[2]=
palavras[3]=
palavras[4]=
"Marcar consulta com um especialista.";
"Agendar algum exame de laboratório.";
"Marcar uma consulta de ret^
orno.";
"Bater papo à-toa.";
por
strcpy(palavras[1],
strcpy(palavras[2],
strcpy(palavras[3],
strcpy(palavras[4],
"Marcar consulta com um especialista.");
"Agendar algum exame de laboratório.");
"Marcar uma consulta de ret^
orno.");
"Bater papo à-toa.");
e compile o programa. Procure assimilar a mensagem de erro
que surge, ela lhe diz ‘‘incompatible types in assignment’’
significando que a atribuiç~
ao (assignment) tenta associar
23
tipos de dados incompatı́veis.
23 A
mensagem é estúpida, não são os tipos de dados que são incompatı́veis...
7.6. ESTRUTURA, STRUCT.
159
(d) Observe que texto.c se comp~
oe de tres etapas bem marcadas:
i. diálogo com o usuário;
ii. uma entrada de dados;
iii. uma saı́da de dados.
Marque estas etapas com comentários.
soluç~
ao em texto01.c.
(e) Transforme cada uma das etapas do programa texto01.c em uma
funç~
ao, modularizando o programa.
soluç~
ao em texto02.c.
7.6
Estrutura, struct.
Dados estruturados, foi o grito de guerra da ciência da computação na década
de 70. Claro, hoje continuamos aquela tarefa com um tipo de estruturação
mais aprofundada, orientação a objetos. Vamos ver aqui como se estrutura
a informação, em C e por que. Mas não chegaresmos a discutir orientação a
objetos, que é tı́pica de linguagens como C + +, Python, Java, entre outras
menos populares.
Uma estrutura, (struct), é uma construção, em C de um novo tipo de dados
formado de campos onde dados de tipo diferentes são guardados. Dissemos
dados de tipos diferentes porque, se os dados forem do mesmo tipo será melhor
usar vetor em vez de uma estrutura.
Exemplos de estruturas são as tabelas dupla ou múltipla entrada, em que
cada coluna (ou linha) guarda um tipo de dado.
O tipo de dado struct, estrutura, em C se inspira nas tabelas de dupla
ou múltipla entrada, em que cada coluna (ou linha) guarda um tipo de dado:
entrev. \ dados
José
Maria
João
Francisco
salário
500,00
600,00
1.000,00
800,00
idade
27
31
45
35
turno
m,t
t,n
m,n
t,n
profissão
professor
professora
professor
médico
especialidade
literatura
sociologia
matemática
ginecologia
bairro
Junco
Centro
Centro
Junco
que poderia ser uma folha de censo com as respostas dadas por 4 entrevistados.
Quer dizer que para este censo se considerou importante registrar os campos
salário, idade, turno, profissão, especialidade,bairro
como caracterı́stica de cada entrevistado. Um “cidadão”, para este censo, se
caracteriza pelas propriedades acima. Não interessa aqui discutir se este censo
está mal elaborado, e certamente está. Nosso objetivo é considerar um exemplo
160
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
de tabela e ver como é construida para exibir o instrumento da linguagem C
que pode produzir tabelas.
Para que um entrevistador exerça sua função, ele deve ser informado sobre
quais os valores aceitáveis para cada campo. Por exemplo, no campo “expediente” dois valores devem ser dados, tirados de
m, t, n
m de manhã, t de tarde ou n de noite.
Você encontra a definição e exemplo de struct dentro dos programas estruturaX.c
no disco. No diretório do BC, estruX.c
Exercı́cios: 43 tabelas e censos
1. Rode e leia o programa cadast.c
2. Altere o programa cadast.c para registrar os dados de uma turma de
alunos, com os campos nome, nota1,nota2,nota3, média final.
3. Crie uma entrada de dados para o programa e uma saı́da de dados, se não
tiver feito ainda.
Da mesma forma temos, por exemplo, “tempo”. Tempo é uma “estrutura”com os seguintes campos:
dia da semana(0..6) dia do ano(0..365) segundos (0..59)
minutos(0..59) hora(0..23) dia do mes(1..31)
mes(0..11) ano(1900 ...)
Ao lado de cada variável da estrutura tempo, e apenas para informação
do usuário, se encontram os valores que foram planejados. Em geral não é
conveniente criar as variáveis com tais restrições do ponto de vista de seus
valores, embora isto seja possı́vel e crie mais segurança para o programa (e mais
trabalho para o programador que deve criar mecanismos de verificação para os
dados fornecidos ou lidos automaticamente).
De maneira análoga poderiamos ter construido a tabela do censo, indicando
com brevidade, usando códigos, as informações de cada campo. Para profissão,
poderiamos ter usado a codificação da Receita Federal. Para salário poderiamos
ter usado inteiros indicando múltiplos do “salário mı́nimo”.
Feita uma “especificação” deste tipo é possı́vel guardar a informação de
forma compactada. Assim, no caso do tempo, o conjunto de dı́gitos
200007011331290346
representa um determinado instante na seqüência do tempo e poderia ser reescrito, em formato de fácil leitura, para humanos:
7.6. ESTRUTURA, STRUCT.
161
2000.07.01.13.31.29.03.46
ou usando outro qualquer tipo de separador como
2000/07/01 − 13.31.29 − 03.46
porque definimos uma estrutura para caracterizar o tempo e agora com um
programa podemos facilmente passar da expressão apropriada para humanos
para a expressão apropriada para cálculos algébricos no computador e viceversa.
Uma rotina pode então ser construida para fazer uma álgebra de tempo para
somar, subtrair tempos permitindo que um programa possa calcular prazos decorridos, ou detectar se a data para um deteminado evento chegou.
O gcc previu estas duas formas de tratar o tempo, humano e interno para
cálculo com as máquinas. Cabe ao programador construir a tradução adequada.
Da mesma forma um programa de computador pode agir sobre o “censo”
para dele extrair informações qualificadas sobre uma determinada região, como
salário médio, concentração profissional, etc... através de uma “álgebra” adequada para tratar estes dados.
Observação: 33 Codificação e leitura humana
Codificação é assunto para computadores. Programas devem ser escritos
para uma fácil comunicação com humanos que lêm frases, e não códigos.
As traduções, código ⇐⇒ frases podem ser feitas facilmente com funções
em qualquer linguagem de programação, desde que as frases se restrinjam a um
conjunto bem definido de palavras. Inclusive estas frases podem ficar guardadas
em arquivos no computador e serem escolhidas a partir de pedaços digitados pelo
usuário, como acontece nos módulos de pesquisa dos programas na Internet.
Internamente os programas vão trabalhar com os códigos.
Depois desta breve apresentação genérica sobre estrutura de dados24 , vamos nos especializar em alguns casos. Você pode encontrar um mundo de informações a respeito no info, no manual sobre Libc, veja no ı́ndice remissivo
info ou Libc.
Vamos adotar aqui uma terminologia para fazer referência aos dois formatos
sob os quais o gcc processa o tempo.
• tempo para leitura, chamado em inglês de broken time, porque ele se
apresenta em campos, como se encontra descrito a seguir;
• tempo para cálculos, que se apresenta em estado bruto, em segundos,
a partir de um momento que foi definido como epoch, “a época”.
É relativamente fácil passar de um para o outro entre estes dois tipos de
sistemas de processamento de informações relativas ao tempo, no ggc. Se você
24 há
livros inteiros, com mais de 200 páginas sobre o assunto...
162
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
tiver tempo no formato bruto em segundos, tempo para cálculos, contando
desde a época, basta sair sub-dividindo em aglomerados sucessivos de anos e o
resto em meses, e assim assim sucessivamente até saber qual é a data que aquele
número representa.
Por outro lado, um inteiro bruto é fácil de ser somado ou subtraido de outro
permitindo assim uma álgebra do tempo simples. Veja um exemplo:
Exemplo: 13 Tradução e álgebra de tempo
Queremos saber qual o tempo que se passou entre
final; 10 de Dezembro de 1998
e
inicio; 15 de Outubro de 1994.
Traduzimos inicio e final para tempo bruto. Nos parece mais fácil se estabelecermos uma “época” particular, por exemplo, 01 de Janeiro de 1994 e
calcularmos o número de segundos contidos em 15 de Outubro de 1994:
inicio = 24969600
e depois, relativamente á mesma época calcularmos o número de segundo
contidos em 10 de Dezembro de 1998:
f inal = 156297600
A diferença em segundos é:
lapso = f inal − inicio = 131328000
que é o lapso de tempo decorrido em segundos, que agora vamos quebrar, como
dizem os americanos, em anos, meses, dias, minutos e segundos. Se for para o
cálculo dos juros de uma dı́vida, iremos desprezar minutos e segundos.
Para isto criamos novas variáveis, ano, mes para conter o número de segundos em ano comercial e no mes comercial que tem 30 dias, porque esta é regra
legal para o cálculo do tempo decorrido. Depois faremos divisões sucessivas:
anos = lapso/ano = 4.16438356164383561643
que diz se terem passado 4 anos, isto
4 ∗ ano = 126144000
restando portanto
resto = lapso − 4 ∗ ano = 5184000
e finalmente vamos ver quantos meses se passaram. Para isto definiremos a
variavel mes:
mes = 30 ∗ 24 ∗ 60 ∗ 60
7.6. ESTRUTURA, STRUCT.
163
e depois calculamos25
meses = resto/mes = 2
Sendo assim o tempo legal decorrido entre as duas datas de 4 anos e 2 meses.
Exercı́cios: 44 Estrutura e álgebra com o tempo
1. Escreva uma função que, recebendo duas datas diferentes, calcule o lapso
de tempo decorrido entre elas.
2. Melhore a função construida usando uma “época” definida no programa.
3. Faça uma função que calcule os juros devidos para uma certa ’soma’ entre
dois perı́odos dados.
4. Faça um programa para cadastrar os funcionários de uma empresa registrando os dados:
Nome, idade, altura, peso
cada um destes dados numa nova linha no arquivo.
Solução: cadapesX.c.
7.6.1
O tempo para os
humanos lerem
• Para expressar o tempo em forma legı́vel pelos humanos,
• Para fazer cálculos algébricos com o conceito de tempo
Neste seção vamos trabalhar com a escrita do tempo para os humanos lerem
e deixar os cálculos do tempo para a próxima.
Você pode encontrar mais informações técnicas no manual do gcc, veja Libc
no ı́ndice remissivo.
Tipo de dados: struct *tm
Este tipo de dados contém os seguintes campos:
• int tm sec que representa o número de segundos e varia de 0..59.
• int tm min Número de minutos até completar uma hora, variação 0..59.
• int tm hour Número de horas a apartir de meia noite, variação 0..23.
• int tm mday Número dos dias do mes, variação 1..31.
• int tm mon Numeração dos meses do ano, variação 0..11.
• int tm year Numeração dos anos a partir de 1900.
25 observe
que se trata de uma multiplicação para não escrever 2592000, que seria a quantidade segundos, porém, indecifrável...
164
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
• int tm wday Numeração dos dias da semana, domingo=0 (Sunday). Variação 0..6
• int tm yday Numeração dos dias do ano, a partir de 1o de Janeiro, variando
0..365.
• int tm isdst Uma etiqueta (flag) para indicar se está em vigor o horário
de verão. Se estiver em vigor,
1. tm isdst=1, se estiver em vigor;
2. tm isdst=0, se não estiver em vigor;
3. tm isdst=< 0, se a informação não estiver disponı́vel;
• long int tm gmtoff
É o número de segundos que mede o afastamento do horário local (algébrico)
do horário-gmt. Por exemplo, deve ser adicionado -4*60*60 para se corrigir o horário de Brasilia relativamente ao GMT. Pertence a biblioteca
GNU-C e nao existe em um ambiente ISO C.
• const char *tm zone Nome da zona de tempo em uso, também inexistente
no ISO C.
• Função:
struct tm* localtime (const time t *TIME)
Na variável TIME se encontra o endereço onde está armazenado o tempo,
a função ‘localtime’ converte o conteúdo de TIME em sua representação
de tempo para leitura, relativamente a zona de tempo registrada na
máquina.
A função devolve um ponteiro para este valor do tempo que pode ser
utilizado para recuperá-lo e fazer as transformações que se deseje, veja
o programa hora.c. Veja mais detalhes em Libc, ver “info” no ı́ndice
remissivo ao final do livro.
Leia (ou releia) o exemplo 13, página 162. Veja também os programas da
série horaX.c em que você pode encontrar dicas de como automatizar os
cáculos feitos no exemplo citado.
• Função: struct tm * gmtime (const time t *TIME)
Semelhante à função localtime. A diferença é que o tempo se apresenta
no formato UTC26
26 esta sigla representa um acordo entre francófonos e anglófonos na divisão do mundo.... os
franceses dizem Universel temps coordoné e os americanos dizem Universal Time Coordinated.
Ambos aceitam perder um pouquinho da sintaxe nas respectivas lı́nguas,fala-se que os ingleses
queriam medir o tempo em pés.
7.7. FORMATADORES PARA SAÍDA DE DADOS
165
• Função: time t mktime (struct tm *BROKENTIME)
A função mktime é usada para converter o tempo fracionado no tempo do
calendário. Completa tm yday , e tm wday que estejam faltando a partir
dos demais dados, num processo de noramalização. Se não for possı́vel
restaurar o tempo para o formato do calendário, o valor de retorno será
-1. Esta função também dá valor a variável tzname - nome da zona de
horário.
7.6.2
O tempo para o
computador fazer álgebra
O exemplo 13, página 162 se constitue na motivação desta seção.
Você pode encontrar mais informações técnicas no manual do gcc, veja Libc
no ı́ndice remissivo.
Exercı́cios: 45 Estruturas
1. Verifique que o programa texto.c usa indevidamente a tipo de dados
array, transforme-o usando struct que é tipo de dados natural para um
tal programa.
7.7
Formatadores para saı́da de dados
Nesta seção final reunimos as informações sobre como formatar os dados basicamente para
as funções printf(), fprintf(), scanf(), sscanf().
Vamos aqui aplicar o assunto tipo de dados em dois dos momentos mais crı́ticos de um
programa, na saı́da e na entrada de dados.
Dominando o uso destas funções, vovê estará fazendo um tutorial prático sobre tipos de
dados.
Nesta seção vamos estudar mais detalhadamente a sintaxe para a conversão
da dados necessária para que
printf(), fprintf(), scanf(), sscanf()
leia ou escreva os dados de forma agradável, ou no caso de scanf consiga interpretar os dados que lhe forem oferecidos.
Apesar da posição deste capı́tulo no livro cabe aqui uma apresentação explicita destas funções, porque você pode estar aqui de visita, vindo das primeiras
páginas do livro incentivado pela indexação do assunto.
Vocabulário: 7 printf(), fprintf(), scanf(), sscanf()
• printf() Saı́da de dados.
É a função que imprime dados na tela e
que nós traduzimos por imprima() ou escreve(). É bastante complexa
porque admite uma grande quantidade de parâmetros permitindo organizar
de forma perfeita a saida de dados na tela ou em papel.
166
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
• fprintf() Saı́da dados. É a função que imprime dados em um arquivo,
fprintf.
O primeiro “f ” do nome quer dizer isto. É muito semelhante printf(),
mas com diferenças especı́ficos, você vai ter que lhe dizer em que “arquivo”
as coisas devem ser impressas. Também você vai ter que dizer de onde vêm
as coisas. Veja nos programas agend1.c exemplos de uso desta função.
• scanf() Entrada de dados. É a função ultra-poderosa, ultra-simples e
sensı́vel para fazer entrada de dados. Tem uma sintaxe semelhante a de
printf(). Enquanto você não tiver uma boa experiência evite de usar
esta função.
Use sscanf() que é mais burocrática, porém mais segura.
• sscanf() Entrada de dados.
É a função muito poderosa para fazer
entrada de dados. Tem a mesma capacidade de scanf() porém sua sintaxe conduz melhor o programador a evitar os erros. Veja no arquivo
entra dados.c exemplo de uso desta função junto com fgets().
Vamos nos concentrar em printf uma vez que vale o que dissermos também
para fprintf e, em alguma medida, também para scanf. Vamos discutir as
diferenças.
A figura (fig. 7.2) página 166, mostra um exemplo e fixa a terminologia que
vamos usar.
formatadores
printf("Vamos imprimir %d na base oito: %o \n", 30,30);
a "stream"
2
printf("%s %d %s %o \n",
"Vamos imprimir o número",
30,
parâmetros
"na base oito ",
30);
4
Figura 7.2: duas formas equivalentes para imprimir 30 na base 8
7.7. FORMATADORES PARA SAÍDA DE DADOS
167
Dois métodos para formatadar os dados:
• Os formatadores de dados imersos num vetor de caracteres.
O exemplo da (fig. 7.2) mostra que os formatadores de dados podem
estar distribuidos (imersos) dentro de um vetor de caracteres oferecido
a printf que então completará este vetor de caracteres “expandindo”
os correspondentes formatadores de dados na ordem em que eles forem
encontrados. A “máscara se chama em ingês, stream contém os formatadores sendo seguida por uma lista de parâmetros. Ver numero01.c como
exemplo deste método.
• Os formatadores de dados numa mascara que tudo define. Uma outra forma
de fazer, consiste em, escrever todos os formatadores de dados como um
primeiro parâmetro, chamado modelo, em inglês template, e fornecer, separados por vı́rgulas, os correspondentes dados. Ver prog20.c, ao final,
como um exemplo deste método. Leia inclusive a observação (20) ao final.
A formatação de dados da função printf() tem a seguinte estrutura:
%modificadores tamanho [ . precisao] tipo de convers~
ao
Veja os exemplos: nas figuras (fig. 7.3), (fig. 7.4).
f indica
tipo float
;x
3.14159265358979323848
; msg = "O valor de pi é "
; printf("%s %25.15f \n",msg,x)
O valor de pi é
3.141592653589793
; printf("%s %20.15f \n",msg,x)
O valor de pi é 3.141592653589793
25.15
20.15
15.15
25.5
indicam
espaço e
precisão
; printf("%s %15.15f \n",msg,x)
O valor de pi é 3.141592653589793
; printf("%s %25.5f \n",msg,x)
O valor de pi é
3.14159
s indica
vetor de caracteres
Figura 7.3: Formatação de dados em printf()
Leia e rode o programa format1.c.
Da mesma forma como diversos tipos de dados podem ser impressos também
diversos formatadores, com seus espaçamentos especı́ficos podem ser incluı́dos
168
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
# include <stdio.h>
int main()
{
float x=3.141516171788;
char
msg[50]="O numero pi é aprox. ";
printf("%s %25.8f \n", msg,x);
printf("%s %25.18f \n", msg,x);
printf("O número pi é aprox. %5.18lf \n",x);
printf("O número pi é aprox. %lf \n",x);
printf("%s %5.5e \n", msg,x);
printf("O número pi é aprox. %5.5lf \n",x);
return 0;
}
saída de dados
O
O
O
O
O
O
numero
numero
número
número
numero
número
pi
pi
pi
pi
pi
pi
é
é
é
é
é
é
aprox.
3.14151621
aprox.
3.141516208648681641
aprox.
3.14151621
aprox. 3.141516
aprox.
3.14152e+00
aprox. 3.14152
Figura 7.4: Uso de printf()
num vetor de caracteres, apenas observada a ordem com que eles sejam fornecidos. Rode e analise os dois programas citados e assim como os programas
formatoXX.c. Veja também formatadores no ı́ndice remissivo deste livro.
Se você desejar imprimir algum caracter especial, como “%”, em libc você
vai ver como. Em particular “%” será impresso pela linha de comando abaixo:
printf(‘‘O percentual do candidato X é %2.2f%%\n’’);
Exercı́cios: 46 Formatação de dados
1. Leia e rode o programa format1.c
2. Altere o programa format1.c substituindo x = 3.141516171788 por x =
3.14 e analise o resultado.
3. Altere o programa format1.c substituindo %f por %lf e analise o resultado.
Solução: format2.c Veja a observação sobre “aproximação arranjada
pelo compilador”
4. Escreva um programa, format3.c para imprimir o sı́mbolo % depois de
um número real (float).
5. Altere o programa que você acabou de fazer para que a frase anunciando
a inflação do mes esteja na primeira linha e na segunda linha o ı́ndice de
inflação.
Solução: format3.c
7.7. FORMATADORES PARA SAÍDA DE DADOS
169
6. Crie variáveis inteiras e use o “coringa” * para usar um formato variável
para imprimir espaço e precisão de dados. Sugestão: use um loop.
Solução: format4.c
7. Faça um programa que leia, com scanf() um número inteiro, um número
real (float), e imprima estes números em duas linhas distintas com a
mensagem que indicando o tipo de número.
Solução errada em format5.c
8. Conserte o prorama errado format5.c.
Solução: format6.c
Observação: 34 Aproximações arrumadas pelo compilador
O programa format2.c exibe, põe em evidência, erros que o compilador insere nos números.
Rode novamente o programa e o leia novamente também para entender o que dissemos.
Não vamos apresentar uma solução para este problema aqui, ela seria complicada para o
nı́vel do texto, mas obviamente que ela existe. Somente queremos alertá-lo para os erros de
arredondamento que o compilador produz, que precisam ficar sob contrôle.
Exercı́cios: 47 Acesso ao disco - fprintf()
1. Leia o programa cadast.c e registre a sintaxe da função fprintf(), semelhanças e dissemelhanças com printf().
2. Leia e rode o programa cadast.c, use nomes completos, nome e sobrenome
3. Leia o arquivo que você tiver indicado para receber os dados e veja que
os dados foram truncados: foi gravado somente o primeiro nome de cada
funcionário.
4. Troque converte palavra() ( sscanf()) por concatena palavras() (strcat()) e rode novamente o programa. Veja que os nomes foram guardados
completamente.
Solução: cadapes3.c
5. Estude a suite de programas cadapesX.c rodando e lendo sucessivamente
os programas. Faça alterações nos programas para incluir mais dados no
cadastro de funcionários.
6. Use o programa cadapes3.c para construir a sua agenda pessoal de endereços com os campos: nome, endereço, telefone fixo, telefone celular,
(outros dados que lhe parecerem importantes).
Solução: agend1.c
7. O programa agend1.c tem um bloco com o comentário “isto é um teste,
deve ser apagado”. Procure o comentário e veja que a sintaxe de fprintf()
é semelhante a de printf(). Analise a diferença.
170
CAPÍTULO 7. OS TIPOS BÁSICOS DE DADOS
8. A função sprintf() permite a composição automática de vetores de caracteres(dentro de um programa). Ver compos autom.c. Altere agend.c
para verificar se o arquivo já existe e usar um nome diferente.
sprintf(deposito, ‘‘%s %d’’,NOME ARQ,extensao)
Defina convenientemente extensao, dê lhe um valor adequado.
Solução: agend3.c
O manual do gcc alerta que a função sprintf() é perigosa, o próximo
exercı́cio oferece uma alternativa.
9. Refaça a questão anterior usando strcat()
Solução: agend4.c
Vocabulário: 8 sprintf(), fprintf()
Primeiro, deixe-nos lembrá-lo uma fonte de consulta, ajuda, . No sistema
info, digite esta palavra numa shell (em LinuX, obviamente), seguida de libc:
info libc
e você tem um manual da linguagem C. Com a barra \ você faz pesquisa de
palavras e o info vai abrir no pê da área de trabalho uma janela onde você pode
digitar o nome de uma função da linguagem, por exemplo, sprintf. Tente.
Este livro teria 1000 páginas se fossemos explicar tudo que existe. E para
que repetir, se o que existe é excelente. O nosso objetivo é conduı́-lo, pedagogicamente, pelo que já existe.
• sprintf()
int sprintf (char *S, const char *mascara, ...)
Semelhante a printf() mas guarda os dados no vetor de caracteres S. Termina os dados com o caractere NULL. É uma função inteira devolvendo
o número de caracteres colocados em S menos um do NULL. O NULL não
é contado. Ver agend3.c. Não oferece proteção contra gravação de um
vetor de dados que supere o tamanho de S. Usar com cuidado.
• fprintf() É a saı́da de dados padrão para arquivos em disco. Semelhante
a printf() com a diferença de que o primeiro parâmetro é do tipo FILE,
para onde são dirigidos os dados. Ver agend.c
Capı́tulo 8
Matemática em C
Neste capı́tulo, o primeiro parágrafo é um breve guia de referência da linguagem C especı́fico para o assunto do capı́tulo, onde você poderá encontrar
rapidamente informações sobre o uso das ferramentas da linguagem.
Aqui operadores aritméticos e lógicos.
Podemos fazer muita matemática com C e vamos deixar-lhe uma pequena
amostra do que é possı́vel fazer.
Em C podemos fazer programas para determinação de códigos, ou melhor,
para validação de códigos.. Os programas que executam estas tarefas são bastante complicados porque fazem operações de comparação com vetores de caracteres, uma pequena amostra disto se encontra no programa sexto06.c, usando
grep senha *.c
você pode encontrar outros programas nossos que dão exemplos de comparação
entre palavras. Mas códigos são mais do que senhas, e pode ser uma aritmética
muito pesada, não vai ser possı́vel desenvolver tais programas aqui.
Vamos apresentar-lhe uma matemática mais simples:
• como calcular aproximadamente integrais;
• o método para calculo de integrais que vamos usar chama-se somas de
Riemann, não é o melhor nem o mais rápido, tem coisa melhor, mas os
outros partem deste, como se tratam de somas, antes aprenderemos a fazer
somas;
• para fazer somas, precisamos de varreduras, antes aprenderemos a fazer
varreduras que também se encontra no caminho para fazer gráficos.
É o que faremos aqui.
171
172
CAPÍTULO 8. MATEMÁTICA EM C
8.1
Operadores aritméticos e lógicos
Este capı́tulo se dedica à aritmética em C mas é impossı́vel escrever um programa sem
usar operadores lógicos, vamos, portanto, misturar os operadores aritméticos e lógicos neste
contexto.
Aritmética e lógica
Falamos de “operadores” na linguagem C de modo semelhante como se diz em
Matemática, “operação”:
• em Matemática escrevemos “a + b” e dizemos que “a operação de adição
entre os números a, b. Você executou uma “operação” matemática.
• em Computação dizemos que o operador “+ se aplica aos números 3, 4
(3, 4) → 3 + 4;
e escrevemos apenas 3 + 4.
Mas quando você, num programa, executar
x = 3 + 4;
não é uma operação matemática que estará sendo executada e sim
• primeiro a operação matemática 3 + 4, porque as sentenças, em C, são
executadas da direita para a esquerda, como em japonês, e de dentro para
fora, se houver parenteses;
• depois foi executada uma operação interna de registro de dados, guardando
na variável x o valor calculado anteriormente
• Previamente a variável x deve ter sido definida;
• agora o número inteiro 7 está sendo sendo associado à variável x.
Em Matemática, e porque os humanos são inteligentes e conseguem deduzir
a confusão de dados a partir do contexto, confundimos duas operações:
1. Associação de dados, que é feito em computação em C com o sı́mbolo
=
2. Teste lógico, que em Matemática se usa o mesmo sı́mbolo, mas em C se
usa o sı́mbolo ==
8.1. OPERADORES ARITMÉTICOS E LÓGICOS
173
Em Matemática, a expressão
3x + 4 = 0
é uma sentença dita “aberta”, e com isto se quer dizer, podemos testar se um
determinado valor dado à variável x torna esta sentença verdadeira ou falsa.
Em C não teria sentido escrever esta expressão sem primeiro atribuir um valor
a x. Em suma, em C podemos fazer:
x = 10;
(8.1)
(3x + 4 == 0)
(8.2)
para verificar se é verdadeira para o valor 10 dado a x. Observe os parenteses,
eles são um operador da linguagem C, o operador avaliação, eles forçam o cálculo
do seu conteúdo.
Veja o programa logica08.c a este respeito. Verifique isto com “calc”, por
exemplo1 , execute em “calc” as seguintes linhas:
x=3
3*x+4==0
Depois de digitada a última linha, “calc” responderá com “0” porque terá verificado que à direita e à esquerda de == se encontram objetos diferentes:
13 = 3x + 4 6= 0 ⇒ 3x + 4 == 0 é falso.
Em Matemática estas diferenças sutı́s têm que ser determinadas a partir do
contexto pelos humanos. Em C, cujos programas são escritos para as máquinas,
estes detalhes têm que ser cuidadosamente diferenciados.
O exemplo acima mostra que não será possı́vel falarmos de aritmética sem
mencionar a lógica. Embora este capı́tulo esteja dedicado à aritmética, teremos
que mencionar os operadores lógicos.
8.1.1
Uma lista seca de operadores
quase seca...
Em C existem alguns “operadores”, que vamos descrever detalhadamente abaixo
incluindo exemplos. Se você tiver chegado até aqui “linearmente”, muitos deles
já serão seus conhecidos, porque você os encontrou nos programas.
1. ! É operador lógico, “not”. O não lógico;
2. ! = É negação do operador lógico “igual”. O “diferente de”;
3. = não é um operador aritmético, é um “comando da linguagem C algumas
vezes identificado como “atribuição”. Sobre tudo observe que é diferente
de == que aparece mais abaixo.
1 na
ausência do calc, faça um pequeno programa contendo somente estas linhas, reserve
teste.c para isto
174
CAPÍTULO 8. MATEMÁTICA EM C
4. %
O operador “mod”, é a divisão inteira. As duas formas abaixo
são equivalentes’:
r = mod(a, b) ≡ r = a%b;
tem como resultado o resto na divisão de a por b.
int resto(int a, int b)
{
printf("O resto na divisao de %d por %d e’ %d ",a,b,mod(a,b))
return 0;
}
resto(4,6) -> O resto na divisao de 4 por 5 e’ 4.
Há diversas aplicações para esta função, uma delas na validação de códigos,
como CPF em que o “dı́gito” pode ser o resto na divisão do número formado pelos outros algarismos por um quociente escolhido. Assim se pode
verificar se um número apresentado como CPF tem legitimidade ou não. O
quociente, o segundo parâmetro em mod(a,b), deve ser grande, em geral,
para aumentar a segurança, ou diminuir a chance nas tentativas falsas.
5. / A divisão comum se faz com o operador / e ainda há uma divisão
inteira que com o operador //. Assim, o resultado do seguinte teste será 1
6. //
(8//5) ∗ 5 + mod(8, 5) == 8
8//5 calcula o quociente na divisão de 8 por 5 e mod(8,5) calcula o resto
nesta divisão. A expressão do teste é a equação da divisão euclidiana:
divisor ∗ quociente + resto = dividendo.
Se você copiar esta expressão para a folha do calc, ao dar enter terá
como resposta:
1
porque a avaliação lógica entre as duas expressões resultou em verdadeiro.
7. ∗ O operador multiplicação;
Este operador é da multiplicação em C como na maioria das linguagens
de programação.
Como a adição, se parece com a multiplicação da Matemática.
Se parece porque em Matemática, sempre podemos calcular a ∗ b dados
dois números. Em C, depende do limite na memória do computador.
Este seria um dos diferenciadores, C foi projetado para trabalhar com
números inteiros dentro de uma faixa de grandeza. Além desta faixa de
grandeza os resultados voltam a se repetir ciclicamente.
8.1. OPERADORES ARITMÉTICOS E LÓGICOS
175
Por, exemplo, apenas para podermos exemplificar com valores pequenos,
suponhamos que o C com que você estivesse trabalhando se encontrasse
limitado aos inteiros de −9 até 9. Neste caso,
3 ∗ 2 = 6;
3 ∗ 5 = (15 − 9) = −4;
3∗6=0
Porque este seu C faria as contas e usaria o “noves fora” para dar a resposta, mas aceitando resultados entre −9 e 8.
8. + O operador adição;
Este é operador de adição valendo os mesmo comentários que fizemos com
respeito à multiplicação comparada com esta operação na Matemática.
9. ++ É o operador incremento. Fica definido pela seguinte identidade:
(a + +) é idêntico a (a = a + 1)
Duas variantes deste operador: =+, =− que exigem um parâmetro numérico.
• =+ Sintaxe: x=+numero equivale a x = x + numero
• =− Sintaxe: x=−numero equivale a x = x − numero
Observe que se “float x”, C prossegue somando uma unidade a x.
10. − O operador subtração;
Este é operador de subtração valendo os mesmo comentários que fizemos com respeito à multiplicação comparada com esta operação na Matemática.
11. −− O operador decremento;
É semelhante ao ++ para fazer subtrações.
Sintaxe x−− equivale a x = x − 1 Ver =−.
12. − > Operador para acessar um elemento de uma classe ou de uma estrutura;
13. / O operador divisão ;
Este é operador de divisão inteira quer dizer que a/b calcula apenas o
quociente na divisão entre os dois números inteiros a, b. Se um dos números
for do tipo float o operador imediatamente se acomoda ao tipo mais
amplo, float tendo como resultado o número racional ab .
Além disto valem os comentários que fizemos com respeito à multiplicação
comparada com esta operação na Matemática.
176
CAPÍTULO 8. MATEMÁTICA EM C
14. < Operador lógico “menor do que” ;
É um operador lógico, quando C avalia a < b o resultado será 0 ou 1
conforme esta expressão seja falsa ou verdadeira.
15. << Operador “shift” à esquerda ; Você vai precisar deste operador
quando seus programas se tornarem mais sofisticados. Serve para agilizar
operações aritmética e lógicas.
Se você conhece (viu) aquelas antigas máquinas de calcular com manivela
e duas orelhas que permitiam correr o carro para direita ou para esquerda,
então você tem aqui um exemplo computacional... << corre o carro para
esquerda. Por exemplo, 4 ≡ 100 à direita a expressão deste número em
binário. 4 << 1 ≡ 100 << 1 = 1000 ≡ 8 aumenta uma casa “positional”
no número (em seu formato binário).
Exemplos:
3 ≡ 11 ⇒ 3 << 1 ≡ 11 << 1 = 110 ≡ 6
4 ≡ 100
8 ≡ 1000
7 ≡ 111
5 ≡ 101
7 ≡ 111
⇒
⇒
⇒
⇒
⇒
4 << 1 ≡ 100 << 1 = 1000 ≡ 8
8 << 1 ≡ 1000 << 1 = 10000 ≡ 16
7 << 1 ≡ 111 << 1 = 1110 ≡ 14
5 << 2 ≡ 101 << 2 = 10100 ≡ 20
7 << 2 ≡ 111 << 2 = 11100 =
10000 + 1000 + 100 ≡ 16 + 8 + 4 = 28
Nas equações acima estamos usando ≡ para traduzir, nos dois sentidos,
de decimal para binário ou vice-versa.
Veja que em a << b, exatamente, o carro anda b casas “posicionais” para
esquerda, sempre na expressão binária do número a.
Observação: 35 Para que servem as operações binárias
São operações de “baixo nı́vel” e consequentemente muito rápidas. Os
dois operadores “shift” (translação) podem ser utilizados para multiplicar
ou dividir. São usados em cálculo lógicos também uma vez que eles se
aplicam aos caracteres.
16. >> Operador “shift” à direita ; Você vai precisar deste operador quando
seus programas se tornarem mais sofisticados. Serve para agilizar operações
aritmética e lógicas.
Aqui você tem o operador que corre o carro para a direita. Consequentemente ele é destrutivo (o que não acontece com as máquinas de balcão
em que simplesmente o carro não corre além do limite fı́sico).
8.1. OPERADORES ARITMÉTICOS E LÓGICOS
177
a >> b corre o “carro”para direita, o número de casas indicadas pelo
parâcimetro b, até “consumir”a.
Por exemplo, 4 ≡ 100 à direita a expressão deste número em binário.
4 >> 1 ≡ 100 >> 1 = 10 ≡ 2 aumenta uma casa “positional” no número
(em seu formato binário).
Exemplos:
3 ≡ 11 ⇒ 3 >> 1 ≡ 11 >> 1 = 1 ≡ 1
4 ≡ 100 ⇒ 4 >> 1 ≡ 100 >> 1 = 10 ≡ 2
8 ≡ 1000 ⇒ 8 >> 1 ≡ 1000 >> 1 = 100 ≡ 4
7 ≡ 111 ⇒ 7 >> 1 ≡ 111 >> 1 = 11 ≡ 3
5 ≡ 101 ⇒ 5 >> 2 ≡ 101 >> 2 = 1 ≡ 1
7 ≡ 111 ⇒ 7 >> 2 ≡ 111 >> 2 = 1
Nas equações acima estamos usando ≡ para traduzir, nos dois sentidos,
de decimal para binário ou vice-versa.
Veja que em a >> b, exatamente, o carro anda b casas “posicionais” para
esquerda, sempre na expressão binária do número a.
17. <= Operador lógico “menor ou igual a”;
Associa à expressão a <= b um dos valores 0, 1 conforme ela seja verdadeira ou falsa considerando os dados guardados nas variáveis a, b.
Algumas vezes os programadores são tentados a deixar dentro dos programas “chaves” secretas que eles conhecem. É uma forma de garantir que
ninguém venha a substituı́-lo na manutenção de um programa.
Veja esta forma maldosa de atribuir zero a uma variável
a = (5 <= 3);
Como 5 <= 3 será avaliada como falsa, o resultado, depois de calculados
os parênteses, é zero que será atribuido à variável a. Esta linha perdida
entre uma centena de linhas, é uma chave secreta violenta.
Péssimo exemplo, não siga. Mas eventualmente ele pode ser útil, neste
caso coloque comentários dizendo o que está fazendo.
18. == Operador lógico “igual”;
Diferente de =.
a == b serve para testar se a é igual a b.
19. > Operador lógico “maior do que” ;
Releia <= substituindo este por > com a leitura adequada....
178
CAPÍTULO 8. MATEMÁTICA EM C
20. >= Operador lógico “maior ou igual a”;
Releia <= substituindo este por >= com a leitura adequada....
21. ? : Condicional dentro de uma expressão;
De mau estilo, torna o programa ilegı́vel. Veja o segmento de programa:
a = 3;
b = 7;
a > b? imprima(”verdade”):imprima(”falso”);
f also
ou ainda
a > b? imprima("verdade"):imprima("falso");
Equivale a “se() ou entao”, portanto use o “se() ou entao”.
22. ˆ O operador bit-lógico “xor” (ou exclusivo);
23. { } Chaves - para delimitar blocos lógicos de um programa;
24.
| O operador bit-lógico “or” (ou) ;
25. || O operador lógico “or” (ou) ;
26. ∼ O operador bit-lógico “não” (calcula o complemento de todos os bits,
onde estiver 0 coloca 1 e vice versa.) ;
8.2
Equação do segundo grau
Vamos analisar a sucessão de programas prog6101.c ... prog6104.c que resolvem
equações do segundo grau. O programa prog6101.c é programa que foi feito
inicialmente sendo os demais evoluções que ele sofreu. O prog6104.c é o “top
de linha”sendo um programa modularizado e com um lay-out agradável para o
usuário. A recomendação é que você primeiro rode os programas e depois os
estude.
Observação: 36 Compilando programas matemáticos
Se você compilar com a linha de comandos:
gcc -Wall prog6101.c -oprog
o compilador vai emitir uma mensagem de erro dizendo desconhecer a função
pow. Isto acontece porque as funçõe matemáticas implementadas pela Fundaç~
ao
Gnu sofreram uma compilação particular que as torna mais rápidas sendo necessário fazer referência a um tipo particular de acesso à biblioteca matemática:
8.2. EQUAÇÃO DO SEGUNDO GRAU
179
gcc -Wall -lm prog6101.c -oprog
com a instrução de compilação −lm Experimente os dois modos de compilação
para ver o resultado.
Vamos discutir aqui o mais simples dos programas apenas, o primeiro deles.
Um programa que resolva equações do segundo grau deve, sequencialmente,
efetuar as seguintes operações:
• Diálogo inicial Mostrar o formato da equação para estabelecer uma linguagem de comunicação (qual é o coeficiente do termo do segundo grau,
do primeiro grau e do termo independente);
imprima("ax2 + bx + c = 0 \n ");
imprima("Forneca-me os coeficientes -> a, b, c \n");
• Entrada de dados Ler os coeficientes que o usuário irá digitar pelo teclado;
leia(deposito, tamanho do(deposito), entrada pdr);
converte palavra(deposito, "%f", &a);
leia(deposito, tamanho do(deposito), entrada pdr);
converte palavra(deposito, "%f", &b);
leia(deposito, tamanho do(deposito), entrada pdr);
converte palavra(deposito, "%f", &c);
A função
leia(deposito, tamanho do(deposito), entrada pdr);
aguarda que algum dado seja fornecido pelo teclado. O usuário pode
responder com qualquer coisa, mesmo absurda.
A função seguinte
converte\_palavra(deposito, "%f", &a);
toma o conteúdo de deposito e vai lançá-lo na variável a. Eu já tenho
gravado em disco um pequeno arquivo chamado “entra dados” em que
estas duas linhas se encontram gravadas e simplesmente eu as copio para o
programa que estiver fazendo quando quiser fazer uma entrada de dados.
Sempre uso a mesma variável deposito com esta finalidade. Se eu me
esquecer de fazer a declaração palavra deposito[80] o compilador vai
me anunciar este erro dizendo-me que “a função deposito está sendo
definida implicitamente” que significa que ele a desconhece...
• algoritmo Calcular o “delta” para decidir se a equação tem soluções reais
e quantas são; delta = b*b - 4*a*c;
• algoritmo Separar em tres opções os cálculos, dependendo do valor do
“delta” com uma mensagem adequada para cada caso. Veja na figura (fig.
8.1) página 180,
180
CAPÍTULO 8. MATEMÁTICA EM C
se (delta >= 0)
inicio
delta = pow(delta,2);
if (delta == 0)
inicio
raiz1 = −b/2*a;
imprima("Ha uma unica raiz: %f \n", raiz1);
imprima("\n ");
fim
ou\_entao
inicio
raiz1 = (−b − delta)/2*a;
raiz2 = (−b + delta)/2*a;
imprima("As raizes sao \%f e \%f$\backslash$n ", raiz1, raiz2);
fim
fim
ou\_entao
inicio
imprima("A equacao eh impossivel no campo real $\backslash$n ");
fim
Figura 8.1: Equação do segundo grau
O resultado desta análise é o programa prog6101.c. Rode primeiro o programa para ver seu funcionamento, depois leia o programa e verifique que as
etapas acima descritas se encontram executadas.
Exercı́cios: 48 Equação do segundo grau
1. Rode o programa prog6101 e verifique que o código tem um defeito grave,
o usuário fica sem saber o que deve fazer, porque o programa não o informa
de que se encontra à espera de uma entrada de dados. Corrija isto.
Solução prog6102
2. Rode programa prog6101 com uma equação simples (que você conheça as
raizes), verifique que o programa calcula errado. Corrija o programa.
3. Rode o programa prog6102 em que o defeito de comunicação com o usuário
foi corrigido. Mas verifique que o programa continua calculando errado as
raizes. Corrija isto.
Solução: prog6103.c
A solução destes exercı́cios se encontram nos programas
prog6102.c prog6103.c prog6104.c
4. Corrija o “diálogo” na entrada de dados fazendo aparecer uma mensagem
indicando qual o coeficiente que deve ser fornecido. Solução prog6102.c
5. Estude o programa prog6103.c. Observe que nele foram incluidas as
funções
• mascara() logo no inı́nicio.
• obrigado(), apetecof(), copyleft() ao final.
8.2. EQUAÇÃO DO SEGUNDO GRAU
181
Rode o programa e veja o efeito destas funções. Elas estão definidas no arquivo ambiente.h. Altere estas funções (em ambiente) e roda o programa
para analisar os efeitos.
6. Altere as funções obrigado(), apetecof(), copyleft() para atender
os seus interesses.
7. Estude o programa prog6104.c. Ele traz a seguinte inovação sobre
prog6103.c
modulariza as operações. O “lay-out” das comunicações com o usuário
está também melhorado.
8. Complete o programa prog6104.c para que ele resolva equações com soluções
complexas (não) reais. Acrescente um módulo para fazer isto, mas não se
desconcerte se funcionar mal...
Solução prog6105.c
9. Modularize mais o programa prog6105.c separando as rotinas para calcular raizes complexas reais das raizes complexas não reais.
Solução prog6106.c
Observação: 37 Modularização dos programas
Um melhor tı́tulo para este texto seria “nı́vel de abstração nos programas.”
Na suite de programas prog6101.c, ..., prog6106.c não há nenhuma
inovação essencial fora o caso do programa prog6103.c em que todos os defeitos
principais de prog6101.c foram sanados.
Os outros programas, principalmente prog6105.c, prog6106.c são refinamentos dos anteriores.
Uma pergunta se impõe: trabalho necessário ou inútil.
Aqui vamos retomar, brevemente, uma observação feita na introdução destes
livro: “como aprender a programar bem?” Mesmo aqui, perto do fim do livro, é
difı́cil de responder a esta pergunta...
Uma tentativa de resposta viria dentro de uma longa história da computação
que obviamente não faremos aqui, até mesmo porque não somos historiadores.
Mas vamos deixar uma breve resenha que não será muito clara por si própria,
entretanto, de tanto repassar os olhos sobre ela você vai terminar compreendendo
qual o seu sentido.
• Primeiro se “programou” o ENIAC para resolver cada problema individualmente.
• Depois as linguas de programação foram criadas. Com uma linguagem de
programação resolvemos grupos de problemas, sem mexer no computador.
Observe que no item anterior se quer dizer que se alterava a configuração
da máquina, literalmente.
182
CAPÍTULO 8. MATEMÁTICA EM C
• Depois as linguagens de programação evoluiram passando a usar bibliotecas em que várias operações comuns a todos os problemas passaram a
ficar disponı́veis para qualquer programa. Veja o que acontece com o programa prog6103.c, nele fazemos uso das funções contidas na biblioteca
ambiente.h
– mask(),, que identifica todos os nossos programas. Criamos a nossa
marca, a simplesmente a repetimos em todos os programas.
– copyleft(), estabelecemos uma vez por todas a questão da titularidade dos programas.
– obrigado(), pensamos uma vez em como fazer uma despedida, depois
simplesmente a repetimos.
– apeteco(), esta é uma função importantı́ssima, pese sua grande simplicidade. Seu nome significa “APerte uma TEcla para COntinuar” e
de uma vez por todas resolvemos este detalhe em qualquer programa.
E a história ainda continua, nas linguagens de programação montadas durante os anos 90, se aprofundou a capacidade de “abstração” permitindo programas com muito mais alcance. Surgiram as linguagens de programação a objeto
que são módulos mais evoluidos ainda. É o caso de C + +, Python, java, entre
outras menos recentes.
A “modularização” é apenas um pequeno detalhe, um passo inicial, mas é
necessário dominar desde logo este passo.
E repetindo, os módulos de um programa não devem passar do tamanho da
tela porque os insetos adoram se esconder nas laterais...
8.3
Somas e integrais em C
Se você não sabe o que é integral, ou se você tem medo de integrais,
melhor pular esta seção. Se você quiser aprender um pouco sobre
integral, acompanhe o processo.
Vamos estudar uma sucessão de variantes do programa
integral.c. Começaremos com uma versão simples até alcançarmos uma versão mais “sofistificada”. A suite de programas
que estudaremos é:
integral01.c integral02.c integral03.c
8.3.1
Integral de funções univariadas
Sobre integral leia por exemplo [3] onde o assunto está voltado para o cálculo
aproximado de integrais como é o espirito aqui. Para uma visão teórica mais
detalhada veja [4].
8.3. SOMAS E INTEGRAIS EM C
183
A integral de f , simbólicamente:
Zb
f
a
que se lê
integral de f de a até b,
é área da região limitada pelo gráfico de f , pelo eixo OX entre os pontos a, b.
Esta é uma definição elementar que serve a muitos propósitos. Calcular
Zb
f
a
aproximadamente pode ser feito de várias maneiras, por exemplo, calculando as
áreas de retângulos contidos na região acima descrita. Veja gráficos em [4], ou
em qualquer livro de Cálculo.
A idéia do algoritmo está na figura (fig. 8.2) página 183,
precisão
soma = 0;
deltax = 0.001;
x = a;
enquanto (x $<$ b)
{
soma = soma + f(x);
x = x + deltax;
}
soma = soma*deltax;
voltar(soma);
calcula
a soma das
alturas
deltax fica
em evidência
Figura 8.2: Cáculo da integral, aproximadamente.
em que fazemos a variável x “varrer” o intervalo [a, b] com passo deltax. Quando
x for igual a b, ou ultrapassar este valor, então o programa para devolvendo o
valor soma ∗ deltax
Dentro do loop, os valores de f calculados sobre a malha determinada em
[a, b] pelo passo deltax, vão sendo acumulados na variável soma que foi inicializada com zero, soma = 0, fora do loop.
Isto equivale a dizer que somamos todos os valores de f calculados sobre
esta malha. A expressão matemática do loop é:
k=n−1
X
f (xk ).
k=0
e, naturalmente, é melhor que você converse com um matemático (ou me envie
um e-mail...) se tiver alguma dúvida sobre o que está dito aqui, do contrário
este livro se tornaria um livro de Matemática.
Quer dizer que “somas” ou “varreduras” se fazem com loops.
184
CAPÍTULO 8. MATEMÁTICA EM C
Analise o programa integral01.c, depois o rode para ver o resultado e volte
a lê-lo para compreender melhor como ele funciona.
O algoritmo que apresentamos acima, é o loop da função
Riemann()
que calcula Somas de Riemann. Ela está etiquetada com o número (4) no
programa. Veja o comentário “ o algoritmo para calculo da integral”.
A função “principal” se ocupa de todos os detalhes.
• Inicialmente “conversa” com o usuário solicitando o o intervalo de integração:
imprima("inicio do intervalo [a,b] de integracao");
• Se seguem depois tres entradas de dados para receber os valores de
a, b, deltax.
que se encontram “numeradas” com (3), (3.1).
• Finalmente uma única linha de comando contém a mensagem final e chama
Riemann()
imprima(" de
%f \n",Riemann(_inicio,_fim,deltax)); // (4)
A etiqueta (4) marca os pontos em a função Riemann() está definida ou é
chamada.
Defeitos do programa integral01.c
O diálogo inicial com o usuário é pobre. Deveria ser mais longo dizendo qual
o objetivo do programa. As tres entradadas de dados dizem pouco sobre o que
pedem.
Um programa precisa ser mais elegante e conversar melhor com o usuário.
Claro, numa versão posterior deste programa (noutro livro...), podemos incluir
um ambiente gráfico deixando o programa trabalhar no porão e apresentar em
janelinhas as mensagens instruindo o usuário sobre as entradas de dados. Mas
agora já é possı́vel algo melhor do isto, veja os exercı́cios.
A saı́da de dados final está muito lacônica. Ela poderia até mesmo explicar
como o programa foi feito. Quando soubermos fazer gráficos, (próxima secção),
seria o caso do programa ir mostrando o gráfico do que está fazendo.
Exercı́cios: 49 Melhorando integral01.c
1. Melhore a entrada de dados de integral01.c explicando melhor o que o
programa se propõe a fazer com cada um dos números solicitados.
8.4. GRÁFICOS DE FUNÇÕES USANDO C
185
2. Veja em integral02.c que espaçamento colocado entre entre as seções do
programa não interferem com a lógica e podem ser usados para facilitar
sua interpretação. Faça alterações semelhantes para tornar integral01.c
mais legı́vel e mais comunicativo.
3. Construa uma função para fazer a entrada de dados. Solução integral03.c
4. Subdivida o programa em pequenos módulos tornando a função principal
apenas uma listagem de tarefas. Solução integral03.c
5. Melhore a saı́da de dados tornando-a mais verbosa.
8.4
Gráficos de funções usando C
Você vai ver aqui como poderia fazer uma pequeno tutorial para ensinar
gráficos de funções aos seus alunos ou para você mesmo, para seu aprendizado
pessoal.
Nesta seção faremos duas coisas:
• Chamaremos um programa externo
Dentro do espirito do Unix, e LinuX é Unix: para que fazer outro programa se já existe um que funciona bem? e é o caso com gráficos.
Existe um programa, de domı́nio público, chamado GNUPLOT, com
versões para DOS e WINDOWS, inteiramente grátis, que trabalha
muito bem com gráficos.
Vamos aqui aprender a chamar o GNUPLOT de dentro de um programa feito em C para que GNUPLOT faça o gráfico que precisamos.
• Vamos reutilizar um programa
Para fazer o gráfico de uma função f precisamos “varrer” o domı́nio de
f para construir a matriz dos pontos (ai , f (ai )) em que i ∈ {1, . . . n}
sendo n a quantidade de pontos que vamos representar, ou a precisão
com que faremos o grá fico.
Esta idéia é a mesma para calcular integrais usando Somas de Riemann,
logo vamos reutilizar os programas integral01.c ... que fizemos anteriormente.
Este parágrafo está baseado no programa grafun.c e o nosso objetivo é conduzı́lo a entender este programa.
Leia rapidamente grafun.c, inclusive a avaliação do mesmo que pode ser
encontrada no começo: programa primário... há uma lista de coisas que devem
ser feitas para que ele se torne um programa respeitável.
Observe que grafun.c se encontra no “estágio final” onde a sucessão de
programas
grafun01.c grafun02.c grafun03.c
o vai levar, logo ele não deve ser fácil de ser entendido. Não se desespere,
entretanto... Vamos começar com grafun01.c, que se encontra no “estágio
inicial”.
É sempre assim, não tenha dúvida, que começa a vida de um programa.
Primeiro construimos um que funcione, depois passamos a melhorá-lo. Melhor
186
CAPÍTULO 8. MATEMÁTICA EM C
uma galinha2 na panela do que duas no terreiro.
Rode o programa grafun01.c e depois o leia.
gcc -Wall -lm grafun01.c -o grafun
e rode o executável que foi criado:
grafun
ou possivelmente, dependendo da organização de sua máquina, (se a tiver recebido a mensagem de que o programa não existe)
./grafun.
8.4.1
Comentando o programa grafun01.c
Vamos iniciar o comentário pelo centro (a parte mais importante) do programa.
O resto é periférico... 3
O centro do programa é:
dados = fopen("dados","w"); // (5.1)
x = _inicio;
enquanto (x <= _fim)
inicio
fprintf(dados, "%f %c %f\n",x,’ ’,funcao(x));
fprintf(dados, "%f %c %f\n",x,’ ’,0.);
x = x + delta;
fim
fclose(dados); // (5.2)
Isto porque estamos usando um método tı́pico do Unix. Se tem um programa
que faz gráficos, eu não vou perder tempo inventando um meio de fazer gráficos,
eu vou usar este programa: Gnuplot.
Gnuplot faz gráficos de distintas maneiras, numa delas ele precisa ler um
arquivo contendo os pares de pontos (x, f (x)) para fazer o gráfico da função f.
Rode grafun01.c, depois edite o arquivo “dados” para ver o que foi escrito
alı́.
grafun01.c abre o arquivo “dados” e nele escreve os pares de pontos
(x, f (x)), (x, 0).
Depois o programa Gnuplot vai ler este arquivo e criar o gráfico.
Mas para fazer isto é preciso definir as variáveis:
• x;
• inicio;
• fim;
2 pensamento
3 você
imediatista...as galinhas no terreiro põem ovos!
não deve levar a sério os comentários do autor...
8.4. GRÁFICOS DE FUNÇÕES USANDO C
187
• dados;
• funcao();
Vamos ao começo do programa. Depois dos comentários iniciais, o programa
começa chamando as bibliotecas necessárias onde estão definidas as funções que
o programa precisa e inclusive nossas traduções.
Depois definimos duas funções auxiliares:
inteira
real
rotulo();
funcao(real x); // (3)
equacao cuja grafico se deseja
Em rotulo() uma marca simples para o programa dizendo o que ele faz. Em
funcao() definimos a equação da função cujo gráfico desejamos.
Definimos as variaveis necessárias ao programa:
• palavra deposito[60], titulo[80];
• real inicio, fim, x, delta;
• ARQUIVO *dados; // (5.1) dados para o Gnuplot com (x,funcao(x))
• ARQUIVO *transfere; // (5.3) linhas de comando do Gnuplot
• palavra programa[18]=”gnuplot transfere”; // (6) programa externo
Leia grafun01.c vamos apenas observar as linhas:
deposito[strlen(deposito)-1]=’\0’;
copia_de_palavra(titulo,"’");
concatena_palavras(titulo,deposito);
concatena_palavras(titulo,"’");
// (7.3)
// (7.4)
// (7.5)
• Em (7.3) estamos cortando a variável depósito para ficar com o tamanho
exato da expressão do tı́tulo que o usuário tiver escolhido. Colocando o
NULL na última posição vaga.
• Em (7.4) estamos colocando aspas simples, necessário para Gnuplot que
exige que o texto que ele receba fique entre aspas simples ou duplas.
• Em (7.5) estamos fechando as aspas.
finalmente chegou o momento de lançar os dados em “dados” como dissemos
acima. Leia o arquivo “dados”. Depois abrimos o arquivo “transfere”
// **** redireciona output para ’transfere’
transfere = fopen("transfere","w");
// (5.3)
fprintf(transfere, "set title %s \n",titulo);
fprintf(transfere, "set pointsize %f \n", 0.1);
188
CAPÍTULO 8. MATEMÁTICA EM C
fprintf(transfere, "set size %f ,%f \n", 1. , 0.7);
fprintf(transfere, "%s %s %s \n","plot","’dados’"," with points");
fprintf(transfere, "%s \n", "pause -2
");
fclose(transfere);
// fechando o arquivo transfere
escrevendo nele todos os comandos que Gnuplot precisa para fazer o gráfico. A
última função fecha “transfere”: fclose(transfere). Leia o arquivo depois
de rodar o programa para ver o que foi nele escrito, e volte a ler grafun01.c.
Não sei se você vai acreditar, mas eu penei um bocado para aprender a
escrever corretamente os dados em disco. Não espere entender isto de um tapa...
Rode grafun01.c, leia os arquivos dados e transfere, volte a ler e rodar
grafun01.c, e assim você irá entendendo os comandos.
O programa grafun01.c termina chamando Gnuplot através da variável
“programa”:
imprima("*** chama um processo externo, Gnuplot *** ");
system(programa);
// (6.1) (6.2) chama programa {\tt Gnuplot}
Faça a seguinte experiência que o vai ajudar a entender todo o teatro que
acabamos de montar. Execute numa shell do Linux, dentro do diretório onde
você está rodando os programas o comando:
gnuplot transfere
para que você volte a ver o gráfico.
Exercı́cios: 50 Entendendo e modificando grafun01.c
1. Compile grafun01.c! tem tanto erro que é melhor passar para grafun02.c.
Fica como desafio, para quando você adquirir mais experiência, corrigir
este programa. É um bom exercı́cio.
2. Melhor talvez abandonar grafun01.c, tem muito erro. Leia e rode grafun02.c.
O erro está indicado nas primeiras linhas.
3. Apague4 as bibliotecas definidas no inı́cio do programa
#
#
#
#
include
include
include
include
<stdio.h>
// (1) **** leitura da biblioteca stdio.h
<stdlib.h>
<string.h>
"traducao.h" // (2) **** leitura da biblioteca traducao.h
compile o programa e analise os erros (ufa... que quantidade enorme de
erros) apague as bibliotecas uma por uma...
4. Como você já deve ter rodado grafun02.c, vamos entender como funciona
Gnuplot. Rode:
gnuplot transfere
e veja que o resultado é o mesmo que rodar grafun02.c.
4 não
obedeça! em vez de apagar, comente...
8.4. GRÁFICOS DE FUNÇÕES USANDO C
189
5. Edite os arquivos “dados”, “transfere”. Eles foram criados por grafun02.c.
Também grafun02.c chamou Gnuplot e lhe passou os comandos contidos
em “transfere”
system(programa);
// (6.1) (6.2) chama programa {\tt Gnuplot}
Altere esta linha em grafun01.c para
system("gnuplot transfere");
e volte a compilar. Haverá um erro, porque a variável “programa” deixou
de ser usada. Corrija este erro apagando a definição desta variável.
6. Defina mais duas outras funções e faça Gnuplot colocar os seus gráficos
na tela.
Solução grafun02.c
7. Defina uma “biblioteca” de funções, veja menu funcao.h para que o programa nela vá buscar a equação que lhe interessa.
Solução estude grafun03.c
8. Altere grafun03.c para que lhe seja permitido, primeiro, ver o arquivo
menu funcao.h de modo que, quando você digitar o tı́tulo possa escrever
a equação da função. Observe que em LinuX você pode facilmente memorizar a equação da função com o rato e colar na tela de execução do
programa.
Solução: grafun04.c
9. Modularize o programa grafun04.c de formas que a função principal()
seja apenas uma listagem de tarefas definidas pelos módulos (funções)
Solução: grafun05.c
O programa grafun.c é a versão final desta série de estudos.
190
CAPÍTULO 8. MATEMÁTICA EM C
Capı́tulo 9
Programação avançada
Se você chegou até aqui fazendo todos os exercı́cios que encontrou no caminho
então está em condições de dar o salto final que este capı́tulo lhe vai propor.
Esta lição é uma espécie de diploma junto com um convite para continuar...
afinal, todo diplomado deve continuar estudando.
Vamos construir dois programas:
• O menu de opções de um programa para fazer gráficos de figuras
geométricas (esperamos que você o termine e nos envie uma cópia),
• Um programa para fazer aritmética com números complexos.
Os dois programas ficarão imcompletos, mas, já fizemos isto nos capı́tulos
anteriores para envolvê-lo diretamente no trabalho. Vamos deixá-lo a um
passo de prosseguir para programação avançada que deve ser feita com C++.
Não se esqueça, C++ contém C.
9.1
Continuar se aprofundando em C
Dissemos que que continuar se aprofundando em C certamente seria estudar
C + +. Vamos discutir isto aqui. Observe que qualquer livro sobre C + +,
tem pelo menos uma 200 páginas, portanto seria uma desonestidade querer lhe
dizer que ao final deste capı́tulo você saberá programar nesta nova linguagem.
Apenas esperamos lhe mostrar que vale a pena considerá-la.
A linguagem de programação C + +, é, em tese, uma outra linguagem de
programação, apenas ela entende C ou ainda, com mais precisão, C + + extende
C, contendo todos os comandos de C e além disto tem novas estruturas de
programação.
Os exercı́cios abaixo vão explorar estes fatos.
Exercı́cios: 51 C + + é uma extensão C
1. Execute na linha de comandos:
c++ -Wall -oprog1 integral.c
e rode prog1. Este é o executável criado com o compilador do C + + a
partir do código integral.c que é um programa escrito em C.
191
192
CAPÍTULO 9. PROGRAMAÇÃO AVANÇADA
2. Execute na linha de comandos:
gcc -Wall -oprog2 integral.c
e rode prog2. Este é o executável criado com o compilador do gcc a partir
do código integral.c.
3. Execute na linha de comandos:
c++ -Wall -oprog3 integral.cc
e rode prog3. Este é o executável criado com o compilador do C + + a
partir do código integral.cc que é um programa “escrito” em C + +.
4. Executando (em Linux)
ls -la prog* | more
você vai poder verificar que
prog2 < prog1 < prog3
em que “prog2 < prog1” significa que o código do prog2 é menor do
que o código do prog1, e procure encontrar uma justificativa para este
resultado.
5. Leia o programa integral.cc e veja quais são as notáveis diferenças entre
um programa em C + + ou em C.
O último exercı́cio sugere que a substituição de printf() por cout transformou um programa em C num programa em C + + o que seria uma grande
simplificação.
Na verdade o que se costuma fazer é criar pequenas funções em C para usá-las
em programas escritos em C + +.
Na última parte deste capı́tulo você vai ver dois exemplos de como isto se
faz.
A justificativa para os tamanhos dos programas é a seguinte: prog2 é o
menor porque, naturalmente, é um programa em C e o compilador gcc está
otimizado para compilar os programas nesta linguagem.
Consequentemente prog1 ficou um pouco maior uma vez que o compilador
do C + + foi chamado para compilar um programa escrito em C.
Finalmente usamos o compilador do C + + para compilar uma mistura e o
resultado foi um executável um pouco maior, mas que também pode ser executado.
Lição: evite as misturas...
9.1.1
A linguagem C + +
Você talvez já tenha ouvido falar de programação orientada a objeto. Esta é uma
caracterı́stica fundamental de C + + e de algumas linguagens de programação
como
• Python,
• Java.
9.1. CONTINUAR SE APROFUNDANDO EM C
193
Estas são algumas das linguagens de programação voltadas para programação
orientada a objetos. Existem dezenas delas.
C + + é uma linguagem bastante popular mas que perdeu um pouco de
terreno para Java que é uma linguagem mais simples e sob alguns aspectos mais
segura que C + +. Entretanto C + + é extremamente rápida, de 20 a 30 vezes
mais rápida que Java segundo a maioria dos analistas.
Tres pontos levam Java a se sobrepor a C + +,
• simplicidade,
• segurança,
• e o poder econômico de uma grande firma.
A simplicidade tem aspectos negativos, e a consequêcia disto é que Java
está crescendo em complexidade de modo que este aspecto (positivo) tende a
desaparecer.
A segurança dentro de C++ pode ser alcançada se você aprender a programar
bem. Lembre-se do que já dissemos, repetindo o que dizem outros autores:
um programa mal escrito pode ser tão ruim que é melhor você re-aprender a
programar e fazer outro programa em vez de tentar corrigir o programa. Se
você aprender a programar bem, a segurança em C + + pode ser facilmente
atingida e seus programas serão rápidos e confiáveis como há muitos rodando
por aı́.
O poder da grande firma aos poucos vai se diluindo frente a beleza, a coerência
do software livre e de domı́nio público de onde vêm C + + e Python. Embora o
Java também já tenha sido tomado pelo software livre... não é a tôa que uma
grande multinacional chama o software livre de cancer, porque vai terminar por
comê-la... é, a liberdade é um cancer, para o poder.
Mas, é claro, se você optar por Java, não estará mal servido e o que aprender
aqui sobre C + + lhe vai ser útil para programar bem em Java.
Seguindo o esquema que nos guiou até aqui, construiremos um exemplo.
Vamos retomar o programa menu.c e construir, a partir dele, um novo programa
menu.cc. Nesta construção, e nos comentários, lhe mostraremos um pouco de
C + + e da leveza que é programar orientado a objeto.
Existe uma dificuldade inicial, devido a abstração deste novo estilo de programação, mas vamos lhe mostrar que vale a pena o salto.
9.1.2
Programação orientada a objeto
Primeiro uma advertência: leia esta seção sem levá-la muito a sério, rapidamente. Passe logo para a seguinte em que vamos construir a prática. Depois de
ler a próxima seção volte para uma leitura mais detalhada desta, e verá que ela
ficou mais clara e, inclusive, lhe explicou melhor o que aconteceu na próxima.
Observação: 38 Abstração em programação
194
CAPÍTULO 9. PROGRAMAÇÃO AVANÇADA
A palavra abstração é usada com o seu sentido literário exato, consiste em
produzir não exatamente programas mas modelos de programaç~
ao. Volte posteriormente para ler este texto, quando tiver lido e rodado o primeiro programa
orientado a objeto.
Há muitas “rotinas”que se repetem dentro de uma “tarefa”, então a idéia de
programar orientado a objeto consiste em captar tais rotinas e os seus objetivos
e colocar tudo isto numa única cápsula que pode ser chamada de qualquer lugar
de dentro da “tarefa”.
A palavra encapsular faz parte do jargão de programação orientada a objeto
e significa isto, construir o modelo constituido de variáveis, agora chamadas
de objetos, e as funções que as alteram, agora chamadas de métodos .
Em C + + encapsulamos os objetos e os métodos em classes que são os
modelos que serão depois copiados, como a natureza faz com as moléculas de
DNA...
Leia os exemplos e depois volte a ler este texto.
Programar orientado a objeto exige algum aprofundamento na concepção do
que é um programa. Os programas ficam mais abstratos no sentido de que, em
vez de fazermos um programa, criamos uma classe de programas. Nosso comportamento nos capı́tulos anteriores conduziu a isto aos poucos quando insistentemente falamos de modularização dos programas. Um programa orientado
a objeto em qualquer linguagem tem a seguinte estrutura geral:
# include sistema.h (1)
class sistema programa; (2)
int main() (3)
{
programa.init() (4)
programa.abre(programa.dados) (5)
programa.leia(programa.coisa) (5)
programa.faça(programa.coisa) (5)
programa.escreva(programa.coisa) (5)
programa.fecha(programa.dados) (5)
return 0; (6)
}
Explicações
1. #include sistema.h é o método em C para incluir a leitura de bibliotecas. Aqui estamos supondo que no arquivo sistema.h esteja a classe que
precisamos.
Esta linha traz para a memória todo o conteúdo da “classe” mencionada.
Na classe sistema estão definidas todas as funções necessárias para comunicação com o sistema e os tipos de dados relacionados com os periféricos.
Por exemplo, cout é um método definido na biblioteca iostream.h que se
ocupa da saı́da de dados. Lá também está definida cin, que é um método
para leitura de dados.
9.1. CONTINUAR SE APROFUNDANDO EM C
195
Na linguagem de orientação a objetos as funções agora recebem o nome de
métodos. Quando você constrói uma classe, que é o protótipo de um objeto,
você também define os métodos que devem alterar as variáveis desta classe.
Então cout, cin são dois métodos definidos em iostream.h.
O sistema fica assim bastante protegido e claramente concebido. Tudo de
um determinado objeto fica junto com ele, o que torna fácil a manutenção
e a reutilização.
2. class sistema programa
é equivalente a uma definição de tipo de dado, aqui muito mais poderosa
porque cria um objeto a imagem da classe sistema, quer dizer, tendo
todos métodos e variáveis definidos no protótipo sistema. É o que se
chama tecnicamente de herança.
programa herda as propriedades de sistema. .
Usa-se também a expressão: programa é uma instância de sistema. Instância é um sinônimo de exemplo ou de representação.
Nem tudo de sistema será herdado por programa. O código pode estabelecer restrições declarando partes que sejam públicas ou privadas. Isto
é um detalhe a ser visto mais a frente. Então programa herda tudo que
for declarado como público da classe sistema
3. Como em C sempre há uma funçãoprincipal.
4. O método init(), abreviado do inglês, intitialization tem a função
de criar as variáveis que vão ser herdadas com valores apropriados. Da
mesma forma como se “inicializam variáveis, há também métodos para
destruı́-las, que não vamos discutir aqui.
Em geral programa.init() é o primeiro item de main() e que serve para
inicializar as variáveis herdadas. Isto em geral é muito importante, mas
pode não ser necessário em um determinado programa.
5. Aqui estamos supondo que em sistema foram definidos os métodos
abre(), leia(), escreva(). fecha()
e agora programa herdou estes métodos. A forma como eles devem ser
usados é com o operador “ponto”que hoje é universalmente usado
• em redações de textos técnicos. As rubricas dos planejamentos indicam a precedência de rubricas com sufixos indicados pelo “ponto”;
• na definição dos nós da Internet;
Assim programa.abre() é um método herdado por programa definido em
sistema.
O método abre definido em sistema está possivelmente associado a um
arquivo em disco e agora este arquivo se torna a entrada ou a saı́da padrão.
Ao final ele é fechado sem erro de fechar outro arquivo que não interessava.
196
CAPÍTULO 9. PROGRAMAÇÃO AVANÇADA
Pode haver até vários arquivos abertos sendo manipulados por outros ramos do programa num sistema multi-tarefa em uso em vários computadores, tudo sob controle, será fechado quando terminar de ser utilizado pela
inst^
ancia de sistema que foi chamada.
programa.leia(programa.coisa)
quer dizer que existe um método em sistema com o nome leia() e que
vai ser aplicado a uma variável que em sistema tem o identificador coisa.
6. E, como é imperativo, toda função termina com um return, e “sempre”main() produz um inteiro ao final de suas operaç~
oes.
Usamos verbos e frases que não pertencem a nenhuma linguagem de programação, mas que se parecem com C, que traduzem o que acontece em qualquer uma delas. Quando você ver um programa em C + + irá logo reconhecer o
que fizemos acima.
Veja as vantangens desta complicação...
• Os “métodos” definidos em sistema se ocupam da segurança e dos detalhes.
• Quando você quiser atualizar um sistema, tudo deve ser feito na classe
sistema. Se ela for bem feita o restante simplesmente herda as modificações sem grandes traumas, algumas vezes sem que seja necessário mexer em coisa alguma dos programas que herdam métodos e objetos das
classes. Outras vezes pequenas modificações são necessárias.
Vou lhe dar um exemplo errado (que novidade)!
Observe a função limpa janela(), definda em ambiente.h. Depende de
que ambiente.h você vai ler... se for no diretório do DOS ela usa uma
função apropriada para este sistema. Se for em Linux, vai usar outra.
Alteramos apenas a biblioteca ambiente.h e todos os programas, usando
limpa janela() funcionam, podem ser usados nos dois sistemas.
Por que o exemplo está errado ? porque ambiente.h não é uma classe !
Mas a idéia é a mesma. Basta transformar ambiente.h em uma classe e
o exemplo fica perfeito.
• Compare o código acima com o conteúdo de menu.cc e com o código de
menu.c e se convença que menu.cc é mais poderoso. Com o tempo você
vai se convencer que também é mais simples.
• Aparentemente uma complicação nova apareceu, aumentou a quantidade
de texto para ser digitada: programa.qualqueroutracoisa(). Sempre
temos que digitar “programa”. Isto não representa problema: simplesmente não digite “programa”. Escreva sua ideia. Quando compilar, o
compilador vai lhe dizer que qualqueroutracoisa() não existe... Ai você
digita um “sinal qualquer” antes de todos os métodos e variáveis e faz
uma troca automática deste sinal por “programa.”.
Você evitou de digitar “programa.”!
9.2. O PROGRAMA MENU.CC
197
• Finalmente, quando for executado
programa.fecha(programa.dados)
não haverá mais nada zanzando pelos sistema ligado ao disco e e nem ao
arquivo “dados”. Segurança total.
Se você estiver achando este texto difı́cil, muito abstrato, lembre-se da observação feito ao começo. Era para fazer uma leitura rápida e passar às seções
seguintes em que exemplos vão ser construı́dos, e depois voltar para uma segunda leitura.
9.2
O programa menu.cc
Vamos reproduzir a idéia do programa menu.c que é um programa (incompleto, verdade!) escrito em C com os novos conceitos de programação orientada a objetos.
9.2.1
Construção da idéia
Leia o programa menu.c e rode este programa para ver onde queremos chegar.
Primeiro rode e depois leia.
Primeiro um pouco de propaganda. Leia os dois programas
• menu.cc e
• menu.c
e vamos compará-los sem entrar nos detalhes técnicos da comparação.
• Veja que a função executa() nos dois programas é a mesma.
• Veja que a função main() em um dos programas é mais simples. É quase
uma lista de frases que dizem o que vão fazer. Costumo chamar esta
função main() de script contrariando um pouco o linguajar técnico de
computação em que esta palavra tem um significado especial, aqui usamos
no espirito com que se usa, no do teatro:
main() em menu.cc é um script que chama os atores, na ordem certa,
para executarem o espetáculo. Os atores já sabem o que fazer.
Ainda dentro do espirito de propaganda, no bom sentido, (se é que propaganda pode ter um sentido bom...), compare os programas
menu.cc e menu mil.cc.
Obviamente, veja o nome, menu mil.cc é uma1 versão muito melhorada do
anterior. Rode primeiro para se convencer deste detalhe:
c++ -Wall -oprog menu.cc
e rode ./prog.
Depois
1 mil
é abreviação de milenium...
198
CAPÍTULO 9. PROGRAMAÇÃO AVANÇADA
c++ -Wall -oprog menu mil.cc
e rode ./prog.
Agora leia os dois programas, menu.cc e menu mil.cc.
A classe que está definida no arquivo ambi.h, que é a versão para C + + de
da “velha” ambiente.h.
Foi fácil melhorar o programa menu.cc e dar-lhe a cara de menu mil.cc,
apenas incluindo a bilioteca ambi.h onde estão definidas as funções
limpa janela(), apetecof(), copyleft()
como ambi.h é uma classe pode ser utilizada com as modificações descritas
anteriormente, usando o “ponto” para caracterizar a origem dos métodos e neste
caso chamariamos
amb.limpa janela(), amb.apetecof(), amb.copyleft()
em que amb seria uma inst^
ancia da classe ambi.h.
Vamos aos passos na transformação de menu.c, em menu.cc usando a classe
informacao. Leia o arquivo ambi.h em que a classe está gravada.
• Observe a estrutura de uma classe, não entre nos detalhes agora.
– Começa com a palavra chave
class
seguida do nome da classe
informacao.
– A palavra-chave “public:”, terminada com dois pontos, marca uma
área em que estão definidas variáveis e métodos que poderão ser utilizados por qualquer instância (exemplo) da classe, os objetos.
– Segue a lista do que é público e depois poderia haver uma lista de
variáveis ou métodos privativos da classe numa área etiquetada com
a palavra-chave “private:”.
– Observe que tudo isto está entre chaves e ao final um ponto e vı́rgula.
Nesta área os métodos se encontram apenas anunciados, a implementação vem depois.
– A implmentação dos métodos vem logo abaixo, cada método iniciado
com a palavra-chave inline. Veja a sintaxe:
inline void informacao::init()
que copiamos do método init() e que podemos repetir de forma
mais abstrata (geral):
inline tipo informacao::nome()
em que informacao é o nome da classe.
Agora é usar a classe como em menu mil.
9.3. NÚMEROS COMPLEXOS
9.3
199
Números complexos
Vamos mostrar como criar uma classe chamada complexo que
define um número complexo e depois fazer uso desta classe num
programa.
9.3.1
Que é número complexo
Os números complexos são aqueles números que aparecem com as equações do
segundo grau quando o delta for negativo, as equações que não tem raizes reais.
Isto dá origem a números do tipo
z = 3 + 2i = a + bi;
<(z) = 3 = a;
=(z) = 2 = b;
(9.1)
(9.2)
(9.3)
Ou seja, um número complexo é um par de números reais (a, b) ≡ a + bi.
Quando somamos dois números complexos, usamos a regra da álgebra “soma
de termos semelhantes”. Poristo somamos parte real com parte real e parte
imaginária com parte imaginária.
Quando multiplicamos dois números complexos, usamos também a mesma
regra da álgebra “soma de termos semelhantes”, mas antes, multiplicamos todos
os termos entre si (usando a propriedade distributiva), depois somamos o que
for semelhante.
Veja na figura (fig. 9.1) página 200,
Para criar esta classe de objetos temos que ter “pares ordenados” e pelo
menos os seis métodos:
• Método para extrair a parte real
• Método para extrair a parte imaginária
• Método da soma
• Método do produto
• Método do produto por um escalar
• Método para calcular o módulo
Em vez de diretamente criar uma classe, vamos primeiro fazer um programa
em C que execute estas tarefas todas, depois vamos transformá-lo em classe de
modo que nunca mais precisemos que repetir a tarefa inicial. Quando precisarmos de números complexos faremos uma herança.
200
CAPÍTULO 9. PROGRAMAÇÃO AVANÇADA
parte
imaginária
a
+
bi
c
+
di
X
−−−−−−−−−−−−−−−−
ac−bd + (ad + bc)i
parte
real
Figura 9.1: O produto de números complexos: parte imaginária se obtem em cruz
9.3.2
O programa em C
O programa em C é o trabalho experimental em que se baseia a construção da
classe, e aqui está uma das razões pelas quais você deve aprender C...
A lista de exercı́cios seguinte o conduz a construção do programa complexos01.c.
Quer dizer que este programa contém as soluções dos exercı́cios.
Exercı́cios: 52 Construção da álgebra dos complexos
1. Faça um programa que entenda o que é um número complexo e possa
explicitar sua parte real e sua parte imaginária. Solução complex01.c
2. Extenda o programa complex01.c para que ele faça a soma de dois números
complexos. Solução complex02.c
3. Construa um programa que calcule a soma e o produto de dois números
complexos. Solução complex03.c
4. Transfira todas as funções de complex03.c, exceto main() para a biblioteca complex.h, coloque a diretiva de compilação adequada para leitura
desta biblioteca e roda o programa. Solução complex04.c, complex04.h
5. Complete a biblioteca complex.h para que sejam feitas as operações de
multiplicação por um escalar e cálculo do módulo de um número complexo. Altere a função entrada() e roda o programa. Solução complex.c,
complex.h
6. Verifique que os programas complexos01.c e complexos.c são apenas
versões de complex.c sem uso da bibliteca complex.h. Tome uma posição
sobre qual é o melhor dos métodos.
9.3. NÚMEROS COMPLEXOS
9.3.3
201
Construção de complexo milenium plus.cc
Neste momento não precisamos mais buscar um longo atalho para chegar no
nosso objetivo. Talvez já tenha lido umas duas vezes a seção inicial deste
capı́tulo, você já deve ter compreendido claramente que, programar orientado a
objeto consiste em criar
• o objeto um conjunto de dados, ou de tipos de dados, que descrevem o
objeto;
• os métodos um conjunto de funções que, métodos, que manipulam o objeto
ou partes do objeto
encapsulados numa classe que poderá ser re-utilizada em diversas instâncias.
O que faremos agora é simplesmente transformar a biblioteca complex.h em
complexO.h e o programa complex.c em complexO.cc.
Leia os dois árquivos complexO.h, complexO.cc, e depois rode
c++ -Wall -oprog complexO.cc
./prog
para ver que o efeito é exatamente o mesmo que
gcc -Wall -oprog complexO.cc
./prog
mas que agora você pode reaproveitar o conteúdo da estrutura Complexo
com mais facilidade. Os exercı́cios lhe mostrarão como fazê-lo.
Exercı́cios: 53 Números complexos orientados a objeto
Construção de uma classe derivada
1. Quando definimos os números complexos em complexO.h, nos esquecemos
de definir módulo. Defina uma nova classe, algebra, que herde o contúdo
de Complexo e tenha o método val abs comp() para calcular o módulo dos
complexos. Solução algebra.h
2. Altere o menu do programa complexO.cc para nele incluir a nova possibilidade do cálculo de módulos de números complexos, redija um novo
arquivo, não altere o existente. Solução complexo milenium.cc.
3. Sem solução neste livro... Construa uma classe menu retirando de
complexo milenium
todas as funções de maneiras que fique apenas a main(). Fica como desafio
para o leitor, construir
complexo milenium platinum.cc
a versão definitiva.
Neste momento você pode ver a construção de algebra.h como uma correção
feita à classe complexo definida em complexO.h.
Mas não é bem assim o nosso objetivo. Queremos que veja que você pode
ter classes com menos propriedades, mais gerais, e com elas construir classes derivadas que tem mais propriedades. Se convença: quanto maior for o
202
CAPÍTULO 9. PROGRAMAÇÃO AVANÇADA
número de propriedades de uma classe, menos probabilidade vai haver para sua
re-utilização.
O desafio colocado como último exercı́cio, no bloco anterior, mostra como podemos chegar a uma situação limite: o programa complexo milenium plainum.cc
definitivo que nunca será alterado. Todas as alterações serão feitas apenas em
classes especı́ficas:
• Novas propriedades dos números complexos, serão incluı́das na classe complexo (observe que a classe algebra é desnecessária, ela foi construı́da
apenas para dar um exemplo de classe derivada). As alterações, as novas propriedades que queiramos incluir nos números complexo, podem ser
feitas na classe complexo.
• Alterações no menu comentários, enfim, comunicação com o usuário, serão
feitas na classe menu
• Obviamente o trabalho fica departamentalizado, mas é bom não esquecer que toda alteração na classe complexo implica em possı́vel alteração
na classe menu, por exemplo, o acréscimo da possibilidade do cálculo do
módulo de números complexo nos obrigou a alterar o menu.
complexo gold Claro, quando precisarmos vender a próxima versão super
melhorada do nosso produto, ainda temos referências como gold e plus para
garantir ao cliente que vale a pena investir no novı́ssimo produto... Pensamos na
próxima edição deste livro lançar complexo gold XGL em que o desafio estará
resolvido. Aguarde...
Capı́tulo 10
Manual introdutório de
referência
Na internet você pode encontrar um mundo de informações oferecidas, com
frequüência, “gratuitamente”. Por exemplo
• Software http://www.gnu.org/graphics/agnuhead.html Este é o site
da Fundação para Software Livre (Free Software Foundation - FSF).
Lá você pode encontrar uma grande variedade de programas ou links
para outros sites onde programas podem ser encontrados.
• Linux e software rodando em Linux http://debian.org Este é um dos
principais pontos de referência para LinuX. Lá você vai encontrar centenas de outros sites.
• Dicionário on line http://www.yourdictionary.com de repente, se
você precisar de saber a tradução de uma palavra...
são alguns dos sites onde você pode encontrar informações on line. Experimente!
Neste capı́tulo vamos mostrar como você pode obter informações na internet
ou na máquina LinuX em que você estiver trabalhando. Mas se acostume
com uma idéia relativamente nova: não precisamos ter tudo guardado na
memória, as informações tem que ficar guardadas em bancos de dados, e
nós vamos até elas quando precisarmos. Aqui você vai saber onde existem
informações para você usar quando necessitar. Não tente decorar nada, não
tente copiar tudo que encontrar. Use e aprenda onde se encontram as coisas.
203
204
CAPÍTULO 10. MANUAL INTRODUTÓRIO DE REFERÊNCIA
Se você tiver chegado aqui sem ter lido nenhum programa aı́ gostariamos de conhecê-lo
pessoalmente, você deve ser um tipo muito especial, mas por favor corrija este defeito...
Claro, você já deve ter lido muitos programas e visto nas primeiras linhas, logo depois dos
comentários iniciais, o sı́mbolo #. A mais comum de todas é # include ...
Este sı́mbolo indica ao compilador que alı́ se encontra uma “instruç~
ao de compilaç~
ao, quer
dizer, um comando que o gcc deve executar antes de começar a ler o programa.
Vamos discutı́-las aqui, mas não se esqueça da observação tantas vezes já repetida: estamos
apenas levantando uma pontinha da coberta... há ainda muita coisa que pode ser dita e um
local onde você pode encontrar tudo é no manual da Libc de que já falamos muitas vezes e
que você pode consultar em LinuX com a sequência:
• Digite info libc. Ai você esta no manual da Libc, edição 1.0 mas não se assuste com
este número tão baixo, ela já surgiu muito boa... Digitando apenas info você abre o
sistema de informações do LinuX.
• Procure o que você deseja usando Crl-S e reponsdendo com a palavra chave desejada.
Funciona digitando apenas a /.
• Ou comece por ler informalmente o seu conteúdo para ver o que tem lá dentro. Você
vai então ver como este livro é diminuto... Você toda libc apenas dando enter, e as
páginas irão passando.
• Para sair aperte “q”, algumas vezes será preciso colocar o cursor, com o ratinho, dentro
do texto, primeiro.
10.1
O Sistema operacional e a shell
Para que um computador “rode” são necessárias dois processos básicos invisı́veis
para o usuário:
• O assembler Uma pequena linguagem de máquina que é chamada de assembler da máquina. É o assembler que caracteriza a diferença entre as
máquinas.
• O sistema operacional que é um programa de nı́vel um pouco mais alto
e bem mais sofisticado que o assembler e que vai se comunicar com o
usuários, com os programas e com os periféricos. LinuX é um sistema
operacional.
Em geral, a grande maioria dos usuários, mesmo usuários avançados, não
fazem uso direto do sistema operacional, alguns até podem ignorar que ele existe.
Em LinuX, por exemplo, existe um terceiro programa que é quase um sistema
operacional, o X-windows, que toma conta da máquina e faz a comunicação
usuários-programas-LinuX-periféricos
tudo isto num ambiente gráfico em que o ratinho tem uma importância quase
tão grande quanto a do teclado.
Se você estiver acostumado e quiser se manter preso a esta gaiola gráfica,
salte desta seção para a próxima e simplesmente ignore
X-windows, LinuX, assembler
mas isto não seria tı́pico para um futuro programador na linguagem C . . .
Vamos deixar de lado o assembler, vamos conversar um pouquinho sobre
LinuX. Vamos também ignorar o X-windows que ele é tão perfeito qual um burocrata de categoria: trabalha sem que ninguém se dê contas de sua existência.
10.1. O SISTEMA OPERACIONAL E A SHELL
205
Quando você atingir a perfeição você poderá se interessar pelo X para eventualmente incluir algumas de suas propriedades em seus programas.
LinuX é um unixoide e C foi feito por programadores que “nasceram” na
perspectiva do Unix1 , consequentemente é natural que quem deseje programar
em C precise saber como funciona o sistema operacional. A recı́proca é fundamental, quem quiser entender o sistema operacional tem que saber C. Porisso é
muito natural que em toda instalação LinuX se encontre o gcc.
Mas ninguém usa LinuX diretamente. Usamos uma linguagem que se comunica com o sistema operacional chamada bash que existe em diversos sabores
sh, bash, tcsh e outros. Sempre tem alguém que acha que uma delas é superior às outras, mas elas fazem as mesmas coisas.
Esta linguagem se chama shell e você pode ficar sabendo tudo (que é mais
importante) sobre shell com
info shell.
Leia a respeito do info em uma das seções adiante neste capı́utlo (procure no
ı́ndice remissivo). Você pode ler todo o manual sobre a shell apenas acionando
a barra de espaços, experimente.
A palavra inglesa shell também se refere a área de trabalho, talvez a razão
desta confusão venha do fato de que somente podemos usar os comandos da
shell quando abrirmos uma área de trabalho. E, que é uma área de trabalho?
• Se você estiver no X possivelmente, no pé da tela tem um ı́cone que lembra
uma “tv”. Clique nele e irá se abrir uma área de trabalho. Agora digite
info shell
e leia o manual da shell. Obviamente, o ı́cone com a pequena “tv” não
precisa estar no pé da tela, onde ele estiver, clique nele...
• Se você não estiver no X, no modo gráfico, então você já se encontra numa
shell ... digite
info shell
e leia o manual da shell.
• Se não houver nenhum ı́cone lembrando uma “tv” no X, clique no botão
que inicia os menus e você vai encontrar a palavra shell, escolha uma das
opções que lhe oferecerem e, quando aparecer uma pequena janela, ative
com o ratinho, digite
info shell
e leia o manual da shell.
• Se nada isto der certo, entre em contacto comigo...mas me dê alguma
pista sobre o que estiver acontecendo, por exemplo, se o computador está
ligado, sem tem energia elétrica, enfim qualquer coisa que ajude a saber
como começar. Uma alternativa: se houver alguém a volta,
pergunte,
lhe asseguramos, foi assim que começamos.
1c
foi feito para fazer o Unix
206
CAPÍTULO 10. MANUAL INTRODUTÓRIO DE REFERÊNCIA
10.2
As instruções de compilação.
1. #def ine Veja como exemplo traducao.h. Objetivo definir macros, constantes. Por exemplo você pode definir um modesto π assim
# define pi 3.1415
2. #if def #endif Muito poderosa, em geral você vai necessitar dela em programas mais avançados. Serve para alterar outras diretivas, como include
ou outras definições. Veja o exemplo
lookup.c
logo no inı́cio do programa. Se você usar ifdef tem que usar também
endif para marcar o fim do bloco.
3. # else Cria uma alternativa dentro de uma definição, ver lookup.c.
4. #if ndef Tradução: se não estiver definido. Semelhante a # ifdef para
o caso de não estar ainda definido.
5. #include Inclusão de bibliotecas, veja traducao.h
6. #undef Cancela uma definição. . Se você quiser corrigir um π “muito
pequeno” use esta diretiva e depois defina novamente π com “tamanho
melhor”...
10.3
Instruções na linha de comando
Ao compilar um programa qualquer você deve ter digitado:
gcc -Wall -oprog programa.c
em que programa.c é o nome do arquivo que contém o programa que você deseja rodar, o
código fonte prog é nome que você deseja dar ao programa executável, -Wall diz ao gcc que
ele deve ser falador e lhe explicar em detalhe todos os erros que encontrar. Naturalmente
gcc é o nome do compilador. Aqui gcc obedece a uma regra do Unix, e LinuX é Unix, que
estabelece que os programas podem ser modificados por parâmetros fornecidos na linha de
comando.
Você pode compilar um programa simplesmente digitando
gcc programa.c
e então o gcc irá criar, silenciosamente, um arquivo executável chamado a.out
e se você digitar
a.out
o programa irá rodar, naturalmente se tudo tiver dado certo. Se alguma coisa der
errado, gcc irá avisá-lo, mesmo que você não tenha solicitado a ajuda, entretanto
erros menores que gerariam apenas uma advertência não irão aparecer.
• A instrução -Wall na linha de comandos é reponsável pela lista de erros
e advertências. Sem ela gcc vai trabalhar mudo a não ser que haja erros
alguns dos quais serão informados ao usuário.
10.3. LINHA DE COMANDO
207
• A intrução -o indica ao gcc o nome do arquivo executável. Se você a
omitir ele vai criar o arquivo a.out.
• Se houver um arquivo terminado com “.c” gcc vai considerar este como a
origem do código fonte, o programa que deve ser compilado.
• Se você incluir a opção -c, o gcc irá apenas fazer a análise sintática do
código fonte e criar um arquivo programa.o em que programa é prefixo
do arquivo que contém o código fonte. Mas se você também incluir
-oprog
ele vai colocar o arquivo criado em prog que, entretanto, não será executável. Esta opção -c é útil para grandes programas afim de verificar a
sintaxe porque é um pouco mais rápida.
Se você digitar
gcc --help | more
o compilador vai lhe mostrar uma listagem das instruções de compilação que
podem ser fornecidas na linha de comando com uma breve descrição. Ver na
Libc descrições mais detalhadas ou com info gcc2 uma ajuda mais técnica
sobre o compilador. Evite de se perder, entretanto, vá devagar porque há muita
informação.
Em LinuX existe um formato compilado das funções que se encontram nas
bibliotecas que é mais eficiente e mais rápido de ser usado, mas para acessá-los é
preciso usar a instrução de compilação −lm na linha de comando. O programa
grafun.c, por exemplo, se compilado com
gcc -Wall -oprog grafun.c
vai lhe apresentar erros inexperados, como dizer-lhe que a função “sin” é desconhecida. A saı́da é compilá-lo com
gcc -lm -Wall -oprog grafun.c
que vai informar ao gcc que deve usar a biblioteca compilada. Abaixo você
tem uma listagem parcial do comando gcc --help. Se você quiser ler o seu
conteúdo com calma, execute o seguinte:
• gcc --help > teste e o resultado desta listagem vai ficar guardada no
arquivo “teste”. Preste atenção, se o arquivo existir, ele vai ser perdido
ficando agora os dados desta listagem nele. Porisso sugerimos “teste”, ou
“lixo”, etc... nomes que você deve usar para arquivos temporários. Evite
de usar temp ou tmp porque estes arquivos são usados pelo sistema.
• Leia o arquivo com um editor de textos.
• O sı́mbolo > é um comando do LinuX chamado de “redirecionador da saı́da
de dados”. Sem ele os dados iriam para a “saı́da de dados padrão”que
normalmente é o vı́deo.
Segue abaixo um trecho do resultado de gcc --help com a tradução para
português.
2 na
prósima secção discutimos o sistema info
208
CAPÍTULO 10. MANUAL INTRODUTÓRIO DE REFERÊNCIA
Usage: gcc [options] file...
Options:
--help Mostra esta ajuda
(Use ’-v --help’, em geral para obter ajuda sobre processos)
-save-temps Preserva os arquivos temporarios
-pipe
Usa pipes em vez de arquivos temporarios
-B <directory> Acrescenta <directory> aos caminhos de busca do
compilador
-b <machine>
Executa gcc para <machine>, se instalado
-V <version>
Roda a versao <version>, do gcc, se instalado
-v Mostra os programas chamados pelo compilador
-E Apenas Preprocessamento, nao compila nem
assembla nem link-edita.
-lm Use a biblioteca compilada de matematica
-S Apenas compila, nao cria o assembler ...
-c Compila e cria o assembler, mas nao link-edita.
-o <file>
Coloca o executavel em <file>
-x <language> Escolhe a linguaguem:
Linguagens aceitas inclue: c, c++, assembler, none
’none’ significa que gcc deve escolher
a linguagem a partir da extensao.
Observe que as opções não apresentadas podem ser importantes, entretanto
não agora no inı́cio. Inclusive algumas dessas que apresentamos não lhe serão
significativas agora. Se você tiver analisado o resultado do gcc --help vai
observar que acrescentamos uma que lá não estava explicitada, −lm.
10.4
Operadores aritméticos e lógicos
Veja no ı́ndice remissivo alfabético em que capı́tulo se encontram os operadores
aritméticos e lógicos.
10.5
A libc
Você acessa a libc com
info libc
É o manual mais completo sobre a linguagem C que você pode encontrar, e se
encontra ao alcance de seus dedos, dentro da máquina LinuX que você estiver
usando. Aqui vamos apresentar alguns flashes deste manual.
Para falar a verdade vamos mostrar-lhe como, usando info, você pode passear num dos “sites” mais completos da internet , a máquina LinuX em que
você estiver trabalhando. Como no resto do livro, coloque os dedos no teclado
do computador e nos acompanhe na viagem.
Se você digitar apenas info lhe vai ser apresentado um ı́ndice geral com
todas as informações sobre programas ou pacotes instalados na máquina. Vale
a pena começar por aı́ se você ainda não fez isto.
10.5. A LIBC
209
Se você estiver usando xemacs basta clicar com o ratinho no canto superior
direito, help e escolher info (online docs). Neste caso você pode viajar
apenas usando o ratinho. Se você for um professor talvez seja interessante ler
as primeiras linhas do info para ver que você pode criar informações sobre o
seu curso dentro do sistema Linux...
Os manuais dentro do info estão marcados com um asterisco, dê um enter
ao lado de um nome com asterisco e você entra no manual daquele pacote.
Procure libc usando:
• Ctrl S
• usando a barra / se você estiver entrado no info fora do xemacs
Dentro do xemacs você pode usar os botões a semelhança do que você faria
na internet para ir e voltar (ou sair) exit.
Se você tiver entrado no info no modo texto, use q para sair. Para retornar para um nı́vel superior, dentro da árvore de informações, use u (up) ou
p (previous). As teclas pgU e pgDn funcionam para subir ou descer o texto
dentro da página em uso.
E, naturalmente, info info lhe dá todas as informações sobre o sistema de
informações. E tudo isto é uma ótima oportunidade para aprender inglês...
Procure agora libc usando um dos métodos descritos acima.
No momento em que estamos terminando esta versão do livro, se encontra instalada neste máquina a versão 0.10 da libc, um número extremamente
modesto para o seu poder de informações. Os primeiros tópicos são:
• * Introduction:: Purpose of the GNU C Library. Aqui você vai encontrar
uma descrição do que lhe oferece a libc e como usá-la.
• * Error Reporting:: How library functions report errors.
O tı́tulo diz tudo, mas deixe-nos acrescentar, aqui você vai descobrir para
que serve o último comando da função principal(), o return numero.
• * Memory:: Allocating virtual memory and controlling paging.
É um tópico avançado, os itens do manual absolutamente não se encontram
arrumados em ordem crescente de dificuldade.
• * Character Handling:: Character testing and conversion functions.
Traduzindo a descrição: “testando caracteres e funções de conversão”.
• * String and Array Utilities:: Utilities for copying and comparing strings
and arrays.
Traduzindo a descrição: “utilitários para copiar e comparar vetores de
caracteres (strings) e vetores”.
• * Character Set Handling:: Support for extended character sets.
210
CAPÍTULO 10. MANUAL INTRODUTÓRIO DE REFERÊNCIA
• * Locales:: The country and language can affect the behavior of library
functions.
Traduzindo a descrição: “Paı́s e lingua podem afetar o comportamento
das funções nas bibliotecas.” Explicando melhor, com um exemplo, as
funções que manipulam data podem se acondicionar para a forma como
escrevemos as datas no Brasil.
• * Message Translation:: How to make the program speak the user’s language.
Traduzindo a descrição: “Como fazer os programas falar a linguagem do
usuário”. Aqui você já pode ver um defeito do nosso livro, não usamos esta
possibilidade, as mensagens de erro podem aparecer em diversas linguas
naturais.
Se você entrar neste tópico verá que o primeiro parágrafo diz uma das
verdades menos compreendidas pelos programadores:
“A comunicação dos programas com os seres humanos deve ser planejado
para facilitar a tarefa destes, (dos humanos). Uma das possibilidades consiste em usar as mensagens na lingua que o usuário preferir.” Exatamente
o contrário do que muita gente imagina quando escreve programas, “que
os humanos é que têm que se adaptar à informática”... Entretanto ainda
não está fácil de traduzir as mensagens de erro, é poriso que eu deixei esta
questão de lado, pelo menos no momento.
O pequeno excerto que apresentei acima deve-lhe mostrar que vale a pena
estudar a libc.
Poderiamos continuar analisando todo o manual, mas isto transforamaria
este livro em volume de mil páginas, e inutilmente porque você tem a libc na
ponta dos seus dedos, em inglês, é verdade. Mas se você não se preocupar em
dominar o inglês, pelo menos para leitura (fácil), tem muito coisa interessante
que vai ficar ficar longe do seu alcance.
10.6
Manual do compilador gcc
Faremos referência aqui às informações on line que podem ser obtidas numa máquina LinuX
sobre o compilador gcc. Vamos discutir info gcc. Leia o parágrafo anterior, neste capı́tulo,
sobre o sistema info.
Vamos supor que você digitou numa shell do Linux
info gcc
vamos lhe mostrar uma forma diferente de usar info
Você pode ler todo o manual do gcc como se fosse um livro, (pouco útil), é
melhor procurar questões que resolvam problemas pendentes.
Mas se você quiser ler como se fosse um livro eletrônico (que de fato ele é)
basta ir dando toques na barra de espaço e as páginas irão correndo antes os
10.6. MANUAL DO COMPILADOR GCC
211
seus olhos na tela do micro. Vale a pena fazer para uma primeira leitura (se
aplica melhor à libc).
Os primeiros toques na barra de espaço vão lhe permitir uma leitura do
ı́ndice. Em seguida ao ı́ndice você vai cair no primeiro capı́tulo se continuar
dando toques na barra de espaço e assim sucessivamente.
Observe que as teclas PgUp, PgDn funcionam como esperado, para subir e
descer páginas. Se você tiver uma boa instalação LinuX provavelmente não
precisara de alguns itens como instalaç~
ao e semelhantes. Mas certamente será
interessante fazer várias leituras do capı́tulo *Invoking GCC::. Faça várias
leituras, uma primeira, rápida, e depois outras em que você vai se demorar em
algum tópico que lhe chame sua atenção.
Quando seus programas ganharam mais densidade, quando tiver um projeto, vai precisar usar do utilitário make que também existe no BC. Ele serve
para interligar diversos programas e bibliotecas em único código otimizando o
resultado.
Finalmente você encontra quase ao final do “livro” a famosa GPL.
212
CAPÍTULO 10. MANUAL INTRODUTÓRIO DE REFERÊNCIA
Referências Bibliográficas
[1] Bell, David I. Landon Curt Noll and others CALC - An arbitrary precision
calculator - Debian GNULinux distribution http://www.debian.org
[2] Jargas, Aurélio M. Expressões regulares - Revista do Linux - Março 2000,
página 48 e Julho 2000, página 32
[3] Praciano-Pereira, T. Cálculo numérico Computacional - uma introdução com
Pascal Editora da Universidade Estadual Vale do Acaraú - 1999
edição eletrônica (copyleft) http://www.uvanet.br/matematica/livros.php
[4] Praciano-Pereira, T. Exercı́cios de Cálculo Edições do Labortório de Matemática Computacional da Universidade Estadual Vale do Acaraú - 2001
edição eletrônica (copyleft) http://www.uvanet.br/matematica/livros.php
[5] Oualline, Steve Practical C Programming - O’Reilly - 1997 - confuso, mas
correto - primeira leitura
[6] Loudon, Kyle Mastering Algorithms with C - O’Reilly - 1999 - primeira
edição. - bem escrito, avançado
213
Índice Remissivo
=→==, 81
==, 80, 81
reciclagem, 83
arquivo corrompido, 155
arquivo executável, 207
arquivos temporários, 207
array, 155, 209
ASCII, 47, 56, 146, 147
ASCII, tabela, 63
assembler, 80
atalho, 96
atribuição, 80
atribuir, 156
autor, 19
endereço, 123
avaliação, 80
a.out, 207
abstração e segurança, 152
abstração, 106, 193
acesso
arquivo, 154
disco, 154
acesso a ponteiro, 153
agenda
endereços, 169
ajuda, 207
online, 170
álgebra de tempo, 162
alterando C, 56
ambiente, 19
integrado, 20
programação, 19, 20
ambiente integrado, 20, 33
ambiente.h, 56
apeteco(), 105, 106
área de trabalho, 205
aritmética, 133
aritméticas
operação
ponteiro, 153
operações, 172
aritméticos
operadores, 172
arquivo
acesso, 154
escrita, 61
estruturas, 83
leitura, 61
lembrete, 83
backup, 33
BANCOS, 55, 58
base de numeração, 55
base zero
ı́ndice, 39
básico, valores, 146
BC, 12, 37, 143
biblioteca, 142
bibliotecas, 141
diretorios, 22
floor(), 142
help, 59, 143
math.h, 143
bem programar, 16, 86
biblioteca, 35, 86, 92, 143
string.h, 66
BC, 142
constantes, 136
gcc, 141
gcc - DebianGnuLinuX, 135
padrão, 135
214
ÍNDICE REMISSIVO
bibliotecas de C, 114
bit, 64, 65, 133
bit, byte, 152
bit,byte, 133
bloco
lógico, 50
bloco lógico, 37, 50
Borland C, 25
break, 71, 83, 94
desvio do fluxo, 94, 95
buffer
aditivo, 87
multiplicativo, 87
bug, 16
bugs, 81
byte, 64, 65
byte, bit, 133, 152
bytes
32, 133
número, 133
C
matemática, 171
C + +, 106, 159, 182
C gratuito, 12
C interpretado, 84
C personalizado, 35
calc, 84, 85, 92
caminho, 21
caractere, 145
caracteres, 47, 62, 144, 209
numéricos, 62
vetor, 40, 47, 48, 64, 65
vetor de, 146, 148
vetores de, 147, 209
Caracteres especiais, 146
caracteres, vetor, 69
cascata de execuções, 83
case, 83
caso, 83
cast, 47, 143, 144
ceil(), 143
char, 50, 51, 144
checker, 41
classe, 198
classe derivada, 201
215
codificação, 161
código fonte, 25, 26, 206, 207
códigos
validação, 171
cógido fonte, 25
colateral
efeito, 80
colateral, efeito, 81, 82
comando
linha de, 206
comando novo, 35
comentário, 28, 34, 86
comentários, 46, 90
como rodar programas, 24, 25
compara(), 66
compara palavras(), 66
compila, 80
compilação
instruções, 206
compilação, 24
avisos, 79
diretiva, 49, 50
erro, 178
matemática, 178
compilador
advertências, 206
análise sintática, 207
enganado, 80
erro na aprox., 169
erros, 206
compilar, 26, 206
programa, 101
rodar, 38, 43, 54
compile, 26
concatena palavras(), 66
construção de funções, 104
contabilidade, 107, 108
contador
base 0, 88
base 1, 88
copia de palavra(), 66
cores, 47
corrompido
arquivo, 155
criando informações, 209
Ctrl-c
216
ÍNDICE REMISSIVO
BC, 90
dado
tipo de, 47
dados
BC, formatação, 59
BC, tipo, 59
entrada, 38, 51, 59–61, 66, 166
espaço na memória, 131
formatação, 57, 58
formatadores de, 34, 36, 40, 44–
47
leitura, 59–61
redirecionador, 207
saı́da, 61, 165, 166
tipo, 128, 131
tipo de, 36, 41
tipo, identificador, 58
dados básicos, 146
dados, tipo, 135
DBL EPSILON, 138
DBL MAX, 138
DBL MIN, 138
DebianGnuLinuX, 135
declarações de erros, 209
default, 142
define, 206
depuração de programas, 90
derivada, classe, 201
desigualdades, 95, 96
detetização, 16
deve, haver, 108
dimensão de matriz, 157
direcionador &, 14
direcionador de memória, &, 14
diretiva
compilação, 50
define, 206
else, 206
endif, 206
ifdef, 206
included, 206
undef, 206
diretiva de compilação, 50
disco
abrir arquivo, 154
acesso, 154
fechar arquivo, 154
divisão
algoritmo
div. Euclid., 136
inteira, 136
djdev, 26
DOS
gcc, 12
editor e programas, 33
efeito colateral, 81, 82
encapsular, 194
endereçamento, erro, 14
endereçamento indireto, 13
endereço do autor, 123
endereços, agenda, 169
enfeites, 90
enganando
compilador, 80
enquanto(), 87, 93
enquanto() (while()), 71
entrada
de dados, 60
entrada de dados, 166
equação
segundo grau, 76, 78
erro
de sintaxe, 80
envio de naves, 138
lógico, 80
programa, 151
sintaxe, 80
erro com reais, 138
erro de endereçamento, 14
erro, ponteiro, 14
erros, 53, 209
código, 37
return, 37
escolha(), 83, 84, 86
escolha() (switch()), 71
escolhas múltiplas, 83, 84
escreva(), 50
espaço
naves
rotas, 138, 139
ÍNDICE REMISSIVO
espaço de nomes, 41, 119
espacial
estação, 138
especiais, Caracteres, 146
esqueleto.c, 21, 61
estruturas, 155
arquivo, 83
execuções
em cascata, 83
executável, 26
DOS, 25
LinuX, 26
executável, arquivo, 207
expandindo, 167
expandindo C, 56
expressão
avaliação, 80
externos, processos, 189
fclose(), 154
fgets(), 51, 154
Fibonacci, 48
figura
diretorios
BC, 22
equação, 180
Fluxograma, 78
fluxograma, 75–77
formatadores, 166
integral, 183
máquina, 137
printf(), 167, 168
Produto de complexos, 200
se() ou entao, 74
Variável Local, 122
fim, inicio, 50
float, 47, 137, 143
floor(), 143
floor.c, 143
FLT EPSILON, 138
FLT MAX, 138
FLT MIN, 138
flutuante
número
ponto, 137
ponto, 137, 138
217
fluxograma, 75, 76
fonte
código, 25
fonte, código, 207
fonte, código , 206
fopen(), 154
for(), 92, 94
formal
linguagem, 48
formatação, 56
BC, tipo de dado, 59
dados, 58
formatação de dado, 58
formatadores, 36
formatadores da dados, 47
formatadores de dados, 46
fprint(), 166
fprintf(), 154, 165, 170
FSF, 12
função
passando valor, 127
funções
construção, 104
gráficos, 185
função, 36, 50, 58, 98, 102
correção, 105
reutilizável, 106
função composta, 126
funções
como construir, 104
construindo, 102
método, 105
vantagens, 104
funções matemáticas, 207
garrafa destampada, 39
gcc, 12, 81
biblioteca, 141
para DOS, 12
glib, 114, 142
global
variável, 37
global, variável, 117
globalização, 51
Gnu, 12
Gnu - fundação, 178
218
GPL, 53, 211
gratuito,C, 12
grotesco, 119
haver, deve, 108
header file, 143
help, 59, 207
herança, 195
horas perdidas, 81
humano
programas, 210
IDE, 20, 33
identidade cultural, 51
identificador, 41, 64
identificadores, 65
IEEE, 137
if(), 71
if() else, 71
if(), se(), 71
if()/else, 71, 75–77
igualdade
teste, 80
imperativa
linguagem, 81
imprima(), 50, 56
include, 50
incremento, 88
ı́ndice, 157
ı́ndice
base zero, 39
ı́ndice e memória, 157
infinito, laço, 135
infinito, Loop, 135
infinito, loop, 90
info, 161, 208–210
glib, 114, 141, 142
informação, 204
lendo como um livro, 211
libc, 114, 142
informação
info, 204
informação
internet, 203
tamanho, 64
tamanho , 65
ÍNDICE REMISSIVO
informações, 114
criando, 209
inglês, 210
aprender, 209
inicio,fim, 50
init(), 195
insetos, 16, 81
instância, 195
instruções
de compilação, 206
insucesso(), 105
int, 47
integral.c, 184
integral, 182
inteiro
tamanho, 133
inteiros, 47, 55
inteiros módulo, 134
internacional
estação espacial, 138
internet, 208
informação, 203
programas, 203
interpretador C, 84, 85
joe, 23, 24
laço, 92
laço
condição, 88
Laço infinito, 135
leitura
texto, 46
leitura de dados, 59–61
ler(), 51
riscos, 57
letra maı́scula, 87
letra minúscula, 87
Libc, 114, 141, 161, 204
libc, 114, 142, 208
introdução, 209
linha
mudança, 36
linha de comando, 206
LinuX, 24, 53
local
ÍNDICE REMISSIVO
variável, 37
lógica, 71, 82
lógicas
operações, 172
lógico
bloco, 37, 50
lógicos
operadores, 172
loop, 92
condição, 88
Loop infinito, 135
loop infinito, 90
macro, 64, 65
expansão, 57
macros, 63, 64
main(), 36, 50, 51, 102
planejamento, 104
make, 211
malha, 183
man, 114
matemática, 171
compilação, 178
gráficos, 171
integrais, 171
séries, 171
somas de Riemann, 171
matemática com C, 171
matemáticas
funções, 178, 207
matriz, 155
matriz,dimensão, 157
&, memória
direcionador, 14
memória, 209
checker, 41
problema, 39
memória, direcionador, 33
memória
direcionador &, 14
memória e ı́ndice, 157
men.cc, 193
mensagens, 90
tradução, 210
menu, 85, 107
métodos, 106, 194
219
meu C, 35
Microsoft C, 23
mistérios, 58
modelos
programação, 194
modularização, 105, 117, 181
módulo
inteiros, 134
módulo espacial, 138
mudança
de linha, 36
multi-usuário, 56
múltiplas escolhas, 83, 84, 86
museu
ordem no acervo, 42
piano, 42
novo comando, 35
numeração, base, 55
número
byte, 133
bytes, 133
inteiro, 132, 133
limite no gcc, 132, 133
matemática, 131
ponto flutuante, 137
racional, 137
real, 137
tamanho, 138
tipo de dado, 132
um tipo de dado, 131
número fracionário, 47
número inteiro, 47
número real, 47, 137
números, 47, 55
inteiros, 55
inteiros, variação, 55
Objeto
programação orientada, 98
objeto, 195, 198
programação oo, 193
objetos, 106, 194
OOP,ver POO, 98
operação, ponteiro, 153
operações
220
aritméticas, 172
lógicas, 172
operacional, sistema, 204
operadores
aritméticas, 172
lógicos, 172
padrão linguı́stico, 51
padrao.c, 61
padronização, 154
palavra, 51, 64, 65
Palavra chave, 68
para(), 92, 94
para() (for()), 71
pare, 83, 94
desvio do fluxo, 94, 95
pare (break), 71
passagem de valor, 126
return, 126
pequenos programas, 86
Perl, 106
planejamento, 106
main(), 104
vazio, 73, 74, 104
plano de trabalho, 108
poluição visual, 105
ponteiro, 42, 148, 149
acesso, 153
associação
endereço, 153
variável, 150
declaração, 149, 150
endereço, 149
operação, 153
operação lógica, 153
soma, 153
subtração, 153
tutorial, 151
velocidade, 42
verificação, 41
ponteiro, erro, 14
ponto flutuante, 137
POO, 98
Português
programar em, 49
português, programa, 29
ÍNDICE REMISSIVO
praticidade, 60
precisão dos reais, 138
primeiro programa, 38
primeiro.c, 28, 29
principal(), 36, 50, 51
print(), 165
printf(), 50, 56, 165
private, 195
problema
crı́tico, 139
novo programa, 26
processos externos, 189
produto, 57
program
reset, 21
program reset, 26
programa
bonito, 46
busca de, 135
compilar, 101
depuração, 90
editor, 33
estrutura, 35, 49
função, 49
main(), 49
o primeiro, 38
protótipos, 50
re-inicialização, 26
reciclagem, 36, 68, 107
rodar, 19
ruim, 123
segurança, 105
técnica, 140
técnica de trabalho, 107
uma função, 36
programar
português, 35
Programar bem, 58
programar bem, 16, 86
programar em Português, 49
programas
o embelezamento, 105
reutilização, 106
verificação, 104
programas robustos, 41
programmer
ÍNDICE REMISSIVO
C, 82
projeto, 211
protótipo, 50, 195
protótipos, 50
públic, 195
Python, 106, 159, 182
ratinho, 204
reais, precisão, 138
real, 47, 137
funções, 141
processamento, 141
reciclagem, 104
arquivo, 83
exemplo, 196
programas, 36
reciclagem de programa, 107
reciclar
estrutura, 83
redirecionador
dados, 207
reserva
cópia, 33
reset
program, 21, 26
return, 37, 94
passagem de valor, 126
signficado, 126
return(), 71
reutilização, 68, 104–106
reutilização de função, 106
Riemann, 184
somas de, 184
Riemann, soma de, 182
risco, scanf(), 36
robustos, programas, 41
roda, 26
rodando gcc, 211
rodar
compilar, 38, 43, 54
programa, 19
rodar programa, 24, 26
rodar programas, 24, 25
Rodar um programa, 44
rotas de espaço-naves, 138
roteiro, 102
221
roteiros, 106
run, 21, 26
saı́da
padrão, 80
saı́da de dados, 165, 166
scanf(), 51, 165, 166
risco, 36
riscos, 57, 58
script, 102
script languanges, 106
se() (if()), 71
se() ou entao, 71
se(), if(), 71
se()/ou entao, 75–77
se()/ou entao (if()/else), 71
secreta
chave, 177
segurança e abstração, 152
segurança, programa, 105
senhas, teste de, 101
shell, 24, 28, 204, 205
sı́mbolo, 64, 65, 145
sintaxe, 71
sistema operacional, 204
sizeof, 69
sizeof(), 67
sprintf(), 170
sscanf(), 165, 166
standard output, 80
strcat(), 66
strcmp(), 66, 67
uso correto, 68
strcpy(), 66
string, 36, 40, 43, 46–48, 50
strings, 47, 156
strlen, 69
strlen(), 66, 67
struct, 155
sucesso(), 105
switch(), 71, 83, 84, 86
tabela ASCII, 47, 63
tabela de alocação, 41
tamanho do inteiro, 133
tamanho da(), 69
222
tamanho de palavra(), 66, 67
técnica
programar bem, 140
tempo
álgebra, 162, 163
para humanos, 161
para máquinas, 161
tempo, álgebra, 162
temporários
arquivos, 207
teste de senhas, 101
texto
leitura de, 46
tipo de dado, 30, 47
transformação, 143, 144
tipo de dados, 41, 135
Torvalds, Linus, 53
trabalho, plano, 108
trabalho, regra, 33
tradução, 35
tradução das mensagens, 210
tradução de C, 51
traducao.h, 49
tutorial, 185
ASCII, 146
caracteres, 146
compara(), 70
floor.c, 143
gráficos, 185
ponteiro, 151
ponteiros, 127, 149
strcmp(), 70
tipo de dados, 165
unidade lógica
bloco, 50
Unix, 53
uso de C, 70
uso de C, 15
valor
passando, 127
passando ponteiro, 128
valor, passagem, 126
valores básicos, 146
variáveis, 65
ÍNDICE REMISSIVO
variáveis padronizadas, 60
variável, 48, 64
criação, 117
destruição, 117
eliminando, 125
endereço, 119
espaço de nomes, 119
função composta, 126
global, 37, 117–119
global → local, 127
linguagem comum, 48
local, 37, 117–119
local, denominacação, 119
passando valor, 127
programação, 48
variável local, 37
variação dos ı́ndices, 157
varredura, 183
vazio
planejamento, 73, 74, 104
velocidade
ponteiro, 42
verificação de programas, 104
vetor
de caracteres, 64, 65, 145
vetor de caracteres, 36, 63
vetores
de caracteres, 69, 156
vetores de reais, 47
vi, 24
visual
poluição, 105
vocabulário, 50
voltar, 94
voltar() (return()), 71
while(), 71, 87, 93
word, 64, 65
wpe, 23
X-windows, 204
xcoral, 23
xemacs, 23
xwpe, 23