Disciplina: Iniciação à Computação
Prof. Augusto Antonio Pinheiro Neto
Curso de Licenciatura em Matemática – UFPBVIRTUAL
[email protected]
Ambiente Virtual de Aprendizagem: Moodle www.ead.ufpb.br
Site da UFPBVIRTUAL www.virtual.ufpb.br
Telefone UFPBVIRTUAL (83) 3216 7257
Carga Horária: 60 horas
Créditos: 04
Ementa
•
•
Componentes básicos de um computador
Linguagem de programação
Descrição
Nesta disciplina apresentaremos a configuração básica de um computador, de modo que o aluno
tenha uma compreensão geral de sua arquitetura. Estudaremos em seguida uma linguagem de programação
imperativa, permitindo capacitar o aluno na programação de resoluções de problemas numéricos.
Objetivos
Ao final da disciplina o aluno deverá ser capaz de:
compreender a arquitetura básica de um computador,
programar as operações de entrada\saída,
programar operações básicas como: atribuição de valores a variáveis ou constantes,
permutação de valores entre duas variáveis, uso de acumuladores de soma e de produtos,
programar os diversos tipos de decisão e de repetição (laços),
compreender os principais tipos de dados inerentes à linguagem estudada,
trabalhar com funções (elemento de programação)
elaborar algoritmos de resolução de diversos tipos de problemas numéricos,
programar esses algoritmos
197
Unidades Temáticas Integradas
Unidade I
•
Componentes básicos de um computador
Unidades funcionais básicas
• Unidades de Entrada e/ou Saída
• Dispositivos de Entrada de Dados
• Dispositivos de Saída de Dados
• Dispositivos de Entrada e Saída de Dados
• Memória principal
• Tipos de memória
• Unidade Central de Processamento
o
Unidade de Controle
o
Unidade de Aritmética e Lógica
o
Registradores
o
Clock
• Sistemas Operacionais
• Os arquivos em informática
Unidade II
Algoritmos
•
Algoritmos
•
Abordagem dividir para conquistar
•
Algoritmo TrocarPneuFurado
o Refinamento do passo 1
o Refinamento do passo 1.5
• Algoritmo TrocarLâmpadaQueimada
• Exercícios
• Solucionando um problema
•
o
Fases de resolução
o
Fase de implementação
Atitude é tudo
Unidade III
Introdução à programação
Conceitos básicos
•
Memória e variáveis
•
Dando nome às variáveis
•
Tipos de variáveis
•
Declarando uma variável
•
Atribuindo valor a uma variável
198
•
Constantes
•
Exibindo o conteúdo de uma variável
•
Recebendo uma entrada
•
Teste
Unidade IV
Operações aritméticas básicas
Primeiros passos
•
A divisão
•
O módulo
•
Cálculo entre variáveis
•
Abreviações: incremento e decremento
•
Outras abreviações
•
A biblioteca matemática
•
Teste
Unidade V
Praticando entrada e saída
Algumas considerações sobre a função scanf
•
Algumas considerações sobre a função printf
Unidade VI
•
Estruturas de controle: condicionais
A estrutura if ... else
o O teste if
o O teste else
o O teste else if
• Alguns erros comuns
• Para melhor compreender as condições
• A estrutura switch
• Condições condensadas
• Teste
Unidade VII Estruturas de controle: repetições
•
O laço while
o
O laço do .. while
o
O laço for
o
Teste
199
Unidade VIII Funções
•
Qual o objetivo de uma função?
•
A estrutura de uma função
•
Criando uma função
•
Chamando uma função
•
Mais exemplos
•
o
Conversão Celsius/Fahrenheit
o
Transformar segundos em horas, minutos e segundos
o
Área de um retângulo
o
Um menu
Teste
Unidade IX
•
Ponteiros
Um pouco mais sobre as funções
o
o
•
A memória e os endereços
Endereço e valor
Utilização de ponteiros
o
Criar um ponteiro
o
Enviar um ponteiro a uma função
o
Outro modo de enviar um ponteiro a uma função
•
Um problema estranho?
•
Resumo
•
Teste
Unidade X
Arrays
•
Os arrays e a memória
•
Declarando um array
•
Como acessar um elemento
•
Atribuindo valores a um array
•
Arrays e ponteiros
•
Listando um array
•
Inicializando um array
•
Passagem de arrays como parâmetros
•
Arrays de duas ou mais dimensões
•
Teste
200
Unidade I – Componentes básicos de um computador
1. Situando a temática
Os computadores estão cada vez mais presentes no dia-a-dia dos cidadãos. Mesmo aqueles que não os
utilizam diretamente têm essas máquinas ligadas às suas vidas. Basta nascer para que as informações
referentes ao novo cidadão passem a fazer parte de algum banco de dados, por exemplo, o do hospital onde a
criança nasceu. Ao registrá-la, o pai (a mãe) estará contribuindo para alimentar outro banco de dados, o do
cartório de registros civis. Ao iniciar seus estudos oficiais, novas informações serão transferidas para os
computadores, desta vez os da escola. E assim será por toda a vida, imagino. A informação deve ser
armazenada e disseminada no momento necessário. Seria bom então entender um pouco sobre essas
máquinas que nos acompanham por toda a vida.
2. Problematizando a temática
Os computadores são sistemas eletrônicos de processamento de dados, compostos de uma parte física,
denominada hardware e de uma parte lógica, denominada software. O hardware é formado por um conjunto
de funções lógicas, geralmente associadas a um ou mais equipamentos físicos, capazes de executar
determinadas tarefas. Iniciamos os nossos estudos sobre programação apresentando uma noção sobre a
configuração básica de um computador.
3. Conhecendo a temática
3.1 Unidades funcionais básicas
As funções lógicas associadas ao hardware de um computador são geralmente agrupadas em blocos
chamados de unidades funcionais. Um computador possui as seguintes unidades funcionais básicas,
compostas de circuitos eletrônicos específicos:
UC
Entrada
Teclado
Mouse
Leitor de disquete
Scanner
Tela sensível
Leitor de CD, DVD
UAL
CPU
Saída
Registradores
Monitor (Vídeo)
Impressora
Gravador de disquete
Gravador de CD, DVD
Memória Principal
3.2 Unidades de Entrada e/ou Saída
As unidades de Entrada são blocos funcionais compostos por circuitos eletrônicos com a finalidade
específica de transmitir dados do meio exterior para a memória do computador. As unidades de Saída
realizam a operação inversa, transmitem dados da memória do computador para o meio exterior. A
transmissão física desses dados se dá por meio de dispositivos especiais. Na figura acima são listados alguns
dispositivos que têm a finalidade de realizar essas tarefas. Os dispositivos que realizam tarefas relacionadas à
função de entrada são conhecidos como dispositivos de entrada de dados. De modo análogo, os dispositivos
que realizam tarefas relacionadas à função de saída são conhecidos como dispositivos de saída de dados.
Alguns dispositivos são capazes de executar entrada e saída de dados.
3.3 Dispositivos de Entrada de Dados
Exemplos de alguns dispositivos de entrada de dados: teclado, mouse, scanner. Existem outros, como
microfone (dispositivo de entrada de som), monitores de vídeo sensíveis ao toque, light-pen, joystick, etc.
201
Teclado
Mouse
Scanner
3.4 Dispositivos de Saída de Dados
Os dispositivos mais comuns de saída de dados são as impressoras e os monitores de vídeo. Entretanto,
existem outros dispositivos nesta classe como plotters (traçadores de gráfico), projetores, caixas de som, etc.
projetor
Impressora
Monitor
3.5 Dispositivos de Entrada e Saída de Dados
Alguns dispositivos possuem as funções de entrada e saída. Eles são bem conhecidos e podemos citar, por
exemplo, leitor/gravador de discos (disquetes, CD’s, DVD’s, etc),
Leitor/Grava
dor de CD’s
Leitor/Grava
dor de
Leitor/Grava
dor de HD’s
3.6 Memória Principal
Por memória, compreende-se todo dispositivo capaz de guardar qualquer dado ou informação. A memória
principal de um computador é o local onde estão armazenados os programas e dados que são utilizados
durante um processamento. Todo dado ou programa a ser processado deve estar na memória principal do
computador. Caso esses dados estejam armazenados em disquetes, Cd’s ou HD’s, será necessário, primeiro,
transferi-los para a memória principal. A memória principal do computador possui a característica de ser
volátil, isto é, caso o computador seja indevidamente desligado (falta de energia elétrica, por exemplo), todo
o seu conteúdo será perdido, ou seja, tornar-se-á inacessível.
A memória principal de um computador existe na forma de “pentes de memória” como mostrado nas figuras
a seguir.
Atualmente, os pentes de memória, mais encontrados, instalados nos microcomputadores são os de 256
MBytes, 512 MBytes, ou 1GBytes.
Memória
Memória 256 MB
3.7 Tipos de memória
As memórias estão representadas em duas grandes categorias de chips:
RAM (random access memory): são chips de memória que podem ser lidos e/ou gravados pela CPU.
A memória principal é deste tipo.
202
ROM (read only memory) : são chips de memória que podem apenas ser lidos pela CPU. Uma
memória ROM é permanente e sua gravação é feita pelo fabricante do computador, ou pelo fabricante
de memórias. Um conjunto específico de programas, necessários à inicialização de um computador,
denominado BIOS, localizado na placa mãe do computador, está encapsulado em uma memória deste
tipo.
3.8 Unidade Central de Processamento
A unidade central é o elemento funcional central de todo computador. É o “coração”, ou melhor, o “cérebro”
do computador. Nesta unidade são realizadas todas as operações. Todas as informações tratadas pelo
computador transitam por esta unidade funcional. A unidade central é também conhecida por CPU (Central
Processing Unit), Unidade Central de Processamento e é dividida classicamente em três partes:
I - Unidade de Controle, responsável pela extração das instruções da memória do computador e por sua
análise. Essa unidade controla dois registros especiais denominados CONTADOR DE INSTRUÇÃO (PC)
que contém o endereço de memória da próxima instrução a ser executada e REGISTRO DE INSTRUÇÕES
que contém a instrução extraída da memória. É a unidade de controle que gerencia todos os eventos
necessários à operação do computador.
II - Unidade de Aritmética e Lógica (UAL) controla um conjunto de registros que devem conter os códigos
dos operandos e do operador, necessários à realização de uma operação aritmética ou lógica. Exemplos de
operações aritméticas: adição, subtração, multiplicação ou divisão de números. Exemplos de operações
lógicas: comparação de dois valores alfabéticos, operações envolvendo operadores os booleanos, AND, OR e
NOT.
Nos dias atuais, uma CPU está completamente embutida em uma pastilha, denominada chip, de dimensões
reduzidas. As primeiras CPUs integradas em um único chip foram a CPU 4004 e a CPU 8008. Hoje esses
chips são mais conhecidos no mercado pelo nome de processador ou microprocessador.
Em 1974 a Intel fabricou o 8080, o primeiro microprocessador a ser usado em larga escala nos chamados
"computadores pessoais". Antes dele, os microcomputadores eram usados apenas em laboratórios científicos,
em fábricas e em universidades.
III - Registradores, memórias especiais, de alta velocidade, localizadas no interior de um microprocessador,
enquanto a memória principal é externa a este. Cada registrador possui uma função específica. Dentre os
registradores destacam-se os seguintes:
Contador de programa (PC - Program Counter), que aponta para a próxima instrução a executar.
Registro de instrução (IR - Instruction Register) que armazena a instrução em execução.
Outros registros que permitem o armazenamento de resultados intermediários.
Clock
Clock é um circuito oscilador que tem a função de sincronizar a velocidade de transferência de dados entre
duas partes durante um processamento. Por exemplo, a transferência de dados entre o processador e a
memória principal. Essa velocidade de transferência (freqüência) é medida em ciclos por segundo, ou Hertz.
A velocidade de acesso dentro do processador é maior que na memória principal. Os processadores Pentium100, Pentium II-300, acessam a memória principal a 66 MHz.
3.9 Sistemas Operacionais
Sistemas operacionais são conjuntos de programas que permitem explorar (fazer funcionar) o computador.
Atualmente os sistemas operacionais mais conhecidos são Windows e Linux.
Um computador é um conjunto de materiais inertes. São as diversas camadas de software que fazem
com que ele funcione. Quando ligado à rede elétrica, uma primeira camada de software, sempre a mesma, é
posta em ação. Esta camada foi gravada diretamente no hardware: é a BIOS (Basic Input/Output System).
Esta camada realiza certo número de verificações e de testes (presença de diversos periféricos, volume de
memória, etc). Após isso o sistema operacional assume o controle.
É o sistema operacional que permite aos humanos dialogar com os computadores. Segundo o grau de
convivialidade do sistema, esse diálogo será mais ou menos amigável, podendo ser textual, gráfico ou uma
203
mistura desses. A partir do sistema operacional o usuário pode então lançar os aplicativos (programas
específicos).
3.10 Os arquivos em informática
Todo arquivo é visto e tratado do mesmo modo pelo sistema operacional em vista de seu armazenamento em
disco. Tecnicamente, um arquivo é um conjunto de bits formando uma entidade identificada por um nome e
uma extensão seguindo a forma: nome.ext.
Somente a sua extensão é capaz de determinar a natureza e assinalar ao sistema operacional o que esse
arquivo é capaz de fazer. Assim, um arquivo .doc é reconhecido como um programa Word, um arquivo .xls é
reconhecido como um arquivo do Excel e esses softwares são carregados na memória para execução. Alguns
arquivos de imagens (.jpeg ou .jpg, .gif, .png, etc.) necessitam de um visualizador de imagens instalado no
computador para que essas imagens possam ser exibidas na tela. Por padrão, está previsto que esses arquivos
podem ser abertos no Internet Explorer, capaz de exibi-los a partir de páginas da Internet.
Dependendo do sistema operacional, existem regras com mais ou menos restrições para dar nomes aos
arquivos. No MS-DOS, antigo sistema operacional, os nomes de arquivo deveriam ter no máximo 8
caracteres com uma extensão de três caracteres. A partir do Windows 95 os nomes dos arquivos passaram a
ter até 256 caracteres (incluindo o caminho onde se encontra o arquivo, por exemplo,
C:\programas\C\programa1.exe).
204
Unidade II - Algoritmos
1. Situando a temática
Programar é uma ciência ou uma arte? A computação é uma ciência, porem programar está mais para arte. A
programação requer certo dom. Mas isto podemos desenvolver com esforço e paciência. Assim como
aprendemos a pintar, também podemos aprender a programar. Alguns requisitos são necessários, é verdade.
Para desenvolvermos programas que realizem algumas « proezas » matemáticas, é necessário que tenhamos
algum conhecimento para matemático, senão, como programá-las? Qualquer que seja a natureza de nossos
programas, existem algumas regras básicas a serem seguidas. Primeiramente, é preciso entender o enunciado
do problema a ser resolvido e então partirmos para encontrar uma solução. O estudo do domínio de um
problema é conhecido no mundo da computação como análise. A apresentação de uma solução para esse
problema faz parte do que se chama projeto. Como os domínios (álgebra, geometria, trigonometria, etc) são
vastos e variados, procuramos seguir uma metodologia de resolução de problemas. É o que faremos a seguir
no estudo dos algoritmos.
2. Problematizando a temática
Suponhamos o seguinte problema: trocar o pneu furado de um carro (você já passou por este tipo de
problema?). Parece que estamos diante de um problema simples, mas tentemos explicar a um robô como
proceder para realizar tal tarefa. Veremos um enfoque denominado “dividir para conquistar”, que nos ajudará
bastante nessa e outras tarefas.
3. Conhecendo a temática
3.1 Algoritmos
Definição: conjunto finito, ordenado e não ambíguo de passos necessários para realizar uma tarefa. Esse
conjunto apresenta as seguintes características:
• possui um ponto de parada, isto é, sua execução é finita;
• recebe dados de entrada e, em função desses, produz dados de saída.
Algoritmo ≅ Receita de bolo
Um algoritmo pode ser correto ou não. Um algoritmo correto produz uma saída correta e pára, enquanto que
um algoritmo incorreto produz uma saída incorreta ou não pára.
3.2 Abordagem dividir para conquistar
Essa abordagem, muito utilizada em informática, consiste em dividir o problema inicial em vários
subproblemas, recursivamente. Cada subproblema é, logicamente, mais simples que o problema inicial. Esse
procedimento de divisão continua até que sejam encontrados subproblemas, que possam ser resolvidos de
forma simples ou trivial.
3.3 Algoritmo Trocar Pneu Furado
1.
2.
3.
4.
5.
pegue o macaco e levante o carro
retire o pneu furado
pegue o estepe, coloque-o na roda e aperte os parafusos
abaixe o carro e reaperte os parafusos
guarde o pneu furado e o macaco
Refinamento do passo 1
1.1 remova o macaco do porta-malas
1.2 coloque o macaco sob o carro, próximo ao pneu furado
1.3 desaperte os parafusos da roda
1.4 insira a manivela no macaco (ou monte-o de acordo com o modelo do macaco)
1.5 coloque um calço sob o carro para impedi-lo de se mover
1.6 levante o carro com o macaco até que haja espaço suficiente para colocar o estepe
205
Refinamento do passo 1.5
1.5.1 se o carro estiver em uma ladeira, de frente para o topo desta, então coloque o
calço atrás de um pneu em bom estado caso contrário, coloque o calço na
frente de um pneu em bom estado
O passo 1.6 revela a existência de um procedimento repetitivo: enquanto não houver espaço suficiente para
colocar o estepe, faça o seguinte: levante o carro com o macaco.
Outro procedimento que pode ser associado ao passo 1.6 é: repita o levantamento do carro com o macaco
até que haja espaço suficiente para colocar o estepe.
3.4 Algoritmo Trocar Lâmpada Queimada
1. Remova a lâmpada queimada
2. Coloque a nova lâmpada
Refinamento
1.
2.
3.
4.
5.
6.
7.
Posicione uma escada em baixo da lâmpada queimada
Escolha uma nova lâmpada com a mesma voltagem da queimada
Suba na escada até que a lâmpada possa ser alcançada
Gire a lâmpada queimada no sentido anti-horário até que ela se solte
Posicione a nova lâmpada no soquete
Gire-a no sentido horário até que ela se firme
Desça da escada
1. Posicione uma escada em baixo da lâmpada queimada
2. Selecione uma nova lâmpada para a substituição
2.1. Se a voltagem não for a mesma da lâmpada queimada, repita os passos
abaixo até encontrar uma que sirva
2.1.1. Descarte a lâmpada selecionada
2.1.2. Selecione uma nova
3. Repita até que a lâmpada possa ser alcançada
3.1.1. Suba um degrau da escada
4. Repita até que a lâmpada fique livre do soquete
4.1.1. Gire a lâmpada no sentido anti-horário
5. Posicione a nova lâmpada no soquete
6. Repita até que a lâmpada esteja firme
6.1.1. Gire a lâmpada no sentido horário
7. Desça da escada
Esses passos e refinamentos seriam importantes para a realização dessas tarefas por uma máquina.
Um ser humano faz essas coisas intuitivamente, mas as máquinas...
3.5 Exercícios
1 – Elabore um algoritmo para fazer pipoca em uma panela de fogão, usando manteiga, sal e milho.
2 – Elabore um algoritmo para realizar uma chamada telefônica local.
3 – Elabore um algoritmo para realizar uma chamada telefônica de longa distância (interurbana).
4 – Elabore um algoritmo que simule sua saída de casa pela manhã. Comece com o passo “dormindo na
cama” e inclua todas as suas atividades matinais.
5 – Elabore um algoritmo para calcular as raízes reais de uma equação do 2º grau. Se a equação não tiver
raízes reais, isto deve ser mencionado como resposta.
206
3.6 Solucionando um problema
Fase de resolução do problema
Problema
Passo único
Algoritmo
Fase de implementação da solução
Programa de
computador
Fase de resolução – elaboração de um algoritmo para resolver o problema proposto, incluindo testes para
verificar se a solução é boa.
Fase de implementação – ao encontrarmos o algoritmo adequado à solução do problema, precisamos
codificá-lo em uma linguagem de programação. Geralmente, essa codificação é fácil de ser feita desde que o
programador compreenda bem a sintaxe e a semântica dos comandos da linguagem escolhida para a
implementação.
3.7 Atitude é tudo
Luis é o tipo de cara que você gostaria de conhecer. Ele estava sempre de bom humor e sempre tinha algo de
positivo para dizer. Se alguém lhe perguntasse como ele estava, a resposta seria logo: Se melhorar estraga.
Ele era um gerente especial em um restaurante, pois seus garçons o seguiam de restaurante em restaurante
apenas pelas suas atitudes. Ele era um motivador nato. Se um colaborador estava tendo um dia ruim, Luis
estava sempre dizendo como ver o lado positivo da situação. Fiquei tão curioso com seu estilo de vida que
um dia lhe perguntei: Você não pode ser uma pessoa positiva todo o tempo. Como faz isso? Ele me
respondeu: A cada manhã, ao acordar, digo para mim mesmo: Luis, você tem duas escolhas hoje: Pode ficar
de bom humor ou de mau humor. Eu escolho ficar de bom humor. Cada vez que algo ruim acontece, posso
escolher bancar a vítima ou aprender alguma coisa com o ocorrido. Eu escolho aprender algo. Toda vez que
alguém reclamar, posso escolher aceitar a reclamação ou mostrar o lado positivo da vida. Certo, mas não é
fácil - argumentei. É fácil sim, disse-me Luis. A vida é feita de escolhas. Quando você examina a fundo,
toda situação sempre oferece escolha. Você escolhe como reagir às situações. Você escolhe como as pessoas
afetarão o seu humor. É sua a escolha de como viver sua vida. Eu pensei sobre o que o Luis disse e sempre
lembrava dele quando fazia uma escolha. Anos mais tarde, soube que Luis cometera um erro, deixando a
porta de serviço aberta pela manhã. Foi rendido por assaltantes. Dominado, enquanto tentava abrir o cofre,
sua mão tremendo pelo nervosismo, desfez a combinação do segredo. Os ladrões entraram em pânico e
atiraram nele. Por sorte foi encontrado a tempo de ser socorrido e levado para um hospital. Depois de 18
horas de cirurgia e semanas de tratamento intensivo, teve alta ainda com fragmentos de balas alojadas em seu
corpo. Encontrei Luis mais ou menos por acaso. Quando lhe perguntei como estava, respondeu: Se melhorar
estraga. Contou-me o que havia acontecido perguntando: Quer ver minhas cicatrizes? Recusei ver seus
ferimentos, mas perguntei-lhe o que havia passado em sua mente na ocasião do assalto. A primeira coisa
que pensei foi que deveria ter trancado a porta de trás, respondeu. Então, deitado no chão, ensangüentado,
lembrei que tinha duas escolhas: poderia viver ou morrer. Escolhi viver! Você não estava com medo?
Perguntei. Os para-médicos foram ótimos. Eles me diziam que tudo ia dar certo e que ia ficar bom. Mas
quando entrei na sala de emergência e vi a expressão dos médicos e enfermeiras, fiquei apavorado. Em seus
lábios eu lia: "Esse aí já era". Decidi então que tinha que fazer algo. O que fez? Perguntei. Bem, havia
uma enfermeira que fazia muitas perguntas. Perguntou-me se eu era alérgico a alguma coisa. Eu respondi:
"sim". Todos pararam para ouvir a minha resposta. Tomei fôlego e gritei: - "Sou alérgico a balas!" Entre
risadas lhes disse: - "Eu estou escolhendo viver, operem-me como um ser vivo, não como morto." Luis
sobreviveu graças à persistência dos médicos, mas também graças à sua atitude. Aprendi que todo dia temos
opção de viver plenamente. Afinal de contas, "ATITUDE É TUDO".
207
Agora você tem duas opções:
1. Após ler estas aulas, esquecê-las.
2. Dar o melhor de si, com todas as dificuldades que possam aparecer e escolher aprender.
Boa escolha e bons estudos.
(Texto de autor desconhecido, obtido na Internet.)
4. Avaliando o que foi construído
Pensar por etapas, foi o que vimos. Não adianta querer que os computadores se comportem como os
humanos. Eles fazem, realmente, operações aritméticas e de comparações, muito mais rápido do que nós o
fazemos, mas é preciso dizer-lhes, felizmente, o deve ser feito, como e quando. Os algoritmos se resumem
nisso, informar ao computador de modo inequívoco essas formas e temporalidade.
5. Referências
1. Salvetti, Dirceu D., Barbosa, Lisbete M., Algoritmos. Pearson Education do Brasil, 1998
2. Pinheiro Neto. Augusto A., Notas de aula, 2001.
208
Unidade III - Iniciação a Programação
1. Situando a temática
Poderíamos dizer que a palavra programar vem do Latim programmeus. Você acredita? Pois bem, é
verdade. Mas o que nos interessa é que a palavra programar, em nosso contexto, significa simplesmente:
escrever programas de computador. Os programas solicitam aos computadores a realização de ações.
Veremos como dialogar com um computador, procurando ensinar-lhe como realizar essas ações, ou seja,
vamos aprender a codificar em uma linguagem de programação um algoritmo apresentado como solução
para um problema.
2. Problematizando a temática
O computador é uma máquina estranha, é o mínimo que se pode dizer. Ele entende apenas aquilo que está
codificado somente com 0’s e 1’s. Esse tipo de linguagem é denominado linguagem de máquina, ou de baixo
nível. Imaginemos, por exemplo, que a operação de somar seja representada por 11001100 e que desejemos
somar 2 com 3. Para transmitir essa ordem a um computador, temos que escrever uma instrução equivalente
a esta: 11001100 00000010 00000011. Felizmente, as linguagens de programação atuais estão bem mais
próximas das linguagens naturais, aquelas que os homens utilizam para se comunicarem. Existem diversas
linguagens de programação que podem ser utilizadas na implementação de algoritmos. Estudaremos a seguir
uma das mais importantes hoje em dia, a linguagem C.
3. Conhecendo a temática
3.1 Conceitos básicos
Linguagens de alto nível
As linguagens atuais com as quais se escreve programas para computadores são estruturadas (possuem
estruturas de controle), mais próximas da linguagem natural e com certo formalismo matemático. Essas
linguagens são chamadas de linguagens de Alto Nível. Exemplos de linguagens de alto nível.
C, C++, Java, Pascal, Visual Basic, Delphi, PHP, etc...
Programa fonte e programa executável
Os programas escritos em linguagens de alto nível, aqueles escritos pelo programador, são geralmente
chamados de programas fonte, pois ali são codificados os conhecimentos necessários para a solução de
determinada tarefa. Uma vez traduzido para a linguagem de máquina, aquela dos 0’s e 1’s, o programa
resultante recebe o nome de programa executável.
Tradutores
Tradutores são programas especiais que traduzem um programa fonte para linguagem de máquina. Alguns
programas são ditos compilados e outros interpretados. O que é isso? Existem tradutores que, quando da
tradução do programa fonte, traduzem cada linha e a executam imediatamente. Nesse caso diz-se que houve
uma interpretação do programa fonte e esse tipo de tradutor é denominado interpretador. Quando a tradução
se dá por inteiro e tão somente após seu término o programa é executado, diz-se que ocorreu uma compilação
e esse tipo de tradutor é denominado compilador. Programas compilados são mais rápidos do que programas
interpretados.
Importante! Existe um compilador diferente para cada linguagem de alto nível. Isso é lógico. As linguagens
sendo diferentes, não se traduz C do mesmo modo que Delphi, por exemplo.
209
Fazendo um esquema gráfico do que acabamos de dizer temos:
Programa escrito
em linguagem de
Alto Nível
Programa Fonte
Programa de
tradução para
linguagem binária
Programa traduzido em
linguagem de máquina
Programa Executável
Compilador/
Interpretador Fonte
Veremos em seguida que mesmo para uma única linguagem como C, por exemplo, existem vários
compiladores diferentes. Há compiladores escritos pela Microsoft, GNU, etc. Felizmente esses compiladores
são quase idênticos.
Linguagens de altíssimo nível
São fáceis de utilizar, possuem vários elementos de linguagem que podem ser arrastados para a área do
programa e o seu código é então gerado automaticamente, digamos "grande público", como Visual Basic.
Essas linguagens apresentam alguns inconvenientes: elas custam caro e são limitadas à plataforma. Se o
compilador for voltado à plataforma Windows, por exemplo, não espere fazer um programa desenvolvido
com ele funcionar sob Linux ou Macintosh. Enfim, não podemos fazer tudo que desejarmos com esse tipo de
linguagem. Perceba que estamos de certa forma limitados.
As linguagens de alto nível são um pouco mais difíceis que Visual Basic, mas com uma linguagem deste
tipo, como C ou C++, aprendemos muito mais sobre programação e funcionamento dos computadores. Após
aprendermos uma linguagem deste tipo, somos capazes de aprender mais facilmente uma outra linguagem de
programação. O programador torna-se então mais autônomo. Em particular C e C++ são linguagens muito
populares. Elas são utilizadas para programar uma grande parte dos softwares mais conhecidos. Enfim,
programando em C ou C++, o programador está livre de comprar softwares caríssimos. Programar em C ou
C++ é, geralmente, gratuito.
Qualidades de um programador
Um programador deve ter certas qualidades:
• paciência: se um programa não funcionar da primeira vez, é preciso saber perseverar!
•
bom senso: não é preciso ser tão forte em matemática, mas isso não impede de se ter que refletir!
•
Calma: não se dá tapa no computador
. Isso não faz o programa funcionar.
Em resumo, não são necessários conhecimentos especiais para programar. Alguém fraco em matemática
pode até se sair bem, o essencial é a paciência para refletir.
O mínimo que deve estar disponível para um programador:
•
•
•
Um editor de texto para escrever o código fonte do programa. Em teoria um software como o Bloco
de Notas do Windows, “edit” em linha de comando do DOS ou o “vi” em Linux é suficiente. O ideal
é dispor de um editor de texto inteligente que colore o código, o que permite acompanhá-lo mais
facilmente.
Um compilador para transformar (compilar) o código fonte em binário.
Um depurador (debugger) para ajudar a encontrar alguns erros no programa, erros de utilização da
memória do computador (erros de lógica do programador não são detectados).
É comum utilizarmos um programa 3-em-1 que combina editor de texto, compilador e depurador. Esses
programas são também chamados de IDE "Integrated Development Environment". Exemplos de IDE para
programar em C: Bloodshed Dev-C++ e Code::Blocks.
Em nosso curso escreveremos os programas fonte no bloco de notas (notepad) se o sistema operacional for
Windows ou no KEdit (ou outro editor de texto que acompanhe a versão instalada) se o sistema operacional
for Linux. Isto nos restringe aos conhecimentos da linguagem de programação, o que é mais interessante
210
para quem está iniciando. Após conhecermos bem a sintaxe e a semântica da linguagem, usar uma interface
tipo IDE será fácil. Acredite.
3.2 Memória e variáveis
Para que tenhamos uma idéia, eis aqui diferentes tipos de memória existentes dentro de um computador, da
mais rápida para a mais lenta:
1. Os registros: uma memória ultra-rápida situada dentro do processador.
2. A memória cache: faz a ligação entre os registros e a memória RAM.
3. A memória RAM: a memória principal com a qual trabalhamos geralmente.
4. O disco rígido: que você conhece seguramente, é onde guardamos os arquivos para posterior uso.
Ao dizermos que uma memória é lenta, estamos considerando a escala de tempo de um computador. Afinal,
8 milissegundos são quase imperceptíveis aos humanos, mas é muito tempo para acessar um disco rígido,
por exemplo!
A memória principal
Fotografando de perto a memória principal (RAM) de um computador, não vemos grandes coisas. É mais
importante sabermos como ela funciona internamente.
Vejamos o esquema abaixo. Ele é bastante simples, mas é do necessitamos saber para programar.
Endereço
Valor
0
145
1
3.8028322
2
0.8272555
3
39014768
...
...
n-1
940.5118
Como podemos ver é preciso distinguir duas coisas:
• Endereços: um endereço é um número que permite ao computador se orientar na memória. Tudo
começa pelo endereço 0 (justamente no início da memória) e termina no endereço 2n-1, onde n é o
tamanho do barramento de endereços. Quanto mais memória existir no computador mais endereços
existirão e mais coisas poderão ali ser armazenadas.
• Em cada endereço podemos armazenar um único valor (um número): o computador armazena na
memória esses números para poder recuperá-los depois, quando necessário.
O endereçamento de memória é uma técnica que permite ao processador acessá-la. A interface utilizada para
isto é, na maioria das vezes, chamada barramento (BUS), um conjunto de fios dedicados a uma utilização
particular. No caso do acesso à memória, esse barramento é chamado de barramento de endereços.
A memória RAM só pode armazenar números. Como fazer então para armazenar outros caracteres (palavras,
por exemplo)? Boa pergunta. Na verdade, as letras não passam de números para os computadores! Uma frase
é uma simples sucessão de números. Existe uma tabela, denominada ASCII, que faz a correspondência entre
211
os números e as letras e outros caracteres. Essa tabela diz, por exemplo, que o número 65 corresponde à letra
A.
Variáveis
Em C, uma variável é constituída de dois atributos:
• Um valor: o número que ela armazena, por exemplo, 25.
• Um nome: que permite reconhecê-la. Programando em C não é preciso lembrar o endereço de
memória que guarda certo valor (ufa!), basta lembrar o nome da variável que contém aquele valor. O
compilador fará a conversão entre o nome da variável e o endereço onde ela está armazenada (ainda
bem).
Isso parece um pouco confuso no momento (qual o interesse de armazenar um número se é preciso lembrar o
seu endereço), mas, tudo vai tomar sentido em seguida.
3.3 Dando nome às variáveis
Em C cada variável deve ter um nome. Se uma variável guarda o valor da vida média de um cidadão, nós
bem que gostaríamos de chamá-la “vida média”. Infelizmente isso não é possível. Existem certas restrições.
Veja a seguir as regras que devemos seguir ao darmos um nome a uma variável.
•
•
•
•
Só podem existir letras (minúsculas ou maiúsculas), dígitos e “sublinha” _.
O nome de uma variável deve começar por uma letra.
Espaços são proibidos
Acentos não são permitidos (ãóôéàê etc).
Enfim, e isto é muito importante, a linguagem C (como C++) faz diferença entre as letras maiúsculas e
minúsculas. Então Brasil, bRasil e brasil são nomes que representariam três variáveis distintas. Veja alguns
exemplos de nomes válidos de variáveis:
Vidamedia, vida_media, vida_Media, VidaMedia, num_de_telefone, bola215, etc
Cada programador tem sua maneira própria de nomear as variáveis. Uma maneira bastante utilizada é:
• Começar os nomes de variáveis por maiúsculas.
• Se houver várias palavras no nome da variável, colocar uma maiúscula no início de cada palavra.
VidaMedia, por exemplo.
3.4 Tipos de variáveis
Um computador como se pode constatar nada mais é do que uma grande máquina de calcular. Ele só sabe
trabalhar com números. O problema é que existem vários tipos de números.
• Números inteiros positivos: 45; 578; 2457
• Números inteiros negativos: -87; -513
• Números decimais, isto é, números com vírgula: 1,7741; 9810,7
• Números decimais negativos: -98,45; -1,0045
Nosso pobre computador precisa de ajuda. Quando pedimos a ele para armazenar um número, devemos
dizer-lhe de que tipo é o número. Não é que ele não seja capaz de reconhecê-lo, mas isso o ajuda a organizarse e não perder tempo e espaço de memória com algo que poderia ser evitado. Quando declaramos uma
variável em um programa devemos então indicar o seu tipo. Vejamos os principais tipos de variáveis
existentes em C:
Nome do tipo Valores que podem ser armazenados
Char
-128 a 127
Int
-2 147 483 648 a 2 147 483 647
Long
-2 147 483 648 a 2 147 483 647
Float
-3.4 x 1038 a 3.4 x 1038
Double
-1.7 x 10308 a 1.7 x 10308
Esses não são todos os tipos, mas já dá pra começar.
212
Os três primeiros tipos permitem armazenar números inteiros (1, 2, 3, 4...).
Os dois últimos permitem armazenar números decimais (13.8, 16.911...).
Os tipos float e double permitem armazenar números bastante grandes. Se você não está habituado com as
potências de dez, então imagine que o tipo double permite armazenar o número 1 seguido por 308 zeros, isto
é, 100000000000000.... (eu não vou aqui escrever 308 zeros
)
Observemos que int e long são parecidos. Antes não era assim (um int era menor que um long), mas hoje as
memórias evoluíram e temos bastante espaço para escrever números grandes sem grandes preocupações com
a memória. Por razões de compatibilidade, a linguagem C guarda os dois tipos. Na prática utilizamos
principalmente os tipos char, long e double. Você verá que na maior parte do tempo manipularemos
números inteiros (tanto melhor, pois são mais fáceis de utilizar).
Atenção com os números decimais. O computador não conhece a vírgula decimal. Em seu lugar utilizaremos
o ponto. Não devemos escrever 273,45, mas 273.45!
Isso ainda não é tudo! Além dos tipos que armazenam números inteiros (char, int, long,...) já vistos, existem
outros ditos « unsigned » (sem sinal) que podem armazenar apenas números positivos. Para utilizá-los basta
escrever a palavra unsigned antes do nome do tipo.
unsigned char 0 a 255
unsigned int 0 a 4 294 967 295
unsigned long 0 a 4 294 967 295
A vantagem de utilizar tipos unsigned é que podemos armazenar números duas vezes maiores (char pára em
128 enquanto unsigned char vai até 255, por exemplo). Por que criar três tipos para os números inteiros? Um
só tipo não seria suficiente? Sim, mas no início foram criados vários tipos para economizar memória. A
tabela a seguir mostra o espaço necessário para armazenar os tipos.
Nome do tipo Número de bits
Char
8
Int
32
Long
32
Float
32
double
64
3.5 Declarando uma variável
Uma declaração de variável é muito simples, agora que você sabe tudo que é necessário. Basta indicar, na
ordem:
1.
2.
3.
4.
O tipo da variável que vai ser criada
Teclar espaço
Indicar o nome desejado para a variável
Enfim, não esqueça o ponto-e-vírgula
Por exemplo, se desejamos criar uma variável de nome TotalDePontos do tipo long, basta digitar a linha
seguinte:
long TotalDePontos;
Isso é tudo!
Outros exemplos:
long NotaDeFisica;
double TotalRecebido;
unsigned long NumeroDeEspacosNaLeituraDeUmTextoLongo;
Atenção! A declaração das variáveis deve ser feita no início das funções. O texto no quadro abaixo é um
código de programa escrito em C. Um programa em C é formado por vários trechos de código com um
213
formato especial denominado função. Todo programa possui uma função principal chamada main, essa é a
única no programa abaixo. Não se preocupe, aprenderemos aos poucos a escrever um programa.
#include <stdio.h>
int main(int argc, char *argv[])
{
// Início da função
long TotalDePontos;
system("PAUSE");
return 0;
// Fim da função
}
Se executarmos este programa ficaremos pasmos ao ver que
ele não faz nada.
Algumas explicações
Na verdade se passa alguma coisa, mas não é possível vê-la.
Quando a execução do programa chega à linha de declaração
da variável TotalDePontos ele « solicita » ao computador
permissão para utilizar um espaço de sua memória principal.
Se tudo vai bem, o computador responde « pois não, faça como
se estivesse em sua casa ». O único problema que poderia
acontecer seria não haver mais espaço disponível na memória do computador, mas isso será um caso
extremo. É possível declarar mais de uma variável em uma mesma linha do programa. Por exemplo, long a,
b, c;
Isso criará três variáveis de tipo long denominadas a, b e c.
3.6 Atribuindo valor a uma variável
É tudo que há de mais simples. Para atribuir o valor 8 à variável TotalDePontos basta escrever:
TotalDePontos = 8;
De modo geral, um comando de atribuição tem a forma: variável = expressão;
Nosso programa completo se parece agora com
#include <stdio.h>
int main(int argc, char *argv[])
{
// Início da função
long TotalDePontos;
TotalDePontos = 8 ;
system("PAUSE");
return 0;
// Fim da função
}
Nada é exibido na tela do computador até agora. Em algum lugar
da memória do computador o valor 8 é armazenado, esse lugar é
conhecido no programa pelo nome da variável TotalDePontos e
isso é tudo.
O valor de uma nova variável
Eis uma questão muito interessante. Quando se declara uma
variável, qual o seu valor inicial? De fato, quando o computador
lê a linha long TotalDePontos; ele reserva uma pequena parte da
memória para armazenar um valor do tipo long.
Mas que valor é esse? Existe um valor padrão (0, por exemplo)?
Bem, a resposta é não. Não existe um valor padrão. O computador reserva um espaço na memória para
armazenar um novo valor, mas não apaga o que já existia naquele espaço. Imediatamente a variável recebe o
valor que se encontrava no espaço que foi reservado, que pode ser qualquer coisa. Assim, o melhor é
inicializar as variáveis quando de suas declarações, sempre que possível. Procedemos assim:
long TotalDePontos = 8;
Agora a variável foi declarada e recebeu imediatamente o valor 8. Foi apagado o que poderia haver na
posição de memória que lhe foi alocada e colocado ali o valor 8.
3.7 Constantes
Ocorre às vezes que se tem a necessidade de armazenar um mesmo valor durante toda a execução de um
programa. O valor de pi (3.14), por exemplo. Isso quer dizer que uma vez declarada e inicializada, a variável
deverá conservar o seu valor impedindo que qualquer pessoa possa modificá-lo. Essas variáveis particulares
recebem o nome de constantes, por motivos óbvios. Para declarar uma constante em um programa deve-se
utilizar a palavra “const” antes do tipo e obrigatoriamente, inicializar a variável. Exemplo de declaração de
uma constante:
const long PI = 3.14;
Não é uma obrigação, mas por convenção, escreve-se os nomes das constantes com letras maiúsculas. Isso
nos permite distinguir facilmente os nomes de constantes dos de variáveis. Fora isso uma constante é
utilizada do mesmo modo que uma variável.
214
Ao tentar modificar o valor de uma constante no corpo de um programa o compilador acusará um erro (por
quê?).
3.8 Exibindo o conteúdo de uma variável
Utilizamos a função printf para exibir valores na tela do computador.
printf("Total de pontos = %d ", TotalDePontos);
O símbolo especial % seguido da letra d é um especificador de formato. Este especificador permite a
exibição de um número inteiro na tela do computador. Existem vários especificadores de formato que serão
visto mais tarde. Por enquanto usaremos os especificadores para números inteiros e números decimais.
Símbolo Significado
%d
Número inteiro (ex: 56)
%f
Número decimal (ex: 13.75)
Por enquanto saiba que, se quisermos exibir um número inteiro, precisamos utilizar o especificador %d e
para exibir um número decimal precisamos utilizar o especificador %f.
Os especificadores de formato são geralmente utilizados dentro de um texto explicativo, como no exemplo
anterior. É preciso, entretanto indicar para a função printf qual o valor a ser exibido. Para isso é preciso
escrever o nome da variável que contém esse valor após o texto contendo o especificador, após uma vírgula.
Vejamos outros exemplos:
printf("O valor de a eh %d ", a1);
printf("Foram encontrados %d resultados ", r);
No primeiro exemplo %d indica que um valor inteiro deve ser exibido na tela do computador e esse valor
está armazenado em a1. No segundo exemplo esse valor está armazenado na variável r.
Por enquanto veremos os programas prontos e aprenderemos cada vez mais sobre suas estruturas. Pelo que
temos visto podemos tirar algumas conclusões:
• Um programa começa sempre com a instrução #include<stdio.h>, isto indica que uma biblioteca de
funções de nome stdio deve ser incluída automaticamente no programa e isto é feito, não se
preocupe.
• O cabeçalho da função principal será sempre int main().
• Após // vem um comentário, que não é executado, serve apenas de documentação.
• system("PAUSE"); fixa a tela de modo que possamos ver a resposta.
• return 0 indica um fim normal do programa.
#include <stdio.h>
int main()
{
long TotalDePontos = 5;
// No início o alunos possui 5 pontos
printf("Voce possui %d pontos \n", TotalDePontos);
printf("**** H U M M M ****\n"); // O aluno errou uma questão
TotalDePontos = 4;
// Ele acaba de perder um ponto!
printf("Ah, agora voce so tem %d pontos!\n\n", TotalDePontos);
system("PAUSE");
return 0;
}
Este programa produz a seguinte saída:
Voce possui 5 pontos
**** H U M M M ****
Ah, agora voce so tem 4 pontos!
Pressione uma tecla para continuar...
215
Exibir várias variáveis em um único printf
É possível exibir o valor de várias variáveis em um único printf. Para tanto, é suficiente indicar %d ou %f
onde se deseja que os valores sejam impressos e em seguida indicar as variáveis que contêm esses valores,
obedecendo à ordem em que os especificadores foram indicados, separadas por vírgula.
Exemplo.
printf("Voce tem %d pontos e faltam %d perguntas", TotalDePontos, p);
3.9 Recuperando uma entrada
As variáveis vão começar a ficar mais interessantes agora. Vamos solicitar ao usuário (aquele que está
operando o computador) que digite um número. Vamos aprender a recuperar essa entrada e a armazenar em
um determinado lugar na memória principal. Para solicitar ao usuário que entre com uma determinada
informação pelo teclado utiliza-se a função scanf. Essa função se parece com a função printf. Deve-se
colocar um %d ou %f, entre aspas, para indicar um inteiro ou um decimal a ser digitado.
Veja um exemplo.
scanf("%d", &idade);
Coloca-se apenas %d (ou %f) entre as aspas. Não esquecer de colocar o símbolo & antes do nome da
variável que vai receber o valor. E por que esse & antes do nome da variável?
É preciso que você acredite em mim. Eu explicarei isso mais tarde. Não o faço agora para não embolar o
meio de campo. Quando o programa encontra um scanf ele faz uma pausa e espera que o usuário digite um
valor. Esse valor será armazenado na variável idade. Veja um pequeno exemplo.
#include <stdio.h>
int main()
{
long idade = 0; // inicializa a variável com 0
printf("Qual a sua idade? ");
scanf("%d", &idade); // Solicita a entrada de idade com scanf
printf("Ah ! Voce tem %d anos!\n\n", idade);
system("PAUSE");
return 0;
}
Saída:
Qual a sua idade? 20
Ah! Voce tem 20 anos!
Pressione uma
continuar...
tecla
para
Pronto! Você compreendeu o
princípio. Graças à função scanf
o computador pode interagir
com o usuário.
Observe que nada nos impede de digitar outra coisa que um número inteiro:
• Se digitarmos um número decimal como 34.56, por exemplo, ele será truncado imediatamente e só a
parte inteira é armazenada. Neste caso, 34 é armazenado na variável idade.
• Se digitarmos letras, a variável não muda de valor. Permanece então com o valor 0 (valor com que
foi inicializada). Se a variável não tivesse sido inicializada, o programa imprimiria qualquer coisa.
Percebem a importância da inicialização das variáveis?
3.10 Testes
1. Quando se declara uma variável, qual memória é utilizada?
( ) Registros
( ) Memória cache
( ) Memória RAM
( ) Disco rígido
2. Qual memória não é “apagada” quando o computador é desligado?
( ) Registros
( ) Memória cache
( ) Memória RAM
( ) Disco rígido
216
3. Qual desses não é um nome válido de variável?
( ) scanf
( ) coordenadaDaJanela
( ) desconto_total
4. Qual dos tipos de dado abaixo permite armazenar o número 15.3?
( ) char
( ) long
( ) double
( ) int
5. Qual dos tipos abaixo permite armazenar o número - 1458 ?
( ) long
( ) unsigned int
( ) unsigned double
6. Se a variável “SaldoBanco” é um long que vale 150345 reais (pensemos grande), o que a linha de código
printf("Voce tem %d reais na conta ", saldoBanco); exibirá na tela?
( ) Voce tem %d reais na conta
( ) Voce tem 150345 reais na conta
( ) Voce tem d reais na conta saldoBanco
7. Para armazenar um número decimal digitado no teclado, qual das linhas de código abaixo é a boa?
( ) scanf("%f", num);
( ) scanf("%d", num);
( ) scanf("%f", &num);
( ) scanf("%d", &num);
4. Avaliando o que foi construído
Foram apresentados alguns conceitos básicos necessários para a construção de um programa: memória,
variáveis e constantes, além das instruções capazes de realizar as operações de entrada e saída desses
elementos de programação. Uma operação de entrada consiste em receber um valor via teclado e armazenálo na memória principal do computador. A operação correspondente à saída consiste em exibir, geralmente
na tela do computador, um valor que esteja armazenado em uma variável. Vimos também que as linguagens
utilizadas pelos programadores atualmente são classificadas em um nível tão mais alto quanto mais afastadas
estiverem da codificação binária. Assim é que linguagens como C, Pascal, Ada, Java, PHP, etc., são
denominadas de alto nível.
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. MGraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
217
Unidade IV - Operações Aritméticas Básicas
1. Situando a temática
Nesta unidade aprenderemos a realizar a maior parte dos cálculos que um computador pode executar. A idéia
é retomar o conceito de variáveis visto na unidade anterior e realizar as operações básicas que as envolvem
como somar, multiplicar, permutar, etc.
2. Problematizando a temática
Já sabemos que o computador é uma calculadora muito potente com a qual podemos realizar as seguintes
operações básicas com muita simplicidade:
• Adição
• Subtração
• Multiplicação
• Divisão
• Módulo (eu explicarei o que é isto)
Quando desejamos fazer outras operações mais complicadas, temos que as programar, isto é, explicar ao
computador como fazê-las. Felizmente existe uma biblioteca de funções matemáticas já prontas que
acompanha a linguagem C. Neste caso não é necessário programar as operações, mas simplesmente conhecer
a interface das funções correspondentes, ou seja, saber como utilizá-las.
3. Conhecendo a temática
3.1 Primeiros passos
Vamos começar pela adição. Para fazer uma adição se utiliza o símbolo + (sem brincadeira). O resultado de
um cálculo, geralmente, deve ser armazenado em uma variável.
Atenção! Na maioria das vezes, representaremos apenas a parte do programa fonte que estiver sendo
discutida. Lembre-se de completar o código do programa começando por
#include <stdlib.h>
#include <stdio.h>
..............................
Vejamos um exemplo de um trecho de programa que realiza uma soma.
..............................
long resposta = 0;
..............................
resposta = 13 + 7;
Não é necessário ser um gênio em matemática para saber que a variável “resposta” armazenará o valor 20
após a execução desse trecho de programa. Claro que nada é exibido na tela como resposta. Para ver o
resultado na tela, como já sabemos, é necessário utilizar a função printf. Em algum lugar do programa
poderíamos ter printf(“17 + 3 = %d”, resposta);. O resultado na tela seria 17 + 3 = 20.
Isso é tudo para a adição. Para as outras operações o procedimento é o mesmo apenas o símbolo do operador
muda. Os operadores são representados pelos símbolos:
• Adição: +
• Subtração: • Multiplicação: *
• Divisão: /
• Módulo: %
As três primeiras operações são realizadas de modo natural, conhecido por quase todos. Apenas as operações
de divisão e de módulo merecem um pouco de reflexão, pois existem algumas considerações específicas
relacionadas com os tipos envolvidos.
218
3.2 A divisão
As divisões funcionam normalmente em um computador quando não sobram restos. Por exemplo, 8/2 é 4,
mas 7/2 que deveria ser 3.5 é 3 e sobra um resto 1. Observe.................................
long resposta = 0;
.............................
resposta = 7/2;
printf ("7 / 2 = %d", resposta);
..............................
Saída.
7/2=3
Eis aqui um problema. Sabemos que 7/2=3.5, mas o resultado apresentado foi 3. Ocorre que a variável
“resposta” é do tipo long, portanto inteiro, por isso o computador truncou o resultado ficando apenas com a
sua parte inteira. Se “resposta” tivesse sido declarada como float ou double o resultado seria 3.500000. Faça
as modificações no programa fonte e execute-o novamente para ver o resultado.
Se quisermos que o computador mostre o bom resultado (3.5) será necessário transformar os números 7 e 2
para decimais. Isso é feito escrevendo-se 7.0 e 2.0 (são os mesmos números, mas agora o computador vai
entendê-los como números decimais e, portanto efetuará uma divisão de decimais). Na verdade, basta
escrever um dos números no formato decimal (com ponto decimal). A linguagem C reconhecerá a expressão
como sendo do tipo decimal e efetuará a operação corretamente.
.....................................
double resposta = 0;
....................................
resposta = 7.0 / 2.0;
printf ("7 / 2 = %f", resposta);
......................................
Saída
7 / 2 = 3.500000
Observe que o especificador de formato utilizado foi %f, para números decimais. Mas atenção, às vezes se
deseja realmente que o resultado seja um valor inteiro, nesse caso o resultado inicial (3) estaria correto.
Esta propriedade dos números inteiros é muito importante. Tenha sempre em mente que 5/2=2, 8/5=1, 6/7=0,
etc. Para obter um resultado decimal é necessário que os operandos sejam números decimais: 5.0/2.0=2.5,
8.0/5.0=1.6, 6.0/7.0=0.857143, etc.
De fato, na divisão 5/2, o computador responde à questão “quantas vezes o número 2 está dentro do número
5?” Resposta, 2 (duas vezes).
Você agora deve estar se perguntando “como fazer então para recuperar o resto da divisão”? Esse é o nosso
próximo passo.
3.3 Primeiros passos
O módulo é uma operação matemática que permite se obter o resto da divisão de dois números inteiros.
Talvez seja uma operação menos conhecida que as anteriores, mas de grande importância nos cálculos
realizados por computadores. Como mostrado anteriormente, o operador de módulo é representado por %.
Veja os exemplos:
• 7%2=1
ler: o resto da divisão de 7 por 2
• 28 % 5 = 3
ler: o resto da divisão de 28 por 5
• 10 % 2 = 0
ler: o resto da divisão de 10 por 2
Ao executar a operação 7%2, o computador calcula o resto da divisão de 7 por 2, que é 1. A operação 10%2
fornece como resultado 0, pois esse é o resto da divisão de 10 por 2, ou seja, a divisão é exata.
219
3.4 Cálculo entre variáveis
O interessante, agora que sabemos as cinco operações básicas, é utilizá-las entre várias variáveis. Nada nos
impede de escrever, resposta = num1 + num2, por exemplo, onde num1, num2 e resposta são variáveis
declaradas. Esta linha, obviamente faz a soma das variáveis num1 e num2 e coloca o resultado na variável
resposta.
Agora as coisas começam a ficar mais interessantes. Você já é capaz de fazer coisas espantosas, como uma
calculadora. Creia-me. Imagine um programa que solicita ao usuário a entrada de dois números. Esses dois
números deverão ser armazenados em variáveis. Em seguida escreva uma expressão que calcule a soma
desses números e armazene o resultado em uma variável chamada resposta. Então exiba o resultado obtido
na tela do computador. Experimente escrever esse programa sozinho.
Uma possível resposta seria:
#include <stdio.h>
#include <stdlib.h>
int main()
{
long resposta = 0, num1 = 0, num2 = 0;
// ** solicitar num1 e num2 ao usuário **
printf("Entre com o valor de num1: ");
scanf("%d", &num1);
printf("Entre com o valor de num2: ");
scanf("%d", &num2);
// ********* Calcular ************
resposta = num1 + num2;
// ** Mostrar o resultado na tela **
printf ("%d + %d = %d\n", num1, num2,
resposta);
system("PAUSE");
return 0;
}
Saída
Entre com o valor de num1: 52
Entre com o valor de num2: 30
52 + 30 = 82
Experimente agora seguir o mesmo procedimento para que a sua calculadora execute as outras operações
básicas, ou seja, modifique o programa acima para que ele execute as operações de subtração, multiplicação
e divisão.
3.5 Abreviações: incremento e decremento
Existem em C técnicas que permitem abreviar a escrita de certas operações. Essas abreviações têm por
objetivo agilizar a digitação e tornar o código mais conciso.
Incremento
Quase sempre, um programador se depara com um problema bem simples, mas de grande importância:
aumentar de 1 o valor de certa variável.
Imagine que um programa trabalhe com uma variável denominada Total e que se deseje aumentar de 1 o
valor dessa variável. Como fazê-lo se o seu valor atual não é conhecido?
Solução: Total = Total + 1;
O que se passa em um computador ao encontrar uma instrução como esta? Bem, ele sempre avaliará a
expressão que se encontra do lado direito da instrução de atribuição (representada pelo símbolo =) e
armazenará o seu resultado na variável que se encontra do lado esquerdo. Assim, na unidade de aritmética e
lógica (UAL), ele somará 1 ao valor atual da variável Total e substituirá o valor armazenado em Total pelo
valor da expressão calculada.
Na memória
Na UAL
Na memória
total
total
Antes
30
30+1
220
Depois
31
Observe que nessa instrução escreve-se duas vezes o nome de uma mesma variável (Total). Os
programadores achando isso muito cansativo resolveram então abreviá-la. A instrução seguinte faz
exatamente a mesma coisa que a anterior e escreve-se menos: Total++;
Este recurso é muito usado nos programas escritos em C. Você terá oportunidade de comprovar este fato.
Decremento
Esta operação é o inverso daquela denominada incremento. Trata-se de retirar 1 do valor de uma variável.
Então, de modo análogo à operação de incremento tem-se: Total = Total - 1; equivalente a Total--;
3.6 Outras abreviações
Existem outras abreviações que funcionam sob o mesmo princípio que o de incremento (decremento) visto
anteriormente, porem de forma mais geral. Essas abreviações funcionam para todas as operações básicas. A
tabela abaixo mostra essas abreviações.
Operação
total = total +5
total = total -5
total = total *5
total = total /5
total = total %5
Abreviação
total += 5
total -= 5
total *= 5
total /= 5
total %= 5
Exemplo. Supor total = 5 inicialmente.
long total = 5;
total += 4; // total vale 9
total -= 3; // ... total vale 6
total *= 5; // ... total vale 30
total /= 3; // ... total vale 10
total %= 3; // ... total vale 1(pois 10=3*3+1)
A vantagem aqui é que podemos utilizar todas as operações básicas e podemos somar, multiplicar, etc por
qualquer número. Essas abreviações são importantes quando temos operações repetitivas com essa estrutura
em um programa. Todavia, o incremento é a abreviação mais utilizada.
3.7 A biblioteca matemática
Em C existe um conjunto de funções já prontas para uso bastando apenas ao usuário compreender as suas
interfaces, o meio de comunicação do programa com essas funções. Esse conjunto de funções foi escrito por
outros programadores e servem, de certo modo, para evitar que os programadores atuais reinventem a roda a
cada novo programa.
Já utilizamos as funções printf et scanf da biblioteca stdio.h. Existem outras bibliotecas de programas, dentre
elas uma denominada math.h, que contém diversas funções matemáticas já prontas, isto é, já programadas.
Tenha em mente, por exemplo, que a linguagem C não sabe calcular potência. Se escrevermos 43 o
computador não saberá executar essa operação, pois ele não sabe o que isso significa. Para a realização desse
cálculo é necessário indicar no início do programa a inclusão de uma biblioteca que contenha a função
responsável por ele.
Essa indicação é feita por: #include <math.h>
Uma vez feita essa indicação, podemos utilizar todas as funções dessa biblioteca. As principais funções
existentes na biblioteca math são:
221
fabs
Esta função retorna o valor absoluto de um número, isto é, |x| (sua notação matemática).
O valor absoluto de um número é o seu valor positivo:
• Ao passar o valor -78 para a função, ela retornará 78.
• Ao passar o valor 175 para a função, ela retornará 175.
........................................................
double absoluto = 0, x=-42;
absoluto = fabs(x); // absoluto receberá o valor 42
........................................................
A função fabs retorna um valor double por isso devemos declarar a variável absoluto (aquela que vai receber
o retorno da função) como sendo do tipo double. Existe uma função similar a fabs na biblioteca "stdlib.h", é
a função abs. Essa função funciona do mesmo modo que fabs, mas somente para valores inteiros. Devemos
então declarar a variável que vai receber o retorno da função como sendo do tipo int.
ceil
Esta função retorna o menor número inteiro maior que o número decimal que lhe é passado como argumento.
Por exemplo, ao passar 18.33 como argumento para a função ela retorna 19. Esta função retorna um valor do
tipo double.
.........................................................
double teto = 0, valor1 = 18.33;
teto = ceil(valor1);
// teto valerá 19
//Programa floor, ceil
#include <stdio.h>
floor
Esta função funciona como a
int main(void)
precedente, mas retorna o
{
maior inteiro menor do que o
double base=0, valor2=18.99;
valor decimal passado como
float teto=0, valor1=18.33;
argumento. Por exemplo, ao
teto=ceil(valor1);
passar 18.99 como argumento
base=floor(valor2);
para a função ela retorna 18.
printf("Base = %f\n", base);
Vejamos um programa que
printf("Teto = %f\n", teto);
exemplifica as duas funções.
printf("Tamanho de base (em bytes) = %d\n", sizeof(base));
printf("Tamanho de teto (em bytes) = %d\n", sizeof(teto));
getch();
return 0;
}
Outras funções.
função
pow(n, p)
sqrt(n)
argumentos
(double|float|int, double|float|int)
(double|float|int)
sin(x)
cos(x)
tan(x)
asin(x)
acos(x)
atan(x)
exp(x)
em radianos
em radianos
em radianos
double
double
double
double
log(x)
log10(x)
double
double
retorna
double|float|int (np)
double ( n )
double (seno de x)
double (cosseno de x)
double (tangente de x)
double (arco seno de x)
double (arco cosseno de x)
double (arco tangente de x)
double ( e x )
double (loge x)
double (log10 x)
3.8 Testes
1. Qual o símbolo de multiplicação na linguagem C?
( )*
( )+
( )/
( )-
222
( )%
2. Qual o resultado da operação 17 % 5?
( )0
( )1
( )2
( )3
( )4
( )5
( ) 15
3. Qual o valor da variável resultado após a instrução resultado = (8 / 3) - 2;?
( ) -2 ( ) 0
( )1
( )2
4. Qual o nome da operação seguinte em programação? x++;
• ( ) incremento
• ( ) argumento
• ( ) suplemento
5. Qual o valor da variável “numero” após as operações seguintes?
long numero = 4;
numero--;
numero *= 4;
numero %= 12;
numero += 1;
(
(
(
(
(
)0
)1
)4
) 12
) 14
6. Qual das funções abaixo se deve utilizar para que 13.25 seja arredondado para 14?
( ) pow
( ) ceil ( ) floor
( ) sqrt
4. Avaliando o que foi produzido
Essas são as operações e funções que provavelmente todos os programadores mais utilizam ao trabalharem
com as operações aritméticas. A sua compreensão é importantíssima, pois elas formam a base de programas
mais complexos. Teremos oportunidade na seqüência deste curso de utilizá-las como operações elementares.
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. MGraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
223
Unidade V- Praticando Entrada e Saída
1. Situando a temática
As funções que realizam as operações de entrada ou saída não pertencem à linguagem C. A biblioteca padrão
de entrada/saída “stdio” contém tudo que é necessário para as operações de entrada ou saída em um
programa C. Essa biblioteca é formada por um conjunto de funções que fornecem à linguagem um sistema
de interfaces cômodas para a realização das operações de entrada ou saída.
2. Problematizando a temática
As funções mais utilizadas para entrada ou saída são:
scanf
printf
Lê valores de variáveis a partir da entrada padrão, geralmente o teclado.
Escreve sobre a saída padrão. Na ausência de indicação em contrário,
essa saída é a tela do computador.
Sempre que um programa precisar de dados iniciais vindos do mundo exterior, esses deverão ser inseridos no
programa por uma função de leitura. É isso que faz a função scanf. De modo análogo, os dados produzidos
como resultado de processamento interno são exibidos, enviados para o mundo exterior, via a função printf.
Vejamos alguns detalhes sobre essas funções.
3. Conhecendo a temática
3.1 Algumas considerações sobre a função scanf
Consideremos o comando scanf(“%d”, &n);. Trata-se aqui da leitura de um valor que deverá ser
armazenado na variável n. Para administrar corretamente a variável n, a função scanf precisa conhecer o seu
tipo. No exemplo acima, o primeiro parâmetro da função, representado pela seqüência de caracteres “%d”
indica ao compilador C que a variável é de tipo inteiro (int). Se a variável n pertencesse a um outro tipo,
outro caractere seria usado no lugar de d. Essa seqüência de caracteres é conhecida pelo nome “especificador
de formato”. O segundo parâmetro da função (&n) indica o endereço na memória do computador, onde a
variável n será armazenada. O computador se encarrega de calcular esse endereço. O programador apenas
deve escrever o parâmetro &n para que o computador saiba que no programa o valor armazenado naquele
endereço será conhecido por n. O endereço de uma variável x é representado, portanto, pela expressão &x.
3.2 Algumas considerações sobre a função printf
O primeiro argumento dessa função é sempre uma seqüência de caracteres. Explicamos o funcionamento
dessa função com a ajuda de alguns exemplos.
1.
printf(“bom dia”);
• Quando essa instrução é encontrada, a função printf se encarrega de escrever na tela do
computador a seqüência bom dia.
2.
printf("Eh agradavel escrever um programa "
"em C, \nquando o utilizamos \"corretamente\".\n");
• As aspas no fim da primeira linha e no início da segunda permitem escrever uma seqüência de
caracteres em mais de uma linha. Esse comando produz como saída:
Eh agradavel escrever um programa em C,
quando o utilizamos "corretamente".
• A seqüência de caracteres \n diz ao compilador para executar um salto de linha (passar para a
próxima linha da tela)
• A seqüência de caracteres \" indica o uso de aspas. Nesse caso as aspas farão parte da saída que
será impressa.
224
3.
printf(“%d”, 5);
• A função printf substitui o especificador de formato %d pelo primeiro parâmetro situado após a
vírgula, neste caso o inteiro 5. Lembre que o caractere d em um especificador de formato
representa um número inteiro.
Seja o programa
#include <stdio.h>
int main()
{
int altura=185;
float peso=80.5;
char nome[10]="Joao";
printf("%s mede %d cm e pesa %f kg", nome, altura, peso);
getch();
return 0;
}
4.
printf("%s mede %d cm e pesa %f kg", nome, altura, peso);
• Supor que as variáveis nome, altura e peso refiram-se a uma pessoa e valham a seqüência de
caracteres “João”, o inteiro 185 e o número real 80.5, respectivamente. A função printf substitui os
especificadores de formato respeitando a ordem em os mesmos se encontram na instrução. Assim
%s é substituído pelo primeiro parâmetro situado após a vírgula, neste caso a seqüência João, %d é
substituído por 185 e %f por 80.5. Note que %s é o especificador de formato para seqüência de
caracteres e %f o especificador de formato para números em ponto flutuante (números com ponto
decimal).
Observemos o seguinte comando:
5.
printf("os valores sao \n%5d\n%5d\n%.3f\n", v1, v2, v3);
• v1 e v2 são duas variáveis de tipo int que contêm os valores 4 e 256 e v3 é uma variável de tipo
float contendo o valor 7.54756, por exemplo. As indicações numéricas permitem precisar o modo
como esses valores são mostrados na tela.
• %5d: exibe o valor correspondente, um valor inteiro, ocupando 5 posições preenchidas a partir da
direita.
• %.3f: exibe o valor correspondente como uma variável de tipo float com 3 casas decimais, com
arredondamento.
A saída é então:
00004
00256
7.548
getch(): aguarda a próxima tecla ser pressionada. Isto permite ao usuário ver na tela o resultado exibido pelo
programa.
A instrução return 0; ao fim da função main permite enviar o valor 0 ao sistema o que significa, por
convenção, que o programa terminou normalmente. O envio desse valor é obrigatório em C, pois a função
main é declarada como sendo do tipo int, logo espera um valor desse tipo.
3.3 Vamos praticar um pouco
Digitemos o programa abaixo. Antes de executá-lo, procure entender como serão a sua entrada e a sua saída.
O objetivo é que ele seja auto-explicativo.
225
/* Este programa lê 3 valores inteiros */
#include<stdio.h>
int main()
{
int a,b,c;
printf("digite 3 numeros inteiros separados por espaco\n");
scanf("%d %d %d",&a,&b,&c);
printf("\n valor de a = %d",a);
printf("\n valor de b = %d",b);
printf("\n valor de c = %d\n ",c);
getch();
return 0;
}
Alguns comentários.
- algumas palavras estão sem acentuação, pois isto não é permitido na linguagem C, salvo nos comentários;
- o texto entre os símbolos /* e */ é um comentário e, portanto, não será compilado;
- a primeira chamada à função printf serve para orientar o usuário sobre o que ele deve fazer quando da
execução deste programa (o que ele deve fazer mesmo?). Experimente retirar esse comando do programa.
Ele funcionará corretamente, mas o usuário saberá o que o computador estará esperando como entrada de
dados?
- na instrução scanf("%d %d %d",&a,&b,&c); os especificadores de formato (%d) podem ser separados por
espaço ou não, isto não tem influência no programa;
- a cada especificador de formato na função scanf corresponde, em ordem, um argumento a ser lido do
teclado;
- as próximas chamadas à função printf servem para imprimir (exibir na tela do computador) os valores das
variáveis a, b e c;
- no início de cada chamada a printf há uma ordem para o computador mudar de linha (\n);
- getch() permite que o usuário veja o resultado na tela (fixa a tela de execução do programa). Experimente
retirar esse comando do programa e tire suas próprias conclusões.
Na plataforma Moodle você encontrará vários exercícios que
permitirão consolidar a aprendizagem sobre entrada e saída de
dados. Essas operações são importantíssimas e só a prática
leva um programador a se sentir à vontade em sua utilização.
Então, mãos à obra.
4. Avaliando o que foi construído
As operações de entrada e saída são básicas no desenvolvimento de um programa. São elas que
implementam a interface entre o homem e a máquina. Se por um lado é preciso alimentar o computador com
dados do mundo exterior a fim de que ele possa realizar alguns processamentos indicados no algoritmo
implementado como solução de um problema, por outro precisamos ver os resultado obtidos. Bem formatar
esses procedimentos de entrada e saída implica em apresentar um trabalho legível e compreensível. De nada
adianta um programa que tenha um bom desempenho nos seus procedimentos internos se não somos capazes
de alimentá-los de forma adequada e amigável, com facilidade.
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. Mcgraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
226
Unidade VI - Estrutura de Controle Condicionais
1. Situando a temática
Vimos anteriormente que existem várias linguagens de programação. Algumas delas se parecem muito. A
linguagem PHP, por exemplo, foi inspirada na linguagem C. A propósito, a linguagem PHP é muito utilizada
na criação de páginas dinâmicas na Internet. Não é o caso da linguagem C.
Dando continuidade aos conceitos básicos de programação, veremos agora os condicionais, também
conhecidos como « condição » ou « seleção ». Esse conceito é utilizado para selecionar que trecho de um
programa deve ser executado sob certas condições. Sem as condições os programas de computador teriam
um comportamento "retilíneo", isto é, não seria possível a realização de determinadas operações que
envolvem testes de variáveis.
2. Problematizando a temática
Uma condição é o resultado de um teste de comparação. Imaginemos que um programa deve executar a
função A ou a função B de acordo com o valor da média aritmética calculada a partir de três notas. Se o valor
da média for maior ou igual a 7, então deve ser executada a função A, caso contrário deve ser executada a
função B. Assim, em que condição deve ser executada a função A? Obviamente essa função deverá ser
executada se o resultado da comparação entre a média calculada e o valor 7 for "a média é superior ou igual a
7".
Os símbolos de comparação utilizados na linguagem C são listados na tabela abaixo.
Símbolo Significado
==
é igual a
>
é superior a
<
é inferior a
>=
é superior ou igual a
<=
é inferior ou igual a
!=
é diferente de
Atenção especial é necessária para a comparação de igualdade. Note que o
símbolo de comparação é formado por dois símbolos de atribuição (==). Um
erro comum entre os iniciantes consiste em fazer essa comparação utilizando
um único símbolo de atribuição, o que evidentemente não é um teste de
igualdade em C.
As condições são representadas em C por estruturas de controle ditas
estruturas de seleção, implementadas pelas instruções if e switch.
3. Conhecendo a temática
3.1 A estrutura if ... else
O teste if
Este teste consiste em realizar uma operação de comparação e indicar o que deve ser feito quando o resultado
da comparação é verdadeiro. Isto é codificado assim:
onde expr é uma expressão de comparação. A parte do programa escrita entre
as chaves { e } é denominada um bloco de instruções, também chamada
simplesmente bloco. A semântica dessa instrução é: se o valor da expressão
expr for True (verdadeiro), então o computador deverá executar o bloco de
instruções pertencente à instrução e, em seguida, executar a próxima
instrução após o if. Caso o valor da expressão condicional seja False (falso),
a próxima instrução após o if será executada.
Graficamente, a semântica deste teste é representada assim:
if (expr)
{
Bloco de instruções
}
próxima instrução
.....
expr
True
False
próxima instrução
227
Bloco
Problema1. Testar a variável idade, correspondente à idade de uma pessoa. Se o valor dessa idade for maior
do que ou igual a 18, imprimir uma mensagem informando esse fato.
if (idade>=18)
{
printf(˝ voce eh maior de idade ˝) ;
}
próxima instrução;
Observação. Quando há somente uma instrução entre as
chaves, estas se tornam facultativas. Pode-se então escrever:
if(idade>=18)
printf(˝ voce eh maior de idade ˝) ;
próxima instrução;
#include <stdio.h>
int main()
{
long idade = 25;
if (idade >= 18)
{
printf("Voce eh maior de idade!\n");
}
system("PAUSE"); // ou getch()
return 0;
}
Eis ao lado um código completo (código1) que pode ser
testado.
Este código pode servir de base para testar os exemplos
dos próximos testes. Não deixe de fazê-los.
Uma questão de legibilidade
A disposição das chaves não é muito importante. O programa funcionará também se você escrever assim
if(idade>=18) {printf("Voce eh maior de idade!\n");}, mas vemos claramente que a disposição anterior é
mais legível. Essa é a recomendação do estilo C.
O teste else – o uso de else para dizer "caso contrário"
Agora que sabemos fazer um teste simples, é hora de ir um pouco mais longe. Se o teste não funcionar, isto
é, se o resultado da avaliação da expressão de comparação for False e a solução que está sendo codificada
exigir que seja dito explicitamente ao computador o que fazer, trata-se de outro tipo de teste: if ... else. Para
codificar este teste basta adicionar após o bloco correspondente ao resultado True, outro bloco, agora
precedido da palavra reservada else. Esse bloco é conhecido como bloco else. Por exemplo,
Se o resultado da expressão de comparação for verdadeiro
(True), será executada a instrução printf("voce eh maior
de idade") e a execução do programa fará um salto para
"próxima instrução". Se o resultado for falso (False), será
executada a instrução printf("voce eh menor de idade") e a
execução do programa continuará em "próxima
instrução". Observe que apenas um dos blocos de
instruções é executado. A execução de um impede a
execução do outro.
Relembrando! De modo geral, o que é executado quando
o resultado da expressão de comparação é verdadeiro
denomina-se Bloco True e o que é executado quando esse resultado é falso, denomina-se Bloco False. Um
bloco é constituído por uma ou mais instruções. Quando um bloco possui apenas uma instrução, o uso das
chaves é facultativo.
if (idade>=18)
{
printf("voce eh maior de idade ") ;
}
else
{
printf("voce eh menor de idade") ;
}
próxima instrução;
Graficamente a semântica deste teste é representada assim:
....
expr
True
False
Bloco
False
próxima instrução
228
Bloco
True
O teste else if ... – para dizer "caso contrário, se ..."
Quando o primeiro teste não funcionar e a situação exigir, Bloco False poderá conter outro if ... else.
Vejamos o exemplo a seguir.
if (idade >= 18)
{
printf("voce eh maior de idade");
}
else if ( idade >= 12 )
{
printf("voce eh um adolescente");
}
else
{
printf("Paciencia, voce ainda eh uma criança");
}
próxima instrução
O computador faz os testes em ordem.
1. se a expressão (idade>=18) é avaliada True, a
instrução printf("voce eh maior de idade"); é
executada e o controle da execução passa
para ˝próxima instrução˝.
2. se a expressão (idade>=18) é avaliada False,
o Bloco Else é executado. Notar que esse
bloco é um if ... else. Assim, a segunda
expressão de comparação (idade > 12) é
avaliada. Se True, printf("voce eh um
adolescente"); é executada. Se False,
printf("Paciencia, voce ainda eh uma
criança"); é executada. Em qualquer dos
casos, após a execução de printf, “próxima
instrução” será executada.
Atenção! O ˝else˝ e o ˝else if˝ não são obrigatórios, mas geralmente facilitam o modo de escrever e tornam o
programa mais legível.
Graficamente a semântica deste teste é representada assim:
.....
expr
True
Bloco
True
True
Bloco
True
False
expr
False
Bloco
False
próxima
instrução
Problema2. Ler um valor inteiro n e imprimir a mensagem "n pertence ao
intervalo i", onde i = 1, 2, 3 ou 4 de acordo com a tabela ao lado.
Uma solução pode ser o código abaixo. Observar o modo como é feito o teste de
pertinência a um intervalo. Este não é o único modo, logo serão vistos outros.
Procure entender bem o funcionamento desses testes, pois esse entendimento é
indispensável para a seqüência do curso.
229
i
1
2
3
4
intervalo
n<0
0 ≤ n < 20
20 ≤ n <100
n >= 100
#include <stdio.h>
int main()
{
long n;
printf("Digitar um valor inteiro. \n");
scanf("%d",&n);
printf("valor de n = %d\n",n);
/**** testar a pertinência a um intervalo ****/
if(n<0) // n pertence ao intervalo 1?
{printf("%d pertence ao intervalo 1\n",n);}
else if(n<20) // n pertence ao intervalo 2?
{printf("%d pertence ao intervalo 2\n",n);}
else if (n<100) // n pertence ao intervalo 3?
{printf("%d pertence ao intervalo 3\n",n);}
else {printf("%d pertence ao intervalo 4\n",n);}
printf("Saida do IF\n");
system("pause"); // ou getch();
return 0;
}
Teste com várias condições
Para realizar testes com várias condições, é necessário o emprego de alguns conectivos lógicos.
Consideremos uma afirmação que, sem ambigüidade, apenas pode ser verdadeira ou falsa. Uma afirmação
desse tipo é denominada uma proposição. Pois bem, os conectivos lógicos são empregados na realização de
operações sobre proposições. Alguns exemplos de proposições:
• 3 é maior do que 2
• 5 é maior do que 10 (não precisa ficar assustado, esta é uma proposição falsa)
• Há vida fora da Terra (mesmo que não se consiga provar, o resultado dessa afirmação só pode ser
verdadeiro ou falso, logo se trata de uma proposição)
Existem algumas afirmações que não são proposições, por exemplo, “esta afirmação é falsa”. As
proposições são objetos de estudo de um ramo da matemática denominado Cálculo Proposicional. Não há
necessidade de preocupação neste momento com a teoria das proposições, pois as que serão vistas neste texto
são de compreensão natural e imediata, geralmente são afirmações matemáticas corriqueiras.
As tabelas abaixo mostram os principais conectivos dessa lógica.
Conectivo NOT
(símbolo: !)
A
!A
True
False
False
True
A
True
True
False
False
Conectivo AND
(símbolo: &&)
B
A && B
True
True
False
False
True
False
False
False
Conectivo OR
(símbolo: ||)
A
B
A || B
True
True
True
True
False
True
False
True
True
False
False
False
Essas tabelas se tornarão mais compreensíveis com os exemplos a
seguir.
230
Teste AND
Para saber se o valor de n pertence ao intervalo 2 do problema anterior (0 ≤ n < 20) basta escrever: if(n>=0
&& n<20) ... Em português se diria: se n é maior do que ou igual a 0 e menor do que 20 ... Trata-se de uma
condição composta. Ela será verdadeira apenas se as duas condições envolvidas forem verdadeiras.
Teste OU
Para saber se uma pessoa possui mais de 20 anos ou se o seu saldo bancário é superior a 5000 reais basta
escrever: if(idade>20 || saldo>5000) ... Observe que se pelo menos uma das condições for verdadeira então a
condição composta também o será.
Teste NOT
Para saber se uma pessoa não é menor de idade basta escrever: if(!(idade>=18)) ... Observe que se a
condição (idade>=18) é verdadeira a sua negação (!(idade>=18)) é falsa. A comparação !(idade>=18)
corresponde em português à expressão “idade não é maior ou igual a 18”.
3.2 Alguns erros comuns
•
•
Uso de = em teste de igualdade. Para testar se uma pessoa tem exatamente 18 anos, devemos
escrever if(idade == 18)
Uso de ponto-e-vírgula no fim da linha que contém um if. Lembrar que if é um teste de condição.
Coloca-se ponto-e-vírgula no fim de uma instrução e não de um teste de condição.
3.3 Para melhor compreender as condições
Acompanhe o código abaixo.
#include <stdio.h>
int main()
{
if (0)
{
printf("falso");
}
else
{
printf("verdadeiro");
}
system(“pause”);
}
A execução deste código com as modificações indicadas para if(expr)
fornecerá os seguintes resultados:
expr
resultado
0
falso
1
verdadeiro
2
verdadeiro
5
verdadeiro
Resumindo, quando a expressão comparação vale 0 o resultado de sua
avaliação é False e para qualquer outro valor dessa expressão a sua
avaliação é True. Por isso os resultados obtidos acima. De fato, toda vez
que a linguagem C realiza um teste, ela devolve o valor 1 quando o
resultado é verdadeiro e 0 quando o resultado é falso.
Atribuir o resultado de uma comparação a uma variável
Acompanhe o código abaixo.
#include<stdio.h>
int main()
{
int p = 14;
int b = 10;
b = (p >= b);
printf("b = %d\n", b);
system(“pause”);
}
A comparação p >= b retorna o número 1, pois ela é verdadeira.
Assim, b passa a valer 1, como resultado da atribuição. Isso
pode ser verificado com o resultado exibido por printf.
Experimente mudar o valor de b para 20 no código ao lado. O
resultado exibido deve ser 0, pois desta vez o teste é falso.
Um valor boolean
Importante! A variável b do código acima representa um valor boolean. Diz-se que uma variável que só pode
receber os valores 0 ou 1 é do tipo boolean. Na verdade, em C não existe este tipo. Há uma convenção na
linguagem C de modo que 0 é associado ao boolean False e qualquer outro valor é associado ao boolean
231
True. Em C++, a linguagem C orientada a objeto, um novo tipo foi criado especialmente para conter esses
valores. Em C, geralmente as variáveis que guardarão valores boolean são declaradas do tipo int.
Os valores Booleans nas condições
Geralmente fazemos testes sobre variáveis booleans. Se a variável "maior" vale 1 ou qualquer outro valor
diferente de 0 e temos a instrução if(maior) {printf(“maior de idade”)}; ... o resultado exibido é a expressão
”maior de idade”, pois a variável "maior" é diferente de zero, portanto True. Quando é sabido que a variável
pode conter um número fazemos o teste na forma “if(variável == 1)”. Se a variável só pode conter os valores
0 ou 1, fazemos o teste na forma “if(variável)”. Sempre que possível, é bom encurtar os caminhos.
3.4 A estrutura switch
A condição "if... else" é o tipo de condição mais utilizada. Entretanto, quando existem muitas comparações a
serem feitas podemos utilizar outro tipo de estrutura: switch. Analise o seguinte trecho de programa
…
if (idade == 2)
{ printf("Ola bebe!");}
else if (idade == 6)
{ printf("Oi crianca!");}
else if (idade == 12)
{ printf ("Oi jovem!");}
else if (idade == 16)
{ printf ("Oi adolescente!");}
else if (idade == 18)
{ printf ("Salve adulto!");}
else if (idade == 68)
{ printf ("Ola vovo !");}
else
{ printf ("????????????? ");}
…
Existem seis comparações a serem feitas. Para não repetir essas comparações, nova forma de representá-las
foi desenvolvida. O trecho de programa abaixo mostra essa nova forma.
…
switch (idade)
{
case 2: printf ("Ola bebe!");
break;
case 6: printf ("Oi crianca!");
break;
case 12: printf ("Oi jovem!");
break;
case 16: printf ("Oi adolescente!");
break;
case 18: printf ("Salve adulto!");
break;
case 68: printf ("Ola vovo!");
break;
default: printf ("?????????????");
break;
}
…
232
Sempre que possível, é bom escrever utilizando o comando switch, é mais prático. É necessário colocar uma
instrução break, obrigatoriamente ao fim de cada caso. Esquecer de fazê-lo faz com que o computador
continue a executar as instruções seguintes, o que não faz parte da lógica da instrução switch.
Enfim, o caso "default" corresponde ao else já visto. Se a variável não vale nenhum dos valores testados
acima o computador executa esta opção.
Exemplo de um menu
O programa abaixo pode ser utilizado como modelo para a construção de outros menus.
// em um restaurante
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
long menu;
printf("=== Menu ===\n\n");
printf("1. Pao com queijo\n");
printf("2. Pao com manteiga\n");
printf("3. Cuscus\n");
printf("4. Ovo frito\n");
printf("\n Sua escolha? ");
scanf("%d", &menu);
printf("\n");
switch (menu)
{
case 1:
printf("Voce escolheu Pao com queijo!");
break;
case 2:
printf("Voce escolheu Pao com manteiga!");
break;
case 3:
printf("Voce escolheu Cuscus!)");
break;
case 4:
printf("Voce escolheu Ovo frito!");
break;
default:
printf("Voce não fez a escolha correta!");
break;
}
printf("\n\n");
system("PAUSE");
}
3.5 Condições condensadas
Existe ainda um terceiro modo de programar uma comparação, ainda que pouco utilizado. Trata-se da
condição condensada. É como um if … else só que tudo em uma mesma linha. Observar atentamente os dois
trechos de programa abaixo:
233
Um teste if ... else
Se a variável maior for avaliada como True, isto é, se for diferente de zero, então a variável idade receberá o
valor 18, caso contrário receberá o valor 17.
if (maior)
idade = 18;
else
idade = 17;
O mesmo teste, condensado
A semântica desta instrução é idêntica à do comando if ... else. Se maior for diferente de 0 (True), idade
recebe o valor 18. Se maior possui o valor 0, então idade recebe o valor 17. Trata-se apenas de uma forma
condensada de escrever a estrutura if ... else.
idade = (maior) ? 18 : 17;
3.6 Teste
1. O que significa o símbolo <= ?
( ) Superior ( ) Inferior ( ) Superior ou igual ( ) Inferior ou igual ( ) Diferente de
2. Se a variável n vale 0, o que é exibido pelo trecho de programa abaixo?
if (n != 0)
printf ("Siga em frente.");
else
printf ("Aguarde um pouco.");
( ) Siga em frente.
.
( ) Aguarde um pouco
3. Observe o trecho de programa abaixo. O que será impresso?
int temDinheiro = 0, maior = 0;
long dinheiroEmBolso = 10000, idade = 19;
...
temDinheiro = dinheiroEmBolso > 10000;
maior = !(idade < 18);
if (temDinheiro && maior)
printf ("Pode abrir conta em banco!");
else
printf ("Impossivel abrir conta");
( ) Pode abrir conta em banco!
( ) Impossivel abrir conta
4. Qual o problema na instrução abaixo?
switch (k)
{
case 5:
printf ("Bom dia");
case 12:
printf ("Boa tarde");
default:
printf ("Ate logo");
}
( ) faltam as instruções break
( ) falta um ponto-e-vírgula no fim de switch
( ) faltam as chaves em cada "case"
( ) é "case default" e não "default"
234
5. Quanto valerá a variável resp após as instruções abaixo? A variável a vale 200 e b é um Boolean que vale
False.
resp = (b || a >= 200) ? 20 : 30;
resp = (resp == 20 && (b && a >= 200)) ? 40 : 50;
( ) 20
( ) 30
( ) 40
( ) 50
4. Avaliando o que foi construído
Compreender bem as estruturas de seleção é uma condição indispensável para escrever bons programas.
Parece que tudo é indispensável, não? Mas é assim mesmo. Existe uma seqüência lógica no aprendizado de
uma linguagem de programação. É como na matemática. É praticamente impossível calcular uma derivada,
conscientemente, se não conhecemos os conceitos de função, limites e de continuidade. Como veremos a
seguir, as estruturas de repetição trarão embutidas estruturas de seleção.
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. MGraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
235
Unidade VII - Estruturas de Controle (Repetições)
1. Situando a temática
Após termos visto como realizar uma operação de seleção é chegado o momento de entendermos as
repetições ou "laços". O que é um laço? Trata-se de uma técnica que permite repetir as mesmas instruções
várias vezes. Da mesma forma que para as comparações, existem várias formas de realizar os laços.
Veremos três dessas formas.
• while
• do ... while
• for
Os laços, em geral, funcionam assim:
1. O computador executa as instruções de cima para baixo (como de hábito)
2. Ao atingir o fim do laço, retorna para a primeira instrução deste.
3. Pode haver « saltos » para fora do laço
4. Existe uma condição embutida nos laços
5. Essa condição controla a repetição das instruções e a saída do laço
instruções
instruções
..................
instruções
A existência da condição nos laços é necessária para impedir que as instruções fiquem sendo executadas
indefinidamente. Então, ao criarmos um laço, criamos também, implicitamente, uma condição. A condição
significa: « repetir este laço enquanto (até que) a condição seja verdadeira ».
2. Problematizando a temática
Suponhamos que necessitemos imprimir todos os números naturais de 1 até 100. Uma maneira de fazê-lo é
chamarmos 100 vezes a função printf tendo como argumento em cada chamada o número natural desejado. É
uma solução, mas com certeza a menos indicada. Imagine se tivéssemos de imprimir os 1000 primeiros
números naturais. As repetições nos oferecem um modo confortável de realizarmos esta tarefa, como
veremos.
3. Conhecendo a temática
3.1 O laço while
A estrutura de um laço while é
while ( <condição> )
{
Bloco de instruções
}
Sua interpretação é muito simples: enquanto a condição for
avaliada True, o bloco de instruções do laço é executado.
Quando a condição for avaliada False, o controle do programa
passa para a próxima instrução, fora do laço. Evidentemente,
pode ser que as instruções do laço não sejam executadas
nenhuma vez. Por quê?
Exemplo1. Façamos um pequeno teste: escrever um programa que leia um número
inteiro a partir do teclado. Enquanto esse número não for negativo, voltar a ler um
número inteiro a partir do teclado.
236
Ao executar este programa entremos com os
seguintes números:
#include<stdio.h>
main()
{
int n=0;
while (n>=0)
{
printf("Digitar um numero positivo ou nulo: ");
scanf("%d", &n);
}
printf("Obrigado! \n") ;
system(" pause ") ;
return 0 ;
}
Digitar um numero positivo ou nulo: 13
Digitar um numero positivo ou nulo: 21
Digitar um numero positivo ou nulo: 143
Digitar um numero positivo ou nulo: -3
Obrigado!
O programa continuará executando as instruções do laço enquanto o valor digitado for positivo ou nulo e
sairá do mesmo tão logo seja digitado um número negativo.
Graficamente, a semântica da instrução while pode ser representada assim:
condição
próxima
instrução
false
true
Bloco de
instruções
Semântica While
Vamos fazer algo mais interessante, executar um laço certo número de vezes. Para isso deve-se criar uma
variável que "contará" o número de vezes que o laço será executado. Essa variável deverá valer 0 antes do
laço e ser incrementada dentro deste cada vez que o mesmo for executado. A operação "incremento" como
sabemos, consiste em aumentar de 1 o valor de uma variável. Por exemplo, x++ incrementa o valor da
variável x de uma unidade.
Exemplo2. Imprimir (exibir na tela) os números inteiros a partir de 0 até 9.
Acompanhe o trecho de programa abaixo. Completando este trecho de programa, teremos a seguinte saída
(experimente fazê-lo):
....................
long cont = 0;
while (cont < 10)
{
printf("cont = %d\n ",cont);
cont++;
}
.....................
cont = o
cont = 1
cont = 2
cont = 3
cont = 4
cont = 5
cont = 6
cont = 7
cont = 8
cont = 9
237
Procure explicar como isto aconteceu. Ao compreendermos o que se passou poderemos nos divertir
aumentado o valor do limite 10 para 100, 1000, etc.
Ampliando o seu Conhecimento
Atenção com os laços infinitos
Ao criarmos um laço temos que ter a certeza de que ele irá parar em algum momento. Observemos que
neste tipo de laço se a condição é sempre verdadeira, o programa nunca pára.
Exemplo de um laço infinito.
while (1)
{
printf("laco infinito\n");
}
Lembremo-nos dos booleans: 1 = True, 0 = False. Aqui a condição
é sempre verdadeira, portanto o programa imprimirá a frase "laço
infinito" sem parar. Na verdade a intervenção humana pode parar
este programa bastando teclar Ctrl + C.
Evitemos a todo custo cairmos em um laço infinito, a menos que este seja o nosso propósito. Às vezes esses
laços são necessários. Na programação de jogos, por exemplo.
3.2 O laço do ... while
Este tipo de laço é bastante similar ao laço while, porém menos utilizado. A diferença entre as duas
instruções está na colocação física da condição dentro dos laços. Neste tipo de laço a condição aparece após
o bloco enquanto que em while a condição é colocada antes do bloco. Isto quer dizer que as instruções
internas ao laço do ... while são executadas pelo menos uma vez.
Graficamente, a semântica da instrução do ... while pode ser representada assim:
Bloco de
instruções
true
condição
false
próxima
instrução
Semântica Do ... While
Exemplo3. Resolver o mesmo problema de Exemplo2, agora utilizando a instrução do
... while.
.....................
long cont = 0;
do
{
printf("cont = %d\n",cont);
cont++;
} while (cont<10);
.....................
Uma solução para este problema é dada pelo trecho de programa
ao lado. Complete-o e verifique que a sua saída é igual à do
Exemplo1.
Qual a diferença operacional entre este programa e aquele do
Exemplo2? Muito simples, no programa do exemplo2 o bloco de
instruções poderá nunca ser executado bastando para isto que a
condição seja falsa no início. Caso o valor de cont seja
238
inicializado com 30, por exemplo, a condição será falsa na primeira vez em que for avaliada. No programa
do exemplo3 o bloco de instruções será executado pelo menos uma vez. É responsabilidade do programador
gerenciar bem a execução dos laços.
Para não esquecer! Não devemos esquecer de colocar ponto-e-vírgula no fim da instrução do ... while. Este
esquecimento é um erro comum entre os principiantes.
3.3 O laço for
Em teoria, o laço while permite realizar todas as repetições que alguém deseje programar. Entretanto, como
para as condições, existe uma forma condensada, mais rápida para escrever: o laço for. Os laços for, que
serão vistos nesta seção, e os laços while são os mais utilizados em programação.
Estudaremos o laço for a partir de um exemplo.
Exemplo4. Resolver o mesmo problema de Exemplo2, agora utilizando a instrução
for.
Uma solução para este problema é dada pelo trecho de
programa ao lado. Complete-o e verifique que a sua saída
é igual à do Exemplo2.
.............................
long cont;
for (cont = 0; cont < 10; cont++)
{
printf("cont = %d\n",cont);
}
...............................
O laço for é outra forma de escrever um laço while.
Algumas observações
•
A variável cont não foi inicializada a zero quando de sua declaração, mas poderia ter sido.
•
Não há mais incremento cont++ dentro do bloco de instruções
Vejamos o que há entre os parênteses pois é lá que se encontra todo o interesse do laço for. Na verdade,
existem três instruções, separadas por vírgulas, dentro desses parênteses:
•
A inicialização: esta instrução é utilizada para preparar a variável cont. Em nosso caso, ela foi
inicializada com zero.
•
A condição: como para o laço while, é a condição que diz se o bloco deve ser repetido. Enquanto a
condição for verdadeira o bloco é executado.
•
O incremento: esta última instrução é executada ao fim de cada execução do bloco para atualizar a
variável de controle. Às vezes é utilizado um decremento no lugar de um incremento. Às vezes
também, se utiliza outras operações como cont += 2; para avançar de 2 em 2, por exemplo.
Enfim, o laço for será muito útil. Saibamos utilizá-lo bem.
3.4 Teste
1. Qual destes laços não existe em C?
( ) for
( ) repeat
( ) do.. while
( ) while
239
2. Quantas vezes a mensagem "Estude Matematica" será exibida?
long cont = 12;
do
{
printf ("Estude Matematica\n");
cont++;
} while (cont < 13);
( ) 0 vezes ( ) 1 vez
( ) 2 vezes
( ) 12 vezes
( ) 13 vezes
3. Quantas vezes a mensagem "Sistemas operacionais" será exibida?
long cont = 10;
while (cont < 11)
{ printf ("Sistemas operacionais\n"); }
( ) 0 vezes
( ) 1 vez
( ) 14 vezes
( ) 15 vezes
( ) infinitas vezes
4. Qual dos laços for poderia exibir as mensagens seguintes?
(
(
(
(
Linha n°1
Linha n°3
Linha n°5
Linha n°7
) for (cont = 1; cont < 9 ; cont += 2)
) for (cont = 1; cont <= 7 ; cont ++)
) for (cont = 0; cont < 9 ; cont += 2)
) for (cont = 1; cont < 8 ; cont ++)
E para concluir, um lembrete que todos compreenderão, com certeza.
#include<stdio.h>
int main()
{
int cont = 0;
for(cont=1; cont<=100; cont++)
printf("Eu devo entregar minhas tarefas em dia.");
}
4. Avaliando o que foi construído
As estruturas de repetição facilitam bastante a realização das tarefas, como visto. É preciso que o
programador esteja familiarizado com essas estruturas, pois elas são necessárias na maioria dos programas
que são desenvolvidos. Com os conhecimentos adquiridos até aqui o aluno é capaz de programar a solução
de boa parte dos problemas numéricos. Esses recursos serão complementados na próxima unidade com o
estudo das funções.
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. MGraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
240
Unidade VIII - Funções
1. Situando a temática
Concluímos a parte dos conhecimentos básicos da linguagem C com esta noção fundamental que são as
funções. Todos os programas em C se baseiam sobre este princípio. Aprenderemos a estruturar um programa
em pequenas partes. Um programa grande em C é feito de pequenas partes que são “encaixadas”
adequadamente formando um todo. Essas partes são as funções.
Lembremos a estrutura de um programa em C.
# include < stdio.h > ⎫
⎬Diretivas de pré − processador
# include < stdlib.h >⎭
int main(int argc, char * argv[])⎫
⎪
{
⎪
⎪⎪
⎬função
system("pause" );
⎪
⎪
return 0;
⎪
⎪⎭
}
As diretivas de pré-processador começam sempre por # e são geralmente colocadas no início do arquivo
fonte. Em seguida aparece a função main, a função principal de todo programa. Nas unidades anteriores
permanecemos sempre dentro desta função, ou seja, todos os cálculos e outros processamentos foram feitos
no interior da função main. Na prática, quase nenhum programa é escrito unicamente dentro da função main.
Vamos então aprender a dividir a solução de um problema em várias partes, cada uma dessas partes será uma
função.
2. Problematizando a temática
Um programa que realiza várias tarefas pode ser escrito como uma única função, a função principal, main.
Entretanto, essa estratégia torna o programa difícil de ser lido e mesmo desenvolvido. Por isso, é costume
dos analistas subdividirem um problema complexo em subproblemas, menos complexos obviamente, e
resolver cada um desses por meio de uma função. Esse método é conhecido como dividir para conquistar.
Aprenderemos a programar essas unidades de programa agora.
3. Conhecendo a temática
3.1 Objetivo de uma função
Uma função executa ações e retorna um resultado. As ações realizadas por uma função dependem,
geralmente, de alguns parâmetros iniciais denominados de “entrada” e o resultado produzido é denominado
“saída”.
Esquematicamente, podemos representar uma função assim:
entrada
saída
Processamento
Função
Uma função é então um elemento lógico de programação que comporta três etapas:
241
1. Entrada: informações necessárias ao processamento são passadas à função.
2. Processamento: operações realizadas, geralmente a partir das entradas.
3. Saída: terminado o processamento, a função retorna um resultado.
Imaginemos uma função que calcule a soma de dois valores inteiros. Podemos representá-la
esquematicamente como abaixo, onde a e b são valores inteiros.
a
a
Somar a e b
a+b
Em geral as funções são mais complexas que o exemplo acima, mas esses são os conceitos a conservar:
entrada de parâmetros, processamento a ser realizado e saída do resultado produzido.
3.2 A estrutura de uma função
A estrutura de uma função é bem simples. Vejamos um modelo.
tipo nomedaFunção(parâmetros)
Algumas considerações:
•
{
instruções
}
•
•
•
tipo: tal como as variáveis, toda função possui um
tipo. O tipo de uma função indica o tipo do resultado
que ela retornará. Mas existe função que não retorna
valor algum para o programa que a chamou. Neste
caso a função deverá ser de um tipo especial “void”
significando esse fato;
nomedaFunção: é o nome da função, evidentemente, e deve obedecer às mesmas regras que os
nomes de variáveis;
parâmetros: uma lista de parâmetros entre parênteses, separados por vírgula, que contém as
entradas necessárias ao processamento da função. Uma função pode conter qualquer número de
parâmetros, dependendo da lógica e do bom senso do programador.
{ ... }: após o cabeçalho da função (tipo, nome e parâmetros), segue um par de chaves que contém o
corpo da função, as instruções que a compõem.
3.3 Criando uma função
Vamos escrever a função que soma dois valores inteiros, vista anteriormente e dar-lhe o nome soma2.
Eis a nossa primeira função. O que vemos nessa função?
long – tipo dos parâmetros e de retorno da função, por isso a
variável “resposta” também é desse tipo
soma2 – é o nome dado à função
return – instrução que indica à função para retornar um resultado
ao programa que a chamou. Esta instrução geralmente se encontra
no fim da função. Como a variável resposta é declarada dentro do
corpo da função, seu escopo será a própria função. O escopo de
uma variável é o seu campo de abrangência (onde ela é válida). Uma variável declarada assim é chamada
local e não pode ser utilizada fora da função onde é declarada. A variável resposta é então uma variável local
à função soma2.
A função soma2 definida acima, será “chamada” de dentro da função principal (main) de um programa.
long soma2(long p1, long p2)
{
long resposta = 0;
resposta = p1+p2;
return resposta;
}
242
3.4 Chamando uma função
#include<stdio.h>
long soma2(long p1,long p2)
{
long resposta = 0;
resposta = p1+p2;
return resposta;
}
int main()
{
long a,b, soma;
printf("\n digite um valor para a = ");
scanf("%d",&a);
printf("\n digite um valor para b = ");
scanf("%d",&b);
soma=soma2(a,b);
printf(" soma = %d\n", soma);
system("pause");
ret rn 0;
Para chamar uma função a partir da função main ou de qualquer
outra função, basta escrever o seu nome em uma expressão ou
como um parâmetro em um comando printf.
No programa ao lado a função soma2 está sendo chamada a
partir da função main. Observe que o nome da função aparece
em uma expressão, do lado direito de um comando de
atribuição. O nome de uma função nunca pode aparecer do lado
esquerdo em uma atribuição. Lembre que uma atribuição é do
tipo variável = expressão.
Uma função pode chamar outra, que pode chamar outra, etc.
Cabe ao programador não perder o domínio sobre a execução do
código.
Os parâmetros declarados no cabeçalho de uma função são ditos
parâmetros formais, pois servem apenas para dizer ao
compilador o tipo e a ordem em que os parâmetros efetivos
serão transmitidos à função. Parâmetros efetivos são aqueles
que aparecem na chamada da função. No programa acima os
parâmetros efetivos são a e b e os parâmetros formais são p1 e
p2. Os parâmetros efetivos e os formais podem ter os mesmos nomes, porém representam identificadores
distintos.
Ampliando o seu Conhecimento
Protótipo de uma função. O protótipo de uma função é o seu cabeçalho
seguido de ponto-e-vírgula.
Exemplo. long soma2(long p1,long p2);
Um protótipo pode também indicar apenas os tipos dos parâmetros:
Exemplo. long soma2(long,long);
Na definição da função os parâmetros formais serão explicitados.
Uma função pode ser definida também após a função principal. Neste caso, o seu protótipo deve ser
declarado antes da função main. Após a função main coloca-se então a função que está sendo definida.
Usar ou não os protótipos é uma decisão do programador. O seu uso indica ao compilador que a função por
ele representada será definida (codificada) após a função main. Caso uma função seja codificada após a
função main, sem o uso de protótipo, o compilador acusará um erro, pois quando o nome da função aparecer,
pela primeira vez, na função main, ele não o reconhecerá como nome de uma função e o tratará como uma
variável não declarada acusando, portanto erro por falta de declaração de variável.
243
O exemplo anterior ficaria assim:
#include<stdio.h>
/* declaração do protótipo */
long soma2(long p1, long p2);
int main()
{
long a,b, soma;
printf("\n digite um valor para a = ");
scanf("%d",&a);
printf("\n digite um valor para b = ");
scanf("%d",&b);
soma=soma2(a,b);
printf(" soma = %d\n", soma);
system("pause");
return 0;
}
/* definição da função soma2 */
long soma2(long p1,long p2)
{
long resposta = 0;
resposta = p1+p2;
return resposta;
}
Ampliando o seu Conhecimento
Não somos obrigados a armazenar o resultado produzido
por uma função em uma variável. Podemos retornar esse
resultado na forma indicada de uma expressão. Vejamos,
#include <stdio.h>
long soma2(long p1, long p2)
{
return p1+p2;
}
Neste caso, não foi necessário declararmos a variável
“resposta” uma vez que não a utilizamos.
Também é possível chamar uma função a partir de um
comando printf.
printf(" soma = %d\n", soma2(a,b));
Observe que acima a função soma2 está sendo chamada a partir da lista de parâmetros da instrução printf.
3.5 Mais exemplos
I. Conversão Celsius/Fahrenheit
Lembremo-nos que a relação entre as escalas Celsius e Fahrenheit é dada por:
C
F − 32
. Então, de
=
100
180
posse dessa informação vamos escrever uma função que transforme uma temperatura dada em Celsius para
Fahrenheit. Escreveremos apenas a função. Escreva o programa principal (a função main).
/* função conversão: Celsius em Fahrenheit */
Antes de escrever a função principal, responda ao
pequeno questionário a seguir.
float converte(long C)
{
long F = 0;
F = 180*C/100+32;
return F;
}
1.
2.
3.
4.
Qual o parâmetro formal, na função converte?
Quantos parâmetros efetivos devem existir na chamada da função converte?
Qual o tipo do parâmetro efetivo?
Como será feita a chamada à função converte (comando de atribuição ou parâmetro na função
printf)?
Pronto! Agora é só escrever a função main.
244
II. Transformar segundos em horas, minutos e segundos
A função abaixo transforma n segundos em horas, minutos e segundos. Essa função não retornará nenhum
valor. Ela imprimirá o resultado obtido sem a necessidade de retorná-lo para a função main.
/* função tempo: segundos em horas, minutos e segundos */
void tempo(int s)
{
int ss, mm, hh, t;
ss = s%60;
// resto da divisão de s por 60
t = s/60;
// quociente inteiro de s por 60
mm = t%60; // resto da divisão de t por 60
hh = t/60;
// quociente inteiro de t por 60
printf(“%d segundos = %d horas, %d minutos e %d segundos”, s,hh,mm,ss);
}
Antes de escrever a função principal, responda ao pequeno questionário a seguir.
1.
2.
3.
4.
5.
Qual o tipo da função tempo?
Qual o parâmetro formal, na função tempo?
Quantos parâmetros efetivos devem existir na chamada da função tempo?
Qual o tipo do parâmetro efetivo?
Como será feita a chamada à função tempo (expressão ou parâmetro na função printf)?
Pronto! Agora é só escrever a função main. Uma dica: a chamada à função tempo pode ser assim tempo(n);.
III. Área de um retângulo
A área de um retângulo é dada por: largura * altura.
Nossa função AreaRetangulo deve receber dois parâmetros, largura e altura e retornar a área calculada.
/* função AreaRetangulo */
Escreva a função main().
float AreaRetangulo(float larg, float alt)
{
return larg * alt;
}
™
Modifique a função AreaRetangulo para que ela
imprima a área calculada e não precise retorná-la à
função main.
IV. Um menu
Acompanhe o exemplo abaixo. Trata-se da elaboração de um menu, onde podemos escolher qual operação
será executada: o cálculo da área de um círculo, de um quadrado, de um retângulo ou de um trapézio. Este
roteiro poderá ser utilizado para a elaboração de outros menus. Basta fazer as adaptações que se fizerem
necessárias.
/* função menu */
long menu()
{
int escolha= 0;
while (escolha < 1 || escolha> 4)
{
printf("Menu :\n");
printf("1 : Para calcular a area de um circulo\n");
printf("2 : Para calcular a area de um quadrado\n");
printf("3 : Para calcular a area de um retangulo\n");
printf("4 : Para calcular a area de um trapezio\n");
printf("Sua escolha ? ");
scanf("%d", &escolha);
}
return escolha;
}
245
Texto da função menu
int main(int argc, char *argv[])
{
float r,l, la, lb, area;
switch (menu())
{
case 1:
printf("Raio do circulo =");
scanf("%f",&r);
printf("\nArea = %f\n", 3.14*r*r);
break;
case 2:
printf("Lado do quadrado =");
scanf("%f",&l); area=l*l;
printf("\nArea = %f\n",area);
break;
case 3:
printf("Lados do retangulo\n");
printf("lado a = "); scanf("%f",&la);
printf("lado b = "); scanf("%f",&lb);
printf("\nArea = %f\n",la*lb);
break;
case 4:
printf("Bases do trapezio\n");
printf("base maior a = "); scanf("%f",&la);
printf("base menor b = "); scanf("%f",&lb);
printf("altura l = "); scanf("%f",&l);
printf("\nArea = %f\n",(la+lb)*l/2);
break;
}
system("PAUSE");
return 0;
}
Texto da função main
3.6 Teste
1. O que acontece após um return?
•
•
•
( ) A função pára e retorna o resultado indicado
( ) A função continua e retorna o resultado indicado
( ) A função continua e não retorna resultado
2. Em que caso a instrução return não é obrigatória?
•
•
•
( ) Quando a função não possui parâmetro de entrada
( ) Quando a função é de tipo void
( ) Quando a função deve retornar zero
3. O que são os parâmetros de uma função?
•
•
•
( ) Indicações sobre o nome da função
( ) Indicações sobre o valor que ela deve retornar
( ) Variáveis que lhe são enviadas para serem usadas internamente
4. Qual das afirmações é falsa?
•
•
•
( ) Uma função não é obrigada a retornar um valor
( ) Uma função pode retornar um valor de qualquer tipo de variável
( ) Uma função pode retornar vários valores pela instrução return
246
5. O que há de errado na função abaixo?
long quadrado(long num)
{
long resposta = 0;
resposta = num * num;
}
•
•
( ) A função não retorna nenhum valor
( ) Falta um ponto-e-vírgula em algum lugar da função
4. Avaliando o que foi construído
As funções em linguagem de programação, como você já percebeu, surgiram para facilitar a programação de
problemas considerados complexos. Esses problemas requerem uma solução longa o que dificulta o seu
desenvolvimento e sua compreensão. Os programas são entes abstratos, frutos da imaginação. Por isso
mesmo não são fabricados, no sentido em que o são os produtos das engenharias convencionais, como uma
casa, um navio, etc. Assim, quando nos referimos a um programa de computador, sempre usamos o termo
desenvolvimento em lugar de fabricação. De fato, desenvolvemos uma solução para um dado problema, um
algoritmo. É nessa fase do projeto de solução que o problema é “quebrado” em subproblemas. A solução de
cada subproblema é então implementada como uma função. Mais uma vez, só as repetições, os exercícios,
conduzem ao domínio da técnica.
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. MGraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
247
Unidade IX - Ponteiros
1. Situando a temática
Nesta unidade estudaremos um novo tipo de dado, os ponteiros. Veremos como eles funcionam e para que
servem. É quase impossível hoje em dia escrever programas em C sem fazer uso dos ponteiros. Mesmo sem
saber nós já o utilizamos quando realizamos leituras com a função scanf. A noção de ponteiros é importante
para lidarmos com outro tipo de dado muito utilizado denominado array. Mesmo que o escopo deste texto
não cubra outros tipos de dados abstratos de grande utilização na computação, é importante saber,
principalmente para aqueles que pretendem avançar mais no estudo de resolução de problemas com o uso de
computador, que vários desses tipos de dados são implementados à base de ponteiros. A título de ilustração
sobre esses tipos citamos: árvores e listas encadeadas.
2. Problematizando a temática
Um problema estranho
Escrever uma função que retorne 2 ou mais valores.
Isso parece ser "Impossível"!
Sabemos que uma função tem as seguintes características:
• Retorna um único valor ou não retorna valor algum
• Se uma função é do tipo T então ela retorna um valor do tipo T
Vamos adquirir os conhecimentos necessários para solucionar problemas deste tipo.
3. Conhecendo a temática
3.1 Um pouco mais sobre funções
Retomemos o problema de transformar segundos em horas, minutos e segundos, apresentado na unidade IX.
/* função tempo: segundos em horas, minutos e segundos */
void tempo(int s)
{
int ss, mm, hh, t;
ss = s%60;
// resto da divisão de s por 60
t = s/60;
// quociente inteiro de s por 60
mm = t%60; // resto da divisão de t por 60
hh = t/60;
// quociente inteiro de t por 60
printf(“%d segundos = %d horas, %d minutos e %d segundos”, s,hh,mm,ss);
}
Observamos que a função é do tipo void, isto é, não retorna valor algum. De fato, o resultado calculado é
impresso na própria função. Por que não foi possível retornar esses valores para a função main?
Quando passamos uma variável para uma função, como argumento, uma cópia dessa variável é feita e
passada para a função. Assim, se na função main existir uma variável de nome s, que represente o número
de segundos a ser transformado, a variável s na função tempo não é a mesma que aquela de main. Trata-se
apenas de uma cópia. Tudo se passa da mesma forma caso as variáveis correspondentes tenham nomes
distintos.
248
De modo geral, a transmissão de parâmetros se passa assim:
int main()
int a,b,c
.....................
x = f(a,b,c);
int f(int x, int y, int z)
……………….
……………
cópia
de c
cópia
de b
cópia
de a
Observemos que na função main, ao ser executada a instrução x = f(a,b,c); são criadas cópias dos
parâmetros reais (as variáveis a, b e c) e essas cópias são passadas para a função f. Na função f, as cópias
transmitidas recebem os nomes x, y e z, respectivamente, parâmetros formais. Assim, se alguma alteração
ocorrer nos valores de x, y ou z essa alteração não afetará os valores de a, b ou c, pois isso ocorreu apenas na
cópia dessas variáveis ficando os seus originais intactos.
A solução do problema proposto, como veremos, é apenas um exemplo da utilidade dos ponteiros.
A memória e os endereços
Endereço
Valor
0
145
1
3.8028322
2
0.8272555
É mais ou menos assim que podemos representar a memória principal
(RAM) de um computador. A memória é composta de células. Cada
célula possui um número, seu endereço. A memória possui um grande
número de endereços, começando sempre por 0. Em cada endereço
podemos armazenar UM e UM SÓ número. Observemos também que
na memória podemos armazenar apenas números. Ela não pode
armazenar letras, frases, etc. Para contornar esse problema foi inventada
uma tabela, denominada ASCII, já vista na unidade II.
Endereço e valor
Ao criarmos uma variável “soma” do tipo long, por exemplo,
escrevemos:
...
...
long soma = 300; e o programa solicita ao sistema operacional
permissão para utilizar a memória. Como resposta o sistema operacional
n-1
940.5118
indica em qual endereço o programa pode armazenar um valor de tipo
long para a variável soma. Aliás, este é um dos papéis principais de um
sistema operacional, gerenciar a alocação de memória para os programas.
Voltemos à nossa instrução. O valor 300 é armazenado em alguma parte da memória, digamos no endereço
1540. A partir de então, a palavra “soma” no programa é substituída, via compilador, pelo endereço 1540. A
cada ocorrência da palavra “soma” no programa, o computador verifica então o endereço 1540. Para o
programador tudo é bastante transparente. Basta escrever “soma” em uma expressão aritmética, em uma
instrução de leitura (scanf) ou impressão (printf). Vejamos,
3
39014768
printf("A variavel soma vale: %d", soma);
Resultado na tela:
A variável soma vale: 300
Sabemos exibir o valor de uma variável, mas poderíamos exibir o seu endereço? Para exibir o endereço de
uma variável deve-se utilizar o especificador de formato %p (p da palavra ponteiro) na instrução printf. De
outro modo, enviamos à função printf não a variável soma, mas seu endereço. E para fazer isso devemos
colocar o símbolo & antes do nome da variável. Já fazemos isto habitualmente quando utilizamos a função
scanf. Então para imprimir o endereço da variável “soma” devemos escrever
249
printf("O endereço da variavel soma eh: %p", &soma);
Resultado na tela:
O endereço da variável soma eh: 0023FF74
O que aparece na tela é um número sim. Um número hexadecimal, no lugar dos números decimais aos quais
estamos acostumados. Esse é o endereço da variável “soma” na memória do computador, designado pelo
sistema operacional como explicado acima.
Se trocarmos %p por %d na instrução printf acima obtemos um número decimal como saída. Entretanto, %p
foi implementado especialmente para exibir endereços, por isso preferimos esta notação.
Atenção! Ao executarmos um programa em computadores diferentes, o endereço de uma variável será
certamente diferente de um computador para outro. Isso depende dos programas que estão carregados na
memória dos computadores. Não esqueçamos que é o sistema operacional que se encarrega de gerenciar a
memória. É, portanto impossível saber a priori em que endereço uma variável será alocada.
Em resumo,
• soma: exibe o VALOR da variável.
• &soma: exibe o ENDEREÇO da variável.
Com “soma” o computador lê o valor da variável na memória e reenvia esse valor. Com “&soma”, o
computador nos dirá em que endereço se encontra a variável.
3.2 Utilização de ponteiros
Até este ponto utilizamos variáveis apenas para conter números. A partir de agora vamos aprender a criar
variáveis que conterão endereços: é exatamente a isto que chamamos de ponteiros. Ponteiro é uma variável
que contém um endereço.
Poderíamos nos perguntar, mas os endereços não são números também? Sim, mas neste caso esses números
têm um significado particular: eles representam o endereço de outra variável na memória.
I. Criar um ponteiro
Para criar uma variável de tipo ponteiro, devemos colocar o símbolo * no início do nome da variável.
Exemplo.
Podemos também escrever:
long *nomeDoPonteiro;
long *p1, *p2, *p3;
long* p1, p2, p3;
Como temos visto ao longo deste curso, é muito importante iniciarmos as variáveis quando de suas
declarações. É ainda mais importante fazê-lo com os ponteiros. Para iniciar um ponteiro devemos escrever:
A constante NULL indica que o ponteiro p1 não contém nenhum endereço. Essa
instrução reservará um espaço na memória do computador como se estivéssemos
criando uma variável normal. A diferença entre uma variável ponteiro e as outras
variáveis já vistas é que o conteúdo desta será o endereço de outra variável.
Exemplo. A primeira linha significa: "Criar uma variável de tipo
long com valor 300". A segunda linha significa: "Criar uma variável
long soma = 300;
de tipo ponteiro cujo valor é o endereço da variável soma".
long *pointSoma = &soma;
Observação. Não existe um tipo ponteiro como existem os tipos
int, long, etc. Então, não podemos escrever, por exemplo, ponteiro pointSoma; em seu lugar escrevemos
long *pointSoma; como visto acima. Isto quer dizer que a variável pointSoma é um apontador para uma
variável de tipo long. Se a variável soma fosse de tipo int, então pointSoma deveria ser um apontador para
uma variável de tipo int e escreveríamos assim: int *pointSoma;
long *p1 = NULL;
250
Representação gráfica.
soma
pointSoma
Endereço
Valor
0
145
…
3.8028322
135
300
…
39014768
15648
135
n-1
940.5118
No esquema ao lado verificamos que a
variável soma está alocada na posição
135 da memória enquanto que a
variável pointSoma esta alocada na
posição
15648
dessa
memória.
Observemos que o conteúdo da
posição 15648 é exatamente o
endereço da variável soma, isto é, 135.
Esses valores variam de computador
para computador e a cada execução do
programa.
Bem, acabamos de entrar no mundo maravilhoso dos ponteiros.
Estudemos o programa fonte abaixo:
/* ponteiros1*/
#include<stdio.h>
main()
{
int *pontx = NULL;
int x;
printf("Entre com um valor inteiro x: ");
scanf("%d",&x);
printf("\nendereco de x = %p",&x);
printf("\nvalor de x = %d",x);
pontx=&x;
printf("\nendereco de x = %d",pontx);
getch();
return 0;
}
•
•
•
pontx é um ponteiro para uma variável do tipo
int
&x é o endereço da variável x
pontx=&x; o endereço de x é armazenado em
pontx (variável ponteiro)
A atribuição do endereço de x a pontx poderia ter sido
feita assim também:
int *pontx = &x;
Execute o programa ponteiros1.
E como faríamos para imprimir o conteúdo da variável
apontada por pontx? Experimente incluir a instrução
abaixo no código do programa ponteiros1.
printf("\nvalor de x = %d", *pontx);
Como podemos verificar pela execução do programa ponteiros1, o valor impresso é o mesmo que o de x.
Então a expressão *variavelp onde variavelp é uma variável ponteiro, indica o conteúdo da variável
apontada por variavelp. Em resumo, dado o trecho de programa abaixo, temos:
x
......................
int x=100, *p=NULL;
p=&x;
……………
35846
100
p
16315
35846
•
x é uma variável de tipo int [valor de x = 100]
•
&x é o endereço da variável x [endereço de x = 35846]
•
p é uma variável que aponta para uma variável de tipo int, seu valor é um endereço [valor de p =
35846]
•
&p é o endereço da variável p [endereço de p = 16315]
251
•
*p indica o conteúdo da variável apontada por p, isto é, o valor de x [valor de *p = 100]
II. Enviar um ponteiro a uma função
O grande interesse dos ponteiros, mas não o único, consiste em enviá-los a uma função, como parâmetro
claro, de modo que a função possa modificar diretamente uma variável em memória e não apenas sua cópia.
Como isto funciona? Vejamos um primeiro exemplo.
void triplicar(long *ponteiroN);
int main(int argc, char *argv[])
{
long numero = 20;
triplicar(&numero);
// Passa o endereço de número para a função
printf("%d", numero);
// Exibe o valor de número
getch();
return 0;
}
void triplicar(long *ponteiroN)
{
*ponteiroN *= 3; // Multiplica por 3 o valor da variável numero
}
A função triplica possui um parâmetro de tipo long* (isto é, um ponteiro sobre o tipo long). Vejamos o que
se passa.
1. Uma variável "numero" é criada na função main e lhe é atribuído o valor 20.
2. A função triplicar é chamada e lhe é passado o endereço da variável numero.
3. A função triplicar recebe esse endereço na variável ponteiroN. O conteúdo da variável ponteiroN
passa então a ser o endereço de "numero".
4. Como a função triplicar possui um ponteiro para a variável numero, poderá modificar diretamente o
valor dessa variável na memória. Para isto basta utilizar *ponteiroN para designar a variável numero.
Na função triplicar utilizamos uma operação de multiplicação *ponteiroN *= 3; para multiplicar por
3 o valor de numero.
5. A instrução printf("%d", numero); na função main imprimirá 60 como o novo valor de numero.
O interesse na utilização de ponteiros se justifica pelo fato de podermos modificar o valor de várias variáveis
na memória do computador, o que na verdade é uma simulação do retorno de vários valores por uma função.
Então, não estamos mais limitados a escrever funções que retornem um único valor. Interessante não?
Enfim, tudo depende do programa que escrevemos. Às vezes é necessário retornar um valor por uma função,
um código que deverá ser testado na função main, por exemplo.
III. Outro modo de enviar um ponteiro para uma função
No código fonte que vimos não havia ponteiro na função main. Entretanto, é possível passar um ponteiro
para uma função em sua chamada, como um parâmetro real. Vejamos outra versão do programa anterior.
252
void triplicar1(long *ponteiroN);
int main(int argc, char *argv[])
{
long numero = 20;
long *p = &numero; // p recebe o endereço de numero
triplicar1(p); // envia p (endereço de numero) a função triplicar1
printf("%d", *p); // exibe o valor de numero
getch();
return 0;
}
O importante é que o endereço da variável numero tenha sido passado para a função triplicar1. A diferença
entre as duas versões é que na primeira foi passado o endereço de numero para a função triplicar e na
segunda um ponteiro foi transmitido. Na verdade, as duas versões fazem a mesma coisa, transmitem um
endereço.
No início desta unidade dissemos que mesmo sem saber já utilizávamos o conceito de ponteiro. Agora
podemos passar isso a limpo. Trata-se da função scanf. Ao utilizá-la, devemos informar um endereço de
modo que o valor teclado seja armazenado nesse endereço. Só pra lembrarmos acompanhemos o texto
abaixo.
long numero = 0;
scanf("%d", &numero);
3.3 Um problema estranho?
No início desta unidade falamos sobre certo problema estranho. É hora de tentarmos resolvê-lo. Afinal de
contas esse problema é mesmo estranho? Tratava-se de escrever uma função que retornasse 2 ou mais
valores. Agora temos as ferramentas para solucioná-lo. O problema consistia em transformar segundos em
horas, minutos e segundos. Mãos à obra. Apresentamos abaixo uma solução para esse problema. É bom que
o leitor tente encontrar sua própria solução antes de verificar a que segue.
void novotempo(long* pontHoras, long* pontMinutos, long* pontSegundos)
{
/* Não esquecer de colocar um * antes do nome do ponteiro para poder modificar
o valor das variáveis e não os seus endereços. */
*pontHoras = *pontSegundos / 3600;
*pontMinutos = (*pontSegundos % 3600)/60;
*pontSegundos = (*pontSegundos % 3600) % 60;
}
253
/* programa novotempo */
#include<stdio.h>
void novotempo(long* pontHoras, long* pontMinutos, long* pontSegundos);
int main(int argc, char *argv[])
{
long horas = 0, minutos = 0, segundos=0, segtemp=0;
// Entrar com o número de segundos
printf("Digite o numero de segundos a transformar: ");
scanf(("%", &segundos);
segtemp=segundos
novotempo(&horas, &minutos, &segundos);
// Os valores horas, minutos e segundos foram modificados!
printf("\n%d segundos corresponde a:\n", segtemp);
printf("%d horas\n", horas);
printf("%d minutos\n", minutos);
printf("%d segundos\n", segundos);
getch();
return 0;
}
Ao digitarmos o valor 27000 para a variável segundos na execução do programa acima, teremos a
seguinte saída:
Exercício. Procure explicar como os procedimentos foram realizados no programa novotempo. Explicandoos de forma coerente significa que o conceito de ponteiros foi bem assimilado.
3.4 Resumo
Os ponteiros não são simples de trabalhar, reconheçamos. Não nos preocupemos muito com isto agora. É
difícil pra todo mundo. O melhor que temos a fazer é repassar esta unidade tantas vezes quanto necessário a
fim de consolidarmos este conceito. E para terminar, mais um pequeno resumo.
Em C podemos criar duas coisas na memória: variáveis e ponteiros.
•
variáveis: é com elas que temos trabalhado até aqui. Criar uma variável é muito simples: basta
indicar seu nome e seu tipo.
long minhaVariavel = 0; // variável criada na memória (valor 0)
Se escrevemos &minhaVariavel, obtemos o endereço da variável na memória
•
ponteiros: são variáveis um pouco particulares, pois elas tomam por valor o endereço de outras
variáveis. Para criar um ponteiro vazio (que não contém endereço de nenhuma variável) escrevemos
assim:
254
long *meuPonteiro = NULL;
Um ponteiro torna-se útil quando lhe atribuímos por valor o endereço de uma variável.
long *meuPonteiro = &minhaVariavel;
•
observemos que *meuPonteiro funciona exatamente com se tivéssemos escrito minhaVariavel.
printf("%d", *meuPonteiro); é então equivalente a printf("%d", minhaVariavel);
O resultado é exatamente o mesmo salvo que no primeiro caso passamos por um ponteiro para acessar a
variável.
3.5 Teste
1. Qual dos tipos abaixo corresponde a um ponteiro?
• ( ) int
• ( ) double*
• ( ) long
2. Se digitamos &bola, o que obtemos?
• ( ) O endereço de bola
• ( ) O valor de bola
• ( ) O valor da variável apontada por bola
3. Se digitamos *total, o que obtemos?
• ( ) O endereço de total
• ( ) O valor de total
• ( ) O valor da variável apontada por total
4. Por qual valor devemos inicializar um ponteiro?
• ( ) NOTHING
• ( ) NULL
• ( ) MAIN
• ( ) ADDRESS
5. Examine o código seguinte:
long num = 25;
long *ponteiro = &num;
Supor que num se encontre no endereço 1400 da memória do computador e ponteiro se encontre no endereço
3800. Se na seqüência do programa for solicitada a impressão de *ponteiro, que valor será exibido na tela?
•
•
•
•
•
(
(
(
(
(
) 3800
) 25
) 1400
)0
) impressão impossível
4. Avaliando o que foi construído
O conceito de ponteiro é considerado por muitos como aquele em que o programador leva mais tempo para
assimilar. Devemos considerar sua grande importância dentro da programação, pois somente com a sua
utilização podemos trabalhar com estruturas de dados dinâmicas, aquelas podem aumentar ou diminuir
durante a execução de um programa dependendo unicamente da disponibilidade de memória. As principais
255
estruturas de dados dinâmicas utilizadas em computação são as filas, pilhas e árvores. O estudo dessas
estruturas foge ao escopo deste texto, mas poderão ser encontradas nos livros sobre estruturas de dados.
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. MGraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
256
Unidade X - Arrays
1. Situando a temática
Nesta unidade aprenderemos a criar variáveis do tipo array. Os arrays são muito utilizados em C,
pois eles são muito práticos. Começaremos explicando o funcionamento dos arrays na memória de um
computador. A compreensão desse funcionamento conduz o programador a construir programas mais
confiáveis. Afinal de contas é sempre bom compreender o porquê das coisas.
2. Problematizando a temática
Ao trabalharmos com um conjunto de valores, podemos fazê-lo de modo a utilizar cada valor uma única vez.
Se for este o caso, à medida que esses valores forem sendo lidos o processamento necessário é executado e
ficaremos satisfeitos. O que ocorre na prática porém é diferente. Imaginemos que se deseje calcular a
distância entre cada elemento de um conjunto e a média aritmética desses valores. Obviamente, precisaremos
de cada valor para calcular a média. Em seguida, precisaremos outra vez de cada valor para calcular as
diferenças. E onde estão esses valores agora? Se o conjunto possuir 100 elementos, por exemplo, podemos
dar um nome a cada elemento, mas isso seria um trabalho enorme. Os arrays permitem resolver este tipo de
problema sem maiores preocupações.
3. Conhecendo a temática
3.1 Os arrays e a memória
Arrays são seqüências de variáveis de mesmo tipo, situadas em posições contíguas na memória.
Concretamente, trata-se de várias variáveis de mesmo tipo, portando o mesmo nome e diferenciadas apenas
por um ou mais índices. O número de índices de uma variável determina a dimensão do array a que ela
pertence. Assim, se para identificar uma determinada variável precisamos utilizar dois índices, então essa
variável pertence a um array de duas dimensões. Geralmente chamamos de elementos as variáveis de um
array.
Exemplo. Seja um array de 4 elementos alocado na posição (endereço) 1800. Dizer que um array está
armazenado no endereço 1800 é apenas uma forma de nos expressarmos. Na verdade, o array começa nessa
posição. Vejamos o esquema abaixo.
Endereço
Valor
...
2293600
52
2293601
10
2293602
-65
2293603
94
Ao solicitarmos em um programa a criação de um array com 4
elementos, o programa solicita ao sistema operacional espaço suficiente
na memória para a alocação desse array. Os quatro elementos devem
ficar em endereços contíguos, um vizinho ao outro, e devem ser todos
de um mesmo tipo.
Em resumo, sobre os arrays devemos guardar que:
• Quando um array é criado, ele ocupa um espaço contíguo na
memória: os endereços ocupados estão em seqüência, não existe
“buraco” entre eles.
• Todos os elementos de um array são de um mesmo tipo. Um
array de int conterá apenas variáveis de tipo int e nada mais.
...
257
3.2 Declarando um array
Como sabemos todas as variáveis em C devem ser declaradas antes de serem usadas. Comecemos com um
exemplo. Vamos declarar um array de 4 elementos de tipo long.
long x[4];
É suficiente colocar entre colchetes o número de elementos do array. Neste caso, x é o nome
do array. Não existe limite para o número de elementos de um array, salvo a memória
disponível.
3.3 Como acessar um elemento de um array
Para acessar um elemento de um array devemos escrever:
nomeDoArray[n] onde n é a posição (índice) do elemento dentro do array.
Atenção. Os elementos de um array são indexados a partir de 0 (zero). Assim, os elementos do array x
declarado acima são referenciados por: x[0], x[1], x[2] e x[3]. Observe que não existe o elemento de índice 4
na declaração acima. Uma tentativa de fazê-lo resulta em um erro de execução.
3.4 Atribuindo valores a um array
Para atribuir um valor a um elemento de um array é necessário fazer uma referência ao elemento desejado e
atribuir-lhe um valor como fazemos com qualquer outra variável. Lembremo-nos que cada elemento é uma
variável.
Exemplo.
long x[4];
x[0] = 52;
x[1] = 10;
x[2] = -65;
x[3] = 94;
3.5 Arrays e ponteiros
Existe alguma relação entre arrays e ponteiros? Se
escrevermos apenas o nome do array, sem índice,
teremos um ponteiro. Vejamos o programa ao lado.
Resultado: temos o endereço 2293600 onde começa o
array x.
Ao solicitarmos a impressão de x obtivemos o endereço
do primeiro elemento do array x, portanto o valor de x é
o endereço de outra variável, no caso x[0]. Lembremonos do conceito de ponteiro.
Se, no lugar de printf("%d", x); tivéssemos escrito
printf("%d", x[0]); teríamos obtido como reposta 52, o
valor de x[0].
/* Programa array1
#include<stdio.h>
int main()
{
long x[4];
x[0]=52;
x[1]=10;
x[0]=-65;
x[0]=94;
printf("%d", x);
getch();
return 0;
}
*/
Atenção! Como o nome de um array é um ponteiro, podemos utilizar o símbolo * para conhecer o valor do
primeiro elemento de um array. Assim, printf("%d", *x); e printf("%d", x[0]); são equivalentes.
Experimente.
Também é possível obter o valor do segundo elemento escrevendo *(x+1) (endereço do array +1). Para os
demais elementos o raciocínio é o mesmo, *(x+2) referencia o valor do terceiro elemento e assim por diante.
258
3.6 Listando um array
Para listar todos os elementos de um array devemos fazer uso de um comando de repetição. Seria muito
incômodo escrever um printf para cada elemento. Imagine só se o array possuísse 800 elementos! Vejamos
um exemplo com uso do comando for.
/* programa array2 */
#include<stdio.h>
int main(int argc, char *argv[])
{
long x[4], i = 0;
x[0] = 15;
x[1] = -45;
x[2] = 218;
x[3] = 14;
for (i = 0 ; i < 4 ; i++)
{
printf("%d\n", x[i]);
}
getch();
return 0;
}
A execução deste programa produzirá a seguinte saída:
15
-45
218
14
Nosso laço percorre todo o array com a ajuda de uma variável
de controle i. Estude o programa array2 ao lado e procure
entender o que se passa. Observe que a variável de controle i
introduzida no código vale 0, 1, 2 e 3.
Exercício. Modifique o código fonte do programa array2 de
modo que a saída seja
Listagem do array: 15 -45 218 14
Atenção! Não tente exibir o valor de x[4]. Um array com 4 elementos possui índices 0, 1, 2 e 3. Se tentarmos
exibir x[4] ocorrerá um erro de execução e o sistema operacional encerrará o programa avisando que o este
tentou acessar um endereço que não lhe pertence.
3.7 Inicializando um array
Agora que sabemos percorrer um array somos
capazes de inicializar todos os seus elementos com
zero, por exemplo, com o uso de um laço. Tente
fazê-lo antes de ver o programa ao lado.
A saída deve ser algo como:
x=0000000000
Outro modo de inicializar um array
Podemos também inicializar um array de modo mais
automático. Essa técnica consiste em escrever
x[4]={valor1, valor2, valor3, valor4} quando da
declaração do array. De modo geral, basta escrever
os valores entre chaves e separados por vírgula.
/* programa array3 – inicializa um array */
int main(int argc, char *argv[])
{
long x[10], i = 0;
// inicialização do array
for (i = 0 ; i < 10 ; i++)
{ x[i] = 0; }
// verificação – exibição dos valores de x
for (i = 0 ; i < 10 ; i++)
{
printf("x = ");
printf("%d ", x[i]);
}
getch();
return 0;
}
Melhor que isso, se fizermos x[4]={10, -32}, os dois
primeiros elementos serão iniciados com 10 e -32,
respectivamente e os demais serão inicializados com zero.
Exercício. Como inicializar todos os valores a zero utilizando a técnica acima?
3.8 Passagem de arrays como parâmetros
Nós certamente teremos necessidade de imprimir todos os valores de um array. Por que não escrevermos
uma função para executar essa operação? Isto nos permitirá ver como passar um array para uma função,
como parâmetro real, claro.
259
Será necessário passarmos duas informações para a função, o array (seu endereço) e o seu tamanho. De fato,
nossa função deverá ser capaz de inicializar um array de qualquer tamanho, por isso a necessidade de
conhecer o tamanho do array. Vejamos um código para isso.
#include<stdio.h>
// Protótipo da função de impressão
void imprimir(long *v, long comp);
int main(int argc, char *argv[])
{
long vetor[4] = {10, 15, 3}, i = 0;
// imprimir o conteúdo do vetor
imprimir(vetor, 4);
getch();
return 0;
void imprimir(long *v, long comp)
{
long i;
for (i = 0 ; i < comp ; i++)
{
printf("%d\n", v[i]);
}
}
}
Observemos que:
1. vetor em main é um ponteiro
2. v em imprimir é um ponteiro
A função imprimir toma como parâmetro um ponteiro para o tipo long e o tamanho de um array para saber
até onde estão os seus elementos.
Importante: existe outro modo de indicar que uma função recebe array. Em vez de indicar que a função
espera um long *v, podemos escrever assim:
void imprimir(long v[], long comp)
Este comando é equivalente ao usado no código apresentado acima, mas a presença dos colchetes lembra ao
programador que a função está aguardando um array. Isto permite evitar confusões. Esta forma é bastante
utilizada.
Exercícios
É importante escrever várias funções que trabalhem sobre arrays para que nos tornemos “práticos” sobre este
assunto.
1. Criar uma função de nome somaVetor que retorne a soma de todos os elementos de um array. Utilize um
return para retornar o valor calculado. Como incentivo aqui está o protótipo da função somaVetor.
long somaVetor(long vetor[ ], long comp);
2. Criar uma função mediaVetor que calcule e retorne a média aritmética dos
(Atenção. O valor de uma média é sempre um número decimal).
elementos do vetor.
3. Criar uma função copiarVetor que tenha como argumentos 2 arrays. O conteúdo do primeiro array
deverá ser copado para o segundo. Protótipo:
void copiarVetor(long v1[ ], long v2[ ], long comp);
4. Criar uma função maxVetor1 que deverá atribuir o valor 0 a cada elemento do array que lhe é passado
como parâmetro, desde que esse elemento tenha um valor que seja superior a um valor máximo também
recebido por parâmetro. Protótipo:
void maxVetor1(long v[ ], long comp, long max);
5. Criar uma função ordenaVetor que classifique os elementos de um array em ordem crescente. Se um
array inicialmente possui os elementos {21, 30, 17, 8}, nesta ordem, ao fim da operação ele deverá se
apresentar como {8, 17, 21, 30}.
260
3.9 Arrays de duas ou mais dimensões
Para utilizarmos arrays de duas ou mais dimensões precisamos declará-los como fazemos para qualquer
outra variável. A declaração de um array de duas dimensões é semelhante à declaração de arrays com uma
única dimensão. Devemos indicar explicitamente quantos elementos existem em cada dimensão. Vejamos
como declarar um array de inteiros de nome X, com 10 elementos em uma dimensão e 8 elementos em outra:
int X[10][8] ;. Simples não?
Os arrays de uma e duas dimensões recebem nomes especiais. Os arrays de uma dimensão são chamados de
vetor e os de duas dimensões recebem o nome de matriz.
Exemplo de programação usando matriz.
Vamos escrever um programa para preencher uma matriz de nome Nota, que represente as notas de 10
alunos de uma turma em 3 disciplinas. Trata-se de distribuir essas notas em uma estrutura de duas
dimensões. Acompanhe o nosso raciocínio. Consideraremos uma matriz de 3 linhas, cada linha
representando uma disciplina, e 10 colunas, uma para cada nota de um aluno. Nossa estrutura tem a seguinte
aparência.
notas
disciplinas
Como imaginamos, a matriz deve ser declarada como int Nota[3][10].
Atribuindo valor aos elementos de um array
A atribuição de valores aos elementos de uma matriz se dá do mesmo modo que para as outras variáveis já
vistas, ou seja, podemos fazê-lo por atribuição direta ou por leitura via teclado (como uso da função scanf).
Não podemos esquecer que as matrizes possuem dois índices. Assim, podemos escrever Nota[0,3]=8,5; para
indicarmos que o aluno 3 obteve 8,5 como nota na disciplina 1. As disciplinas serão representadas por 0, 1, 2
(linha 0, linha 1, linha2). Não se desespere. Lembre-se que os índices dos arrays começam em 0(zero). Por
isso, a primeira linha tem índice 0, a segunda índice 1, e assim por diante. O mesmo ocorre com relação às
colunas. Então o elemento da terceira linha e sexta coluna é representado por Nota[2,5]. A atribuição de
valores via teclado se dá como auxílio de um comando de repetição. Lembremos do que já foi dito antes:
cada conceito estudado é importante e deve ser acumulado, para uso
#include<stdio.h>
futuro, à medida que os programas vão ficando mais complexos.
int main(){
Vejamos um modelo para a leitura de dados via teclado e atribuição
int i,j;
aos elementos de uma matriz.
int X[3][4];
for (i=0; i<3; i++)
Leia o código ao lado e procure compreender cada passo.
{
Evidentemente este programa lê uma matriz 3x4, de inteiros de
printf("Leitura da linha %d\n", i);
nome X. Escreva outro programa para realizar a mesma tarefa de
for (j=0; j<4; j++)
modo que os dados de entrada apareçam na tela assim :
{
printf("elemento %d ", j);
Primeira linha : 10 23 15 17
scanf("\n%f", X[i,j]) ;
Segunda linha : 76 56 29 2
}
Terceira linha : -5 43 -2 45
}
getch();
Observe bem, após a leitura de um número, não há mudança de
return 0;
linha, a menos que esse número seja o último da sua linha.
}
Na plataforma Moodle, o aluno encontrará vários outros exemplos com matrizes assim como muitos
exercícios.
261
3.10 Teste
1. Qual das linhas abaixo cria um array de double com 10 elementos?
( ) double* vetor[10];
( ) double vetor{10};
( ) double vetor[10];
( ) double vetor[9];
2. Qual o valor do primeiro índice de um array?
( )0
( )1
( ) -1
3. Qual dos protótipos abaixo não permite a passagem do array vetor como parâmetro?
( ) void f(long vetor[], long comp);
( ) void f(long vetor, long comp);
( ) void f(long *vetor, long comp);
4. Qual o outro modo de inicializar o array vetor com estes valores?
long vetor[4];
vetor[0] = 10;
vetor[1] = 23;
vetor[2] = 505;
vetor[3] = 8;
( ) long vetor[4] = 10, 23, 505, 8;
( ) long vetor[4] = [10, 23, 505, 8];
( ) long vetor[4] = (10, 23, 505, 8);
( ) long vetor[4] = {10, 23, 505, 8};
Para não esquecer.
•
•
Não esquecer JAMAIS que um array começa no índice 0 e não no índice 1.
Quando passar um array para uma função, como parâmetro claro, passar TAMBEM o seu tamanho,
senão a função não poderá reconhecer o tamanho do array.
4. Avaliando o que foi construído
Concluímos este texto com uma apresentação sobre os arrays. Esse tipo de dados possui larga utilização na
solução de problemas que necessitam a reutilização de variáveis dentro de um mesmo programa. Conforme
visto, os arrays são utilizados na implementação de vetores e matrizes em C. Existem outros tipos de dados
que deverão ser estudados por aqueles que pretendem aprofundar-se no estudo desta linguagem. O
tratamento de caracteres, registros e arquivos (files) são assuntos fortemente recomendados para leitura. A
base de programação adquirida até aqui é suficiente para que o aluno continue o seu desenvolvimento em
programação, agora de forma autônoma.
262
5. Referências
1. Ascencio, Ana F G., Campos, Edilene A V., Fundamentos da Programação de Computadores:
Algoritmos, Pascal, C/C++ e Java, 2ª edição, Pearson Education, 2007.
2. Hutchison, Robert C., Just, Steven B., Programming using the C language. MGraw-Hill Book
Company, 1988.
3. Kernighan, Brian W., Ritche, Dennis M., Le langage C, Masson, 1986.
263
Download

Disciplina: Iniciação à Computação