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. - tarcisio@e-math.ams.org 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 tarcisio@e-math.ams.org 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 tarcisio@member.ams.org 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 tarcisio@e-math.ams.org). 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 = # 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