Prévia do material em texto
Este material aborda o conteúdo de uma
disciplina de introdução à programação
para cursos de Ciência da Computação e
Engenharias. A linguagem de
programação utilizada é a Linguagem C.
A primeira parte engloba o histórico da
computação e os fundamentos
necessários à compreensão do mundo
digital. A segunda parte contempla os
principais elementos de programação do
paradigma imperativo e procedural.
Introdução à
Programação:
Conceitos e Práticas.
Jorge Habib Hanna El Khouri
Introdução à Programação: Conceitos e Práticas. Página 1
Introdução à Programação:
Conceitos e Práticas.
(notas de aulas)
Introdução à Programação: Conceitos e Práticas. Página 2
Introdução à Programação:
Conceitos e Práticas.
(notas de aulas)
Jorge Habib Hanna El Khouri
Professor Assistente do Centro de Engenharias e Ciências Exatas
Universidade Estadual do Oeste do Paraná
Campus de Foz do Iguaçu
Revisão Técnica:
Fev-2019
Introdução à Programação: Conceitos e Práticas. Página 3
CONTEÚDO
1. História da Computação ................................................................................. 11
1.1. Principais Eventos .......................................................................................... 11
1.2. Gerações de Computadores ........................................................................... 17
1.2.1. Primeira Geração (1938 - 1953) ...................................................................... 17
1.2.2. Segunda Geração (1952 - 1963) ..................................................................... 19
1.2.3. Terceira Geração (1962 - 1975) ...................................................................... 22
1.2.4. Quarta Geração (1972 - ...) ............................................................................. 23
2. Sistemas de Numeração................................................................................. 26
2.1. Sistema de Numeração Egípcio ..................................................................... 26
2.2. Sistema de Numeração Romano .................................................................... 27
2.3. Sistema de Numeração Maia .......................................................................... 27
2.4. Sistema de Numeração Decimal .................................................................... 29
3. Conceito de Bases .......................................................................................... 33
3.1. Sistema Decimal ............................................................................................. 33
3.2. Sistema Hexadecimal ..................................................................................... 34
3.3. Sistema Binário ............................................................................................... 35
3.4. Base Qualquer ................................................................................................ 36
3.5. Conversão entre Bases .................................................................................. 37
3.5.1. Conversão de Qualquer Base para a Base 10 ................................................ 37
3.5.2. Conversão da Base 10 para Qualquer Base ................................................... 39
3.6. Exercícios ....................................................................................................... 42
3.6.1. Conversão da Base 2n para a Base 2 ............................................................. 42
3.6.2. Conversão da Base 4 para a Base 2............................................................... 43
3.6.3. Conversão da Base 8 para a Base 2............................................................... 43
3.6.4. Conversão da Base 16 para a Base 2 ............................................................. 44
3.7. Exercícios ....................................................................................................... 45
4. Operações com Números Binários ................................................................. 46
4.1. Operações Aritméticas ................................................................................... 46
4.1.1. Soma ............................................................................................................... 46
4.1.2. Subtração ........................................................................................................ 47
4.1.3. Multiplicação ................................................................................................... 48
Introdução à Programação: Conceitos e Práticas. Página 4
4.1.4. Divisão ............................................................................................................ 48
4.1.5. Exercícios ........................................................................................................ 49
4.2. Operações Lógicas ......................................................................................... 50
4.2.1. NOT (NÃO) ..................................................................................................... 51
4.2.2. AND (E) ........................................................................................................... 52
4.2.3. OR (OU) .......................................................................................................... 55
4.2.4. XOR / OU EXCLUSIVO ................................................................................... 57
4.2.5. Exemplos ........................................................................................................ 58
4.2.6. Exercícios ........................................................................................................ 60
4.3. Operações Aritméticas Binárias com Circuitos Lógicos .................................. 60
4.3.1. Somador Completo ......................................................................................... 61
4.3.2. Subtrator Completo ......................................................................................... 62
5. Representação de Dados ............................................................................... 65
5.1. Representação de Números Inteiros .............................................................. 66
5.1.1. Inteiros Sem Sinal – Binário Puro.................................................................... 66
5.1.2. Inteiro Com Sinal ............................................................................................. 66
5.1.2.1. Módulo Sinal ................................................................................................... 66
5.1.2.2. Complemento de 1 ......................................................................................... 67
5.1.2.3. Complemento de 2 ......................................................................................... 68
5.1.2.4. Excesso de 2N-1 .............................................................................................. 69
5.1.2.5. Visualização .................................................................................................... 70
5.1.3. Soma em Complemento de 2 .......................................................................... 72
5.1.4. Exercícios ........................................................................................................ 73
5.2. Representação de Números Reais ................................................................. 74
5.2.1. Formato IEEE 754 ........................................................................................... 78
5.2.2. Exercícios ........................................................................................................ 79
5.3. Representação de Informações Textuais ....................................................... 81
6. Introdução a Arquitetura ................................................................................. 84
6.1. Processador- CPU ......................................................................................... 86
6.1.1. Seção de Controle ........................................................................................... 87
6.1.2. Seção de Processamento ............................................................................... 88
6.1.3. Barramentos .................................................................................................... 88
6.1.4. O sinal de Clock .............................................................................................. 91
6.1.5. Organização do Computador .......................................................................... 91
6.1.5.1. Pipeline ........................................................................................................... 92
6.1.5.2. Cache ............................................................................................................. 92
6.1.5.3. Outras Técnicas .............................................................................................. 94
7. Introdução à Programação ............................................................................. 95
8. O Processo de Tradução ................................................................................ 98
9. A Linguagem C ............................................................................................... 99
10. Estrutura de um Programa C ........................................................................ 102
11. Identificadores .............................................................................................. 102
12. Variáveis e Tipos de Dados .......................................................................... 103
13. Operador Atribuição ...................................................................................... 104
14. Expressões Numéricas ................................................................................. 106
14.1. Tipos Numéricos ........................................................................................... 106
14.2. Constantes .................................................................................................... 108
Introdução à Programação: Conceitos e Práticas. Página 5
14.3. Operadores Matemáticos .............................................................................. 111
14.4. Funções Matemáticas ................................................................................... 114
14.5. Exemplos ...................................................................................................... 127
15. Comandos de Entrada e Saída ..................................................................... 129
15.1. Entrada de Dados ......................................................................................... 129
15.2. Saída de Dados............................................................................................. 133
15.3. Exemplos ...................................................................................................... 138
16. Implementando Funções .............................................................................. 140
16.1. Exemplos ...................................................................................................... 141
16.2. Exercícios ...................................................................................................... 144
17. O Tipo Ponteiro ............................................................................................. 151
18. Passagem de Parâmetros ............................................................................ 155
18.1. Passagem de Parâmetro por Cópia .............................................................. 155
18.2. Passagem de Endereço como Parâmetro ..................................................... 158
18.3. Exemplo ........................................................................................................ 161
19. Expressões Lógicas ...................................................................................... 165
19.1. Tipo de Dado Boolean ................................................................................... 165
19.2. Operadores Lógicos ...................................................................................... 166
19.3. Operadores Relacionais ................................................................................ 167
19.4. Exemplos ...................................................................................................... 169
20. Instruções de Controle .................................................................................. 172
20.1. Seleção – IF-THEN-ELSE ............................................................................. 173
20.1.1. Sintaxe e Semântica ..................................................................................... 173
20.1.2. Exemplos ...................................................................................................... 175
20.2. Seleção – CASE............................................................................................ 183
20.2.1. Sintaxe e Semântica ..................................................................................... 183
20.2.2. Exemplos ...................................................................................................... 185
20.3. Repetição – FOR⋯DO .................................................................................. 190
20.3.1. Sintaxe e Semântica ..................................................................................... 190
20.3.2. Exemplos ...................................................................................................... 192
20.4. Repetição – WHILE⋯DO .............................................................................. 209
20.4.1. Sintaxe e Semântica ..................................................................................... 209
20.4.2. Exemplos ...................................................................................................... 211
20.5. Repetição – REPEAT⋯UNTIL ...................................................................... 223
20.5.1. Sintaxe e Semântica ..................................................................................... 223
20.5.2. Exemplos ...................................................................................................... 224
21. Array ............................................................................................................. 228
21.1. Vetor .............................................................................................................. 229
21.2. Sintaxe e Semântica ..................................................................................... 230
21.3. Exemplos ...................................................................................................... 232
22. Expressões Textuais .................................................................................... 254
22.1. Tipos Textuais ............................................................................................... 254
22.2. Constantes .................................................................................................... 254
22.3. Operadores ................................................................................................... 259
22.4. Funções ........................................................................................................ 260
22.5. Exemplos ...................................................................................................... 265
22.6. Matriz ............................................................................................................ 268
Introdução à Programação: Conceitos e Práticas. Página 6
22.7. Sintaxe e Semântica ..................................................................................... 268
22.8. Exemplos...................................................................................................... 271
23. Arquivos ........................................................................................................ 292
23.1. Arquivo Texto: Funções e Procedimentos ..................................................... 296
23.2. Exemplos ...................................................................................................... 303
24. Conjuntos ...................................................................................................... 324
24.1. Operadores ................................................................................................... 324
24.2. Exemplos ...................................................................................................... 326
25. Registro ........................................................................................................ 331
25.1. Sintaxe .......................................................................................................... 333
25.2. Exemplos ...................................................................................................... 336
26. Tipos Ordinais Definidos pelo Usuário .......................................................... 341
26.1. Enumerados .................................................................................................. 341
26.2. Subdomínio ................................................................................................... 344
27. Expressões Binárias ..................................................................................... 347
27.1. Operadores Binários ..................................................................................... 347
27.2. Operadores de Deslocamento de Bits ........................................................... 348
27.3. Exemplos ...................................................................................................... 350
28. Ponteiros ....................................................................................................... 356
28.1. Sintaxe .......................................................................................................... 356
28.2. Operação com Ponteiros ............................................................................... 357
28.3. Exemplos ...................................................................................................... 358
29. Estrutura de dados dinâmica ........................................................................ 368
29.1. Organização da memória de um programa em execução ............................. 368
29.2. Alocação dinâmica ........................................................................................ 370
29.3. Exemplos ...................................................................................................... 372
30. Interpretador da Linha Comando .................................................................. 381
31. Recursividade ............................................................................................... 383
31.1. Conceito ........................................................................................................ 383
31.2. Exemplos ...................................................................................................... 385
32. Referências Bibliográficas ............................................................................ 390
Introdução à Programação: Conceitos e Práticas. Página 7
FIGURAS
Figura 1: Ábaco. ............................................................................................................ 11
Figura 2: Ábaco e o seu uso. ......................................................................................... 11
Figura 3: O mecanismo de Anticítera. ........................................................................... 12
Figura 4: Réplica do mecanismo de Anticítera. ............................................................. 12
Figura 5: Esquema da máquina de Anticítera................................................................ 12
Figura 6: Tabela de Logaritmo de Napier ...................................................................... 12
Figura 7: Manuscrito Original de Schickard. .................................................................. 13
Figura 8: Réplica da Calculadora de Schickard. ............................................................ 13
Figura 9: La Pascaline. .................................................................................................. 13
Figura 10: La Pascaline. ................................................................................................ 13
Figura 11: Régua de Cálculo. ........................................................................................ 14
Figura 12: Máquina de Leibniz. ..................................................................................... 14
Figura 13: Tear Automático de Jacquard. ..................................................................... 15
Figura 14: Máquina Analítica de Babbage. .................................................................... 15
Figura 15: Formulário: Primeiro algoritmo de Ada. ........................................................ 16
Figura 16: Detalhe de parte do Formulário. ................................................................... 16
Figura 17: Cartão perfurado de Hollerith. ...................................................................... 16
Figura 18: Máquina de Tabulação. ................................................................................ 16
Figura 19: ENIAC. ......................................................................................................... 18
Figura 20: Memória de Núcleo. ..................................................................................... 20
Figura 21: Ambiente de Programação da Segunda Geração. ....................................... 21
Figura 22: Evolução no nível de Programação. ............................................................. 22
Figura 23: Principais características de Software e Hardware por geração. ................. 25
Figura 24: Circuitos Digitais: Contribuições ................................................................... 46
Figura 25: Circuito Somador Completo de 1 bit. ............................................................ 62
Figura 26: Circuito da Soma de N bits. .......................................................................... 62
Figura 27: Circuito Subtrator Completo de 1 bit. ............................................................ 64
Figura 28: Mundo Real x Mundo Digital. ....................................................................... 65
Figura 29: Métodos de Representação de Dados ......................................................... 65
Figura 30: Representação em Módulo-Sinal ................................................................. 67
Figura 31: Faixa de Representação de Números Reais. ............................................... 77
file:///C:/Dados/TP6/HABIB/COMPUTA/Material/Computacao%20I/Notas%20de%20Aula%20-%20Computacao%20I%20-%20Ling%20C%20R03.docx%23_Toc3139190
file:///C:/Dados/TP6/HABIB/COMPUTA/Material/Computacao%20I/Notas%20de%20Aula%20-%20Computacao%20I%20-%20Ling%20C%20R03.docx%23_Toc3139196
Introdução à Programação: Conceitos e Práticas. Página 8
Figura 32: Organização típica de um computador. ........................................................ 84
Figura 33: Programa na Memória RAM: Código + Dados. ............................................ 85
Figura 34: Unidade Central de Processamento típica. .................................................. 86
Figura 35: Passos na execução de uma instrução. ....................................................... 87
Figura 36: A sincronização da execução de uma instrução com o sinal de clock. ........ 91Figura 37: Execução e Pipeline. .................................................................................... 92
Figura 38: Memória Cache. ........................................................................................... 93
Figura 39: Genealogia das Linguagens – Base FORTRAN. ......................................... 95
Figura 40: Genealogia das Linguagens. ........................................................................ 96
Figura 41: Níveis de Programação. ............................................................................... 96
Figura 42: Ciclo de Desenvolvimento de Software. ....................................................... 97
Figura 43: Processo de Tradução ................................................................................. 98
Figura 44: Estilo de Programação Spaghetti. ................................................................ 99
Figura 45: Evolução no estilo de programação. .......................................................... 100
Figura 46: Analogia variável vs cadeira. ...................................................................... 104
Figura 47: Precedência de Operadores,. ..................................................................... 112
Figura 48: Estrutura de uma função. ........................................................................... 140
Figura 49: Estrutura da função getimc. ........................................................................ 143
Figura 50: Contexto da chamada da função foo. ......................................................... 156
Figura 51: Ponto 1 – Passagem por Cópia. ................................................................. 157
Figura 52: Ponto 2 – Passagem por Cópia. ................................................................. 157
Figura 53: Ponto 3 – Passagem por Cópia. ................................................................. 157
Figura 54: Ponto 1 – Passagem por ―Referência‖. ...................................................... 159
Figura 55: Ponto 2 – Passagem por ―Referência‖. ...................................................... 159
Figura 56: Ponto 3 – Passagem por ―Referência‖. ...................................................... 160
Figura 57: Ponto 1 – Calculo Raizes. .......................................................................... 162
Figura 58: Ponto 2 – Calculo Raizes. .......................................................................... 163
Figura 59: Ponto 3 – Calculo Raizes. .......................................................................... 163
Figura 60: Diagrama lógico para o cálculo das raízes de uma equação do 2º grau. ... 177
Figura 61: Instrução CASE .......................................................................................... 184
Figura 62: Repetição do tipo FOR. .............................................................................. 191
Figura 63: Estrutura de Controle do tipo FOR ............................................................. 191
Figura 64: Instrução BREAK. ....................................................................................... 204
Figura 65: Conversão Decimal - Binário ...................................................................... 212
Figura 66: Correspondência entre Definição do Vetor e a Atribuição. ......................... 232
Figura 67: Cópia de Sub-Vetor. ................................................................................... 248
Figura 68: Posição de Sub-Vetor. ................................................................................ 248
Figura 69: Eliminação de um Sub-Vetor. ..................................................................... 249
Figura 70: Comparação Escalar, Vetor e Matriz .......................................................... 268
Figura 71: Entrada/Saída de grande volume de dados sem o uso de arquivos .......... 292
Figura 72: Entrada/Saída de grande volume de dados com o uso de arquivos .......... 293
Figura 73: Visualização de um arquivo texto como sequência de caracteres. ............ 294
Figura 74: Visualização de um arquivo executável como sequência de caracteres. ... 294
Figura 75: Dado em Memória gravado em arquivo como binário ................................ 295
Figura 76: Dado em Memória gravado em arquivo como texto ................................... 295
Figura 77: Manipulação de Arquivo por um programa ................................................. 296
Figura 78: Organização dos dados da disciplina na memória ..................................... 311
Introdução à Programação: Conceitos e Práticas. Página 9
Figura 79: Abstrações típicas de uma linguagem procedural ...................................... 332
Introdução à Programação: Conceitos e Práticas. Página 10
PREFÁCIO
O conteúdo deste material é fruto de um conjunto de notas de aulas preparadas para a
disciplina introdutória à programação da grade do curso de Ciência da Computação e
posteriormente estendido para cursos de engenharias, principalmente Engenharia Elétrica
e Mecânica. A estrutura do material não está centrada em uma linguagem de
programação, mas sim no estabelecimento de uma sequência natural de conteúdos que
proporcione, desde o seu princípio, o suporte logico para a construção de programas
elementares. No avançar das temáticas o conhecimento vai sendo refinado e ao mesmo
tempo testado com problemas mais elaborados.
Antecedendo o conteúdo de programação são apresentados tópicos que ilustram os
pontos na história que marcaram a evolução da computação, onde conhecimentos das
mais diversas áreas foram combinados a fim de delinear o estado atual da tecnologia da
informação. Uma parte fundamental da computação está na compreensão da natureza
dos dados e como eles circulam no ambiente computacional. Assim, os sistemas de
numeração são abordados visando alcançar um entendimento sobre a notação binária
que predomina no mundo digital. Uma breve exposição sobre a destaca
o trânsito da informação entre as camadas de e de , e expõe a
importância de se compreender toda a transformação a que é submetido um determinado
problema até se transformar efetivamente em um programa ativo na arquitetura do
computador. Um estudante de computação ou de engenharia (relacionada ao tema) deve
ter a clara noção de todas as camadas de abstração que formam o contexto
computacional. Os principais métodos de representação de dados elementares são
expostos e proporcionam conhecimentos essenciais ao entendimento dos seus impactos
nos algoritmos.
A parte de programação estabelece uma sequência de exposição do conteúdo tanto nos
aspectos conceituais quanto nas aplicações práticas. Os recursos de programação
apresentados são os típicos da família de linguagens denominada imperativa ou
procedural, e a linguagem é a escolhida para concretizar os ensinamentos. A
grande similaridade entre as linguagens procedurais atuais faz com que a transposição
dos conhecimentos apresentados para outras linguagens seja muito facilitada.
Introdução à Programação: Conceitos e Práticas. Página 11
1. História da Computação
Este capítulo resume os principais eventos históricos que culminaram com o estágio
atual da tecnologia digital. Os desenvolvimentos ocorreram nas mais variadas áreas da
Ciência e o processo de evolução tecnológica foi gerado em grande parte pelo
cruzamento destes conhecimentos. Destaca-se como fonte de consulta para
aprofundamento dos temas aqui citados a publicação [ ] . Além disso, o
acervo do e do site , principalmente as
fotos, também serviram de fonte de consulta.
1.1. Principais Eventos
O uso de ferramentas para apoiar as atividades do homem não é recente, e desde os
primórdios de nossa história tem-se observado um constante aumento no uso de
equipamentos e instrumentos que auxiliam nastarefas normalmente realizadas por seres
humanos.
No campo da informática, o processo de evolução se deu de forma lenta durante séculos,
experimentando uma explosão tecnológica acentuada na segunda metade do século
passado.
Na busca por alternativas para o trabalho realizado pelo homem, principalmente na
mecanização do processo de contagem e na realização de trabalhos exaustivos,
obstáculos importantes foram sendo superados, a saber:
a) a.C. - uso do ábaco no vale entre os rios Tigre e Eufrates;
b) a.C. - aparecimento do ábaco chinês, chamado Suan-Pan, e do Soroban no
Japão;
Figura 1: Ábaco.
Figura 2: Ábaco e o seu uso.
c) a.C. – Alfabeto Fenício;
Introdução à Programação: Conceitos e Práticas. Página 12
d) a.C. – uso de um mecanismo denominado Anticítera1 com um complexo sistema
de engrenagens provavelmente aplicado à navegação. Foi resgatado em na
costa da ilha grega de Anticítera. O sistema de engrenagens simulava as órbitas da
lua, sol e de mais cinco planetas, além de prever eclipses da lua e do sol.
Figura 3: O mecanismo de
Anticítera.
Figura 4: Réplica do
mecanismo de Anticítera
2
.
Figura 5: Esquema da máquina de
Anticítera.
e) - Johannes Gutenberg (Mainz – Sacro Império Romano Germânico, —
de fevereiro de ), inventou a Prensa Móvel.
f) séc. - John Napier (Edimburgo – Escócia, — de abril de ) inventou o
logaritmo e uma máquina capaz de multiplicar e dividir automaticamente;
Figura 6: Tabela de Logaritmo de Napier
1
http://pt.wikipedia.org/wiki/Máquina_de_Anticítera
2
Disponível no Museu Arqueológico Nacional de Atenas (feita por Robert J. Deroski, com base em Derek J. de Solla
Price. http://pt.wikipedia.org/wiki/Máquina_de_Anticítera
http://pt.wikipedia.org/wiki/Museu_Arqueol%C3%B3gico_Nacional_de_Atenas
http://pt.wikipedia.org/w/index.php?title=Robert_J._Deroski&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=Derek_J._de_Solla_Price&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=Derek_J._de_Solla_Price&action=edit&redlink=1
Introdução à Programação: Conceitos e Práticas. Página 13
g) - Wilhelm Schickard ( de abril de , Herrenberg, Alemanha — de
outubro de , Tübingen) construiu uma calculadora mecânica, baseada em rodas
dentadas, capaz de realizar as operações básicas com números de seis dígitos e
indicar um overflow através do toque de um sino.
Figura 7: Manuscrito Original de Schickard.
Figura 8: Réplica da Calculadora de Schickard.
h) - Blaise Pascal (Clermont-Ferrand - França, de Junho de — Paris, de
Agosto de ) utilizando o princípio do ábaco, implementou uma máquina
automática de calcular - La pascaline. Efetuava somas e subtrações;
Figura 9: La Pascaline.
Figura 10: La Pascaline.
i) – Padre Willian Oughtred (Eton-Inglaterra, de Março de — Albury, de
Junho de ) inventou a régua de cálculo (Slide rule/ slipstick) baseada nos
logaritmos de Napier e divulgou o uso do sinal para multiplicação, tendo
introduzido os termos , e .
Introdução à Programação: Conceitos e Práticas. Página 14
j) – Seth Patridge, Inglês, desenvolveu a régua de cálculo deslizante. Este
dispositivo foi muito utilizado até os anos setenta;
Figura 11: Régua de Cálculo.
k) - Gottfried Wilhelm von Leibniz (Leipzig - Alemanha, de julho de —
Hanover, de novembro de ) aprimorou a máquina de Pascal e obteve uma
calculadora que somava, subtraía, multiplicava e dividia;
Figura 12: Máquina de Leibniz.
l) - Gottfried Wilhelm von Leibniz (Leipzig - Alemanha, de julho de —
Hanover, de novembro de ) publica o artigo "Explication de l'Arithmétique
Binaire" organizando o conhecimento sobre o sistema numérico binário;
m) - James Watt (Greenock, Reino Unido, 19 de Janeiro de 1736 — Heathfield Hall,
Reino Unido, 25 de Agosto de 1819) – Máquina a Vapor – Revolução Industrial –
Indústria 1.0.
n) – Joseph Marie Charles - apelido Jacquard (Lyon – França, de Julho de –
de Agosto de ), mecânico, construiu um tear automático com entrada de dados
via cartões perfurados para controlar a confecção dos tecidos e seus desenhos;
Introdução à Programação: Conceitos e Práticas. Página 15
Figura 13: Tear Automático de Jacquard.
o) – Charles Babbage (Londres - Inglaterra, de Dezembro de — Londres,
de Outubro de ) projetou a sua Máquina Analítica ou Diferencial, que possuía
memória, programa, unidade de controle e periféricos;
Figura 14: Máquina Analítica de Babbage.
p) - Ada Augusta Byron King, Condessa de Lovelace (Londres – Inglaterra, de
Dezembro de - de Novembro de ), conhecida como Ada Lovelace, fez os
primeiros estudos sobre aritmética binária. Escreveu o primeiro algoritmo para ser
processado por uma máquina, a máquina analítica de Charles Babbage.
Introdução à Programação: Conceitos e Práticas. Página 16
Figura 15: Formulário: Primeiro algoritmo
de Ada.
Figura 16: Detalhe de parte do Formulário.
q) - George Boole (Lincoln - Inglaterra, de Novembro de — Ballintemple,
de Dezembro de ) desenvolveu a teoria da Álgebra de Boole (livro: An
Investigation of the Laws of Thought), que permitiu o desenvolvimento da Teoria dos
Circuitos Lógicos;
r) - Herman Hollerith (Buffalo - EUA, de Fevereiro de — Washington, D.C.,
de Novembro de ) empresário norte-americano, construiu a Máquina de
recenseamento utilizada no censo de dos EUA. A máquina efetuava a leitura dos
cartões de papel perfurados com as informações em código (Decimal Codificado
em Binário). Os dados do censo de foram tabulados em apenas um ano. O
censo de levou oito anos para ser tabulado;
Figura 17: Cartão perfurado de
Hollerith.
Figura 18: Máquina de Tabulação.
s) - Hollerith e Watson fundam a IBM;
Introdução à Programação: Conceitos e Práticas. Página 17
t) - Alan Mathison Turing (Londres – Inglaterra, de Junho de — de Junho
de ) desenvolveu a teoria de uma capaz de resolver qualquer problema
computável. Foi denominada de ;
u) - Claude Elwood Shannon (Petoskey, Michigan – EUA, de Abril de —
de Fevereiro de ) escreve sua tese de mestrado “A Symbolic Analysis of Relay
and Switching Circuits” onde utiliza aritmética binária e Álgebra de Boole para
implementar lógicas através de circuitos com chaves e relés, tornando-se um marco
para os circuitos digitais;
v) - sob a liderança de Alan Turing foi projetado o , computador inglês
utilizado na Segunda Guerra Mundial para quebrar o segredo alemão codificado pela
máquina .
Este processo evolutivo culminou com o surgimento das primeiras máquinas capazes de
realizar cálculos mais complexos do que as operações básicas. A partir de então, várias
gerações de computadores foram surgindo até os dias atuais.
1.2. Gerações de Computadores
Na sequência são listadas as principais contribuições em cada geração de
computadores, enfatizando-se a tecnologia utilizada para a construção do hardware e a
forma de programação. Trata-se apenas de um sumário bastante resumido, que serve
como referência de termos para pesquisas mais detalhadas.
1.2.1. Primeira Geração (1938 - 1953)
A primeira geração foi marcada pela construção do primeiro computador eletrônico, o
V (Electronic Numerical Integrator and Computer), em , tornando-se um marco
na evolução da computação. O começou a operar em , sendo concluído
totalmente em e encerrando suas operações em .
O desenvolvimento do se deu na Universidade da Pensilvânia (Filadélfia, EUA),e
contou com o apoio de John von Neumann.
Introdução à Programação: Conceitos e Práticas. Página 18
Figura 19: ENIAC.
O continha válvulas, relés, resistores, capacitores o
que resultava em mais de toneladas de equipamentos distribuídos em . Seu
consumo chegava a . A entrada e saída de dados eram feitas por meio de leitora
de cartões de perfurados, enquanto que a programação se dava pela construção de
circuitos específicos para tratar o problema por ajustes manuais de chaves e conexões de
cabos.
A unidade de sincronização trabalhava com ciclos de microsegundos ( ciclos de
clock de ), sendo capaz de executar adições/subtrações ou
multiplicações ou divisões ou raiz quadrada por segundo. A memória era capaz de
conter números ( bytes).
Sua utilização inicial foi em trabalhos com soluções numéricas de problemas relacionados
com trajetórias balísticas e no desenvolvimento da bomba de hidrogênio.
Em junho de , von Neumann publicou o trabalho sobre o (Electronic Discrete
Variable Automatic Computer), e estabeleceu as bases para as arquiteturas de
computadores das gerações seguintes, atualmente conhecida como ―arquitetura de von
Neumann‖, que trazia o conceito de programa armazenado.
O esforço de guerra levou à construção, em , pelos ingleses da máquina ,
concebida para quebrar o código da máquina alemã , utilizada na transmissão de
mensagens. Pelo fato de ser uma máquina estratégica no campo militar, a sua divulgação
foi bastante limitada, o que restringiu o reconhecimento da sua importância no
desenvolvimento da computação. Os trabalhos de Alan Turing sobre computabilidade
foram consideravelmente relevantes na construção dos algoritmos da máquina
.
Introdução à Programação: Conceitos e Práticas. Página 19
Nesta geração, a programação era em linguagem de máquina composta da codificação
binária das instruções da máquina. Por exemplo, a soma de dois números produzindo um
resultado na memória tinha um código semelhante à:
O entendimento desta lógica era restrito aos profissionais com conhecimento da
arquitetura do computador.
Em resumo, a primeira geração se caracterizou por:
Surgimento do ;
Utilização de relés como dispositivos de chaveamento;
Utilização de válvulas a vácuo;
Hardware caro, grande e de difícil manutenção;
Programação em linguagem de máquina;
permitiu o uso de software.
1.2.2. Segunda Geração (1952 - 1963)
A segunda geração foi caracterizada pela invenção do por John Bardeen,
Walter Brattain e William Shockley. Em dezembro de Brattain e Moore apresentaram
o dispositivo para um grupo da Bell Labs.
Em a Bell Labs conclui o , tido como o primeiro computador totalmente
transistorizado. As vantagens dos transistores em relação às válvulas a vácuo são
principalmente: menos consumo de energia, peso e tamanho menores, reduzida perda de
calor e aquecimento, maior confiabilidade e robustez, maior duração e resistência a
impacto e vibração.
Também nesta geração foram consolidados dispositivos como as impressoras, as fitas
magnéticas e os discos para armazenamento, dentre outros.
A memória do computador passou a utilizar tecnologia de núcleo magnético, também
denominada , composta por diminutos anéis magnéticos conectados por fios
que permitiam a leitura e gravação de informação. Os valores e eram associados
ao sentido de magnetização deste núcleo.
Introdução à Programação: Conceitos e Práticas. Página 20
Figura 20: Memória de Núcleo.
O surgimento dos sistemas operacionais e das linguagens de programação acelerou o
desenvolvimento dos computadores.
A forma de programação evoluiu da para ,
onde os códigos binários eram associados a mnemônicos que indicavam o tipo de
operação realizada. Assim, a soma de dois números produzindo um resultado na memória
tinha um código semelhante à:
LINGUAGEM ASSEMBLY LINGUAGEM DE MÁQUINA
A percepção do poder de codificação em linguagens mais acessíveis levou a um
importante salto das linguagens de alto nível com a criação do FORTRAN em e
COBOL em , iniciando assim um forte ciclo de desenvolvimento de novas linguagens.
O mesmo problema podia agora ser codificado em uma de forma
muito semelhante à sua notação matemática:
LINGUAGEM DE ALTO NÍVEL LINGUAGEM ASSEMBLY LINGUAGEM DE MÁQUINA
A forma de utilização dos computadores era semelhante a de uma calculadora, onde o
programa e dados eram introduzidos via cartões perfurados, fita de papel perfurado ou fita
magnética e os resultados eram gerados para estes mesmos dispositivos. Em a
primeira impressoraVI, fabricada pela Remington-Rand, foi utilizada como dispositivo de
saída integrado ao computador , dos mesmos criadores do .
Neste período, os computadores ocupavam salas específicas que acomodavam toda a
estrutura de equipamentos e sua operação demandava técnicos especializados. É
importante lembrar que os computadores eram caros e de uso restrito a governos,
universidades e grandes corporações. Os programas eram produzidos e escritos em
formulário próprio e levados à sala de perfuração de cartão. O maço de cartões era levado
á sala com a leitora, onde um operador procedia a leitura e processamento do pacote com
Introdução à Programação: Conceitos e Práticas. Página 21
o programa. Ao encerrar o processamento deste lote de cartões então o operador se
dirigia a sala de impressora para retirar o relatório para entrega ao usuário. A seguinte
figura esboça este fluxo:
Figura 21: Ambiente de Programação da Segunda Geração.
Cada programa que necessitava de processamento tinha que seguir este fluxo. A
necessidade de melhores mecanismos de interação entre usuário e o computador,
facilidades para administrar o processamento dos programas e formas de gerenciar o
computador e seus periféricos levou ao desenvolvimento de um conjunto de programas
que foi denominado de .
Assim, surgiu a ideia de processamento em lote onde grupos de programas (maços de
cartões) eram lidos e gravados em uma fita. A fita era levada à sala de processamento e
lida para o computador, que processava toda a sequência e gerava uma saída também
em fita, cuja impressão acontecia em uma unidade separada (impressão off-line).
Em resumo, a segunda geração se caracterizou por:
invenção do transistor;
1º computador a transistor ( ) pela BELL Labs;
uso de transistores e diodos;
uso de memória de núcleo;
uso de linguagem Assembly até , e de FORTRAN e COBOL a partir daí;
surgimento dos Sistemas Operacionais para Processamento em (Lote) de
programas.
Introdução à Programação: Conceitos e Práticas. Página 22
1.2.3. Terceira Geração (1962 - 1975)
Na geração anterior, o elemento básico para construção dos processadores evoluiu da
válvula a vácuo para os transistores. Na terceira geração, o transistor continuou a ser o
dispositivo de construção elementar, porém surgiu o circuito integrado ( ou chip) em
pequena ( componentes eletrônicos por ) e média escala ( componentes
eletrônicos por ), que são circuitos eletrônicos em miniatura compostos principalmente
por dispositivos semicondutores.
A invenção do circuito integrado ou ( ) é atribuída a Jack Kilby da Texas
Instruments e Robert Noyce, da Fairchild Semiconductor, cabendo a Kilby o Prêmio Nobel
de Física no ano 2000 pela participação na criação.
O uso de circuitos integrados permitiu maior compactaçãoe confiabilidade dos
computadores com expressiva redução de custos. A tecnologia de fabricação da memória
de acesso aleatório (RAM) evolui da para dispositivos de estado sólido.
O tinha as maiores atenções no projeto de um computador, porém o
estava ganhando importância rapidamente. Foram criadas várias linguagens de
programação, entre elas , , , , , dentre outras. O processo
de produção de programas começou a ser visto como atividade de engenharia, com todo
o rigor que isto significa.
A figura a seguir ilustra como o programador foi se afastando da necessidade de
codificar diretamente em binário na medida em que surgiam novas linguagens de
programação. Inicialmente a permitiu maior produtividade na
programação, e a tradução para a era feita por um software
tradutor denominado . Em seguida, com o surgimento da
, como o , o programador codificava de forma mais
adequada aos problemas matemáticos, sendo que a tradução para o era feita
por um software denominado .
Figura 22: Evolução no nível de Programação.
Introdução à Programação: Conceitos e Práticas. Página 23
Houve importante melhoria nos compiladores, que passaram a produzir códigos tão
eficientes quanto um bom programador de linguagem assembly.
O mercado de computadores era majoritariamente dominado pelos produtos da IBM, com
destaque para os ou computadores de grande porte. Os sistemas
operacionais apresentaram significativa melhora e incorporaram recursos de
multiprogramação, time-sharing e memória-virtual. A multiprogramação permitia que o
sistema operacional trabalhasse com vários programas ativos. Neste caso, se um
determinado programa demandava dados armazenados em fita, então outro programa
entrava em processamento, até que o recurso do programa anterior fosse disponibilizado.
O , similar ao multiprocessamento, permitiu que um único computador tivesse
seu uso compartilhado por múltiplos usuários com acesso via terminal on-line. A memória
virtual é um técnica que permite que programas utilizem mais memória do que o
disponível fisicamente na memória principal. A memória secundária (em disco, fita ou
outro meio de armazenamento) é utilizada como suporte para esta técnica de
gerenciamento de memória.
Outro recurso típico da terceira geração permitiu que programas fossem carregados
diretamente dos cartões para discos. Assim, logo que um determinado programa tivesse
sua execução encerrada, o sistema operacional iria carregar um dos programas do disco
para a partição de memória destinada aos serviços em execução. Este recurso recebeu o
nome de ( ).
Em resumo, a terceira geração se caracterizou por:
uso de circuitos integrados em pequena (SSI) e média escala (MSI);
uso de circuitos impressos em multicamadas;
uso de memória de estado sólido;
linguagens de alto nível com compiladores inteligentes;
uso de multiprogramação
sistemas operacionais para uso em (tempo-compartilhado);
utilização de memória virtual.
1.2.4. Quarta Geração (1972 - ...)
Os circuitos integrados apresentaram um aumento significante na quantidade de
transistores. A tecnologia foi denominada de Integração em Escala Muito Grande
( – ), incorporando de a transistores.
As escalas seguintes de integração, ultra e super, comportam a e a
dispositivos semicondutores respectivamente.
Este nível de integração e compactação permitiu que os CI´s incorporassem cada vez
mais blocos funcionais da arquitetura do computador, ao ponto que todos os elementos
básicos de uma CPU (Central Processing Unit) coubessem em um único chip, surgindo o
microprocessador.
Introdução à Programação: Conceitos e Práticas. Página 24
A Intel teve importante papel no desenvolvimento dos microprocessadores, tendo iniciado
com o modelo Intel produzido em . Este processador era um dispositivo de
bits que continha uma ALU (Aritmetic and Logic Unit), uma unidade de controle e alguns
registradores. O marco importante foi o lançamento em , do seu primeiro
processador de bits, o .
Logo em seguida, a IBM lança em o IBM PC que se tornou padrão para
microcomputadores. O respaldo da IBM foi essencial para a aceitação e o crescimento
deste equipamento. Uma série de fabricantes passou a produzir componentes para esta
arquitetura, enquanto que a própria IBM se manteve na linha dos computadores de grande
porte, os .
Os meios de armazenamentos tais como unidades de disco e fita ocupavam espaços
consideráveis nos CPD´s (Centro de Processamento de Dados), tornando assim um
desafio produzir uma unidade que fosse compatível com uso pessoal. Neste ponto, as
unidades de discos flexíveis (floppy disk) foram fundamentais para a viabilização dos
PC´s.
Os sistemas operacionais incorporavam cada vez mais facilidades de gerenciamento dos
recursos computacionais, tais como CPU, memória, processos e arquivos. Os sistemas
operacionais DOS e Unix tiveram seu período de consolidação, sendo o DOS no ambiente
PC Intel e o UNIX basicamente nas RISC. A Microsoft iniciou sua trajetória
com o DOS, e esta plataforma estimulou o surgimento de um novo mercado de
desenvolvimento de softwares.
A necessidade de compartilhar informações e itens de impulsionou o
surgimento das redes de computador e do processamento distribuído. E os sistemas
operacionais tiveram que ampliar suas capacidades para incluir o gerenciamento de mais
estes recursos, fazendo que toda esta complexidade fosse transparente ao usuário.
O nível de integração dos circuitos integrados alavancou arquiteturas de computadores
com múltiplos processadores permitindo o processamento paralelo.
Em resumo, a quarta geração se caracterizou por:
uso de circuitos integrados em escala muito grande (VLSI);
compactação em alta densidade (computadores menores);
Linguagens de alto nível que manuseiam tanto dados escalares como vetores;
sistema operacionais em Time-sharing e Memória Virtual;
sistemas com multiprocessadores;
Processadores paralelos;
aparecimento dos microprocessadores;
uso de disquetes (floppy disk) como meio de armazenamento;
Algumas literaturas qualificam o estágio atual como sendo , principalmente pelo
forte emprego da tecnologia de . Porém, é difícil classificar uma
Introdução à Programação: Conceitos e Práticas. Página 25
geração fazendo parte dela, pois o avanço tecnológico atual traz inovações a todo o
momento. Por exemplo, poderíamos qualificar como marco extraordinário a abertura da
internet e o surgimento da e do primeiro browser em , por Tim
Berners-Lee. O interessante é que seria uma geração caracterizada principalmente por
criações de .
O quadro a seguir destaca os principais elementos de e de que
caracterizaram as gerações:
Sistemas Operacionais
Compiladores Inteligentes
Circuitos Integrados VLSI
Microprocessadores
1935 1975 1945 1955 1965
Linguagem de Máquina
Válvulas e Relés
Linguagem Assembly
Diodo e Transistor
Linguagem de Alto Nível
Circuitos Integrados
Software
Hardware
1ª geração 2ª geração 3ª geração 4ª geração
Figura 23: Principais características de Software e Hardware por geração.
Introdução à Programação: Conceitos e Práticas. Página 26
2. Sistemas de Numeração
Há cerca de anos o homem começou a ter necessidade de contar (agricultura e
pastoreio), onde os pastores de ovelhas tinham necessidades de controlar os rebanhos.
Precisavam saber se não faltavam ovelhas. (III)
Alguns vestígios indicam que os pastores faziam o controle de seu rebanho usandoconjuntos de pedras. Ao soltar as ovelhas, o pastor separava uma pedra para cada
animal que passava e guardava o monte de pedras.
Quando os animais voltavam, o pastor retirava do monte uma pedra para cada ovelha
que passava. Se sobrassem pedras, ficaria sabendo que havia perdido ovelhas. Se
faltassem pedras, saberia que o rebanho havia aumentado. Desta forma mantinha tudo
sob controle.
Uma ligação do tipo: para cada ovelha, uma pedra chama-se, em Matemática,
correspondência um a um. Provavelmente passou a utilizar o dedo como apoio à
contagem, sem, no entanto poder guardar a informação.
O processo de contagem evoluiu para o agrupamento quando tratava de grandes
quantidades, p. ex: João fez pontos ( pontos).
2.1. Sistema de Numeração Egípcio
Essa ideia de agrupar marcas foi utilizada nos sistemas mais antigos de numeração. Os
egípcios da antiguidade criaram um sistema muito interessante para escrever números,
baseado em agrupamentos.
era representado por uma marca que se parecia com um bastão |
por duas marcas ||
E assim por diante.
Símbolo descrição valor
bastão
calcanhar
rolo de corda
flor de lótus
dedo apontando
peixe
homem
Introdução à Programação: Conceitos e Práticas. Página 27
Os números eram compostos pela associação destes símbolos e a quantidade
representada era obtida pela soma dos valores individuais. Por exemplo, é
representado por:
2.2. Sistema de Numeração Romano
O sistema de numeração romano possui um conjunto símbolos, conforme tabela
abaixo, sendo que um número é representado pela combinação destes símbolos e o
valor é a soma de cada uma das parcelas.
A fim de evitar representações muito extensas, um determinado valor pode ser
representado a partir de um símbolo de referência acompanhado de símbolos a
esquerda (subtração do valor principal) ou a direita (soma ao valor principal). Por
exemplo, ( ) e ( – ).
Este sistema é bastante limitado para aplicações da matemática que vão além da
representação de números, onde mesmo as operações básicas são difíceis de serem
efetuadas.
2.3. Sistema de Numeração Maia
A cultura teve destaque nos últimos anos principalmente pelas questões relativas
ao calendário e o suposto fim dos tempos. Porém, os também dispunham de um
sistema de numeração bem estruturado que se caracterizava como posicional,
vigesimal e símbolo para o . Os valores dos símbolos vão de a ,
sendo eles:
Introdução à Programação: Conceitos e Práticas. Página 28
Os números são estruturados pela combinação destes símbolos, sendo cada símbolo
contribuindo com o seu valor multiplicado por elevado a sua posição. A soma destas
parcelas fornece o valor do número. Por exemplo, o número ,
que no sistema fica:
Este sistema permite a execução das operações básicas com a mesma facilidade que
fazemos no nosso sistema decimal. Vejamos o exemplo que segue:
A coluna indica a operação desejada. As colunas e indicam a decomposição do
e no sistema vigesimal. As colunas e
contém o e no sistema . A coluna indica a soma provisória que se
●●●●
●●●
●●
●
●●●●
●●●
●●
●
●●●●
●●●
●●
●
●●●●
●●●
●●
●
𝟐
𝟗
●●
●●●●
Introdução à Programação: Conceitos e Práticas. Página 29
obtém juntando as parcelas. Na sequência vêm os ajustes necessários, transferindo os
excedentes a , ou seja, cada unidades excedentes em uma casa equivale a uma
unidade na casa superior. Como o símbolo equivale a ●●●●●, então na coluna
são transferidas unidades ( e ●) da posição para a posição . Isto
faz surgir um símbolo ● a mais na posição . Na coluna tem-se os ●●●●● da
posição substituídos por um . Na coluna tem-se a notação convertida
para uma expressão convencional e o resultado da soma destas parcelas é indicado na
coluna , tal como se esperava da soma .
2.4. Sistema de Numeração Decimal
Os sistemas numéricos egípcio, romano e maia apresentados são apenas parte dos
muitos sistemas adotados por diferentes civilizações da antiguidade, tais como os
babilônios, gregos, chineses, hindus entre outros. Cabe destacar, que com exceção dos
maias, que habitavam a América, as civilizações da Europa, Oriente e Oriente Médio
mantinham um sistema de troca de mercadorias e comumente impérios que se
sucediam, onde um dos efeitos era a difusão de conhecimentos.
A ciência dos gregos atingiu grande desenvolvimento no século com Euclides,
cuja obra sobre Geometria (Os Elementos) é importante até hoje no ensino dessa
matemática. Os gregos tinham seu próprio sistema de numeração, com base ,
utilizando letras para representar os números, o que não facilitava os cálculos.
Os romanos, que expandiram seus domínios a partir do século ., foram aos
poucos ocupando o espaço do império deixado por Alexandre, ao mesmo tempo que
foram assimilando parte da ciência grega. O interesse dos romanos foi pelas aplicações
práticas na engenharia (construção de estradas e aquedutos) e na medicina, com
pouca contribuição na matemática.
As invasões bárbaras, nos séculos e , colocaram fim ao Império Romano no
ocidente e conduziu a Europa a um período de trevas no campo da Ciência.
Entretanto, enquanto o Império Romano declinava, uma grande civilização florescia no
Oriente, no vale do rio Indo, entre as regiões que atualmente constituem o Paquistão e
a Índia.
É importante destacar que os sistemas de numeração egípcio e romano não eram
propícios para efetuar cálculos. As dificuldades destes sistemas foram superadas pelos
hindus, que desenvolveram as bases do moderno sistema de numeração. Eles
souberam reunir três características que estavam dispersos em outros sistemas
numéricos da antiguidade, a saber:
Decimal: uso de dez símbolos para representar os dígitos de zero a nove (o
egípcio, o romano e o chinês também o eram);
Posicional: os símbolos assumem diferentes valores de acordo com a posição
que ocupa na formação do número (o babilônio e o maia também eram);
Presença do Zero: Foi um importante avanço a adoção de um símbolo para o
nada.
Introdução à Programação: Conceitos e Práticas. Página 30
Estas três características, reunidas, tornaram o sistema de numeração hindu o mais
prático de todos. Não é sem motivo que hoje ele é usado quase no mundo todo.
Enquanto os hindus, que habitavam o vale do rio Indo, desenvolviam seu sistema de
numeração, grandes acontecimentos tiveram início na Península Arábica.
No século floresce o Islamismo, e estabelece um grande império que abrangia a
região do rio Indo, a leste, o norte da África e a Península Ibérica.
A expansão Islâmica através da conquista veio na sequência de grandes impérios, tais
como o babilônio, persa, grego (macedônio) e romano. A matemática era uma das
áreas de interesse dos estudiosos árabes e o contato com o saber oriental e toda a
história de conhecimento dos impérios que o antecedeu permitiu um salto importante na
ciência.
Um grande passo foi dado na direção do extremo oriente, quando os árabes entraram
em contato com a cultura hindu e logo manifestaram particular interesse pelo seu
sistema numérico, reconhecendo sua simplicidade e praticidade.
Em outra frente, os árabes haviam ocupado parte da Península Ibérica, o que serviu de
ponte para a ciência oriental ser introduzida na Europa medieval. A expansão árabe
resultou também no surgimento, entre os séculos e , de várias universidades e
bibliotecas, desde Bagdá, no atual Iraque, até Granada e Córdoba, na atual Espanha.
Um marco relevante na tentativa de introduzir o sistema hindu-arábico na Europa foi a
publicação de Leonardode Pisa (também conhecido por ), intitulada
(O Livro do Ábaco). O pai de Fibonacci era dono de casas de comércio
ultramarino, sendo uma no norte da África. Ainda jovem Fibonacci mudou-se para esta
região e conviveu com esta nova matemática e aprofundou seus conhecimentos no
sistema árabe de numerais e cálculo. Ao retornar a Pisa, Leonardo deu sequência a
seus estudos e publicou pela primeira vez em o destacado livro, cujo conteúdo
introduzia a notação árabe, as operações fundamentais com números inteiros, cálculo
envolvendo séries, proporções, raízes quadradas e cúbicas, e uma breve abordagem
sobre geometria e álgebra. Neste livro foi apresentado o problema de crescimento da
população de coelhos de acordo com uma série que levou o nome de
. As páginas do livro tornavam difíceis a reprodução de
exemplares dado que este processo era totalmente manual, o que limitou sua difusão.IV
O início da retomada, em , do território ibérico dos mouros pelo rei da Espanha,
permitiu que populações árabes passassem ao domínio europeu, facilitando a absorção
do conhecimento escrito em árabe, principalmente os trabalhos de .
Entretanto, foram necessários vários séculos para que o sistema hindu-arábico fosse
aceito de fato na Europa. O intervalo entre a queda do império romano e a era dos
descobrimentos se caracterizou como período das trevas em função da pouca
produção de conhecimento. Não foi diferente em relação ao sistema de numeração
hindu e da nova forma de realizar as operações aritméticas, que enfrentou resistência
frente aos métodos baseados no ábaco, herança dos romanos.
Uma parte do espaço deixado pela queda do império romano no ocidente no século
foi ocupada pela Igreja, que praticamente deu continuidade a utilização do sistema
numérico criado pelos romanos. A chegada dos árabes à Europa no século trouxe
Introdução à Programação: Conceitos e Práticas. Página 31
consigo o sistema de numeração hindu, que certamente em algum momento iria levar à
confrontação de sistemas.
Basta efetuar algumas operações aritméticas utilizando algarismos romanos, para
perceber as inegáveis vantagens que o sistema numérico hindu-arábico tem sobre o
sistema romano. Mesmo assim, o novo sistema de numeração acabou prevalecendo na
Europa somente no século .
Atualmente quando comparamos as representações dos dígitos numéricos em árabe e
a forma ocidental observamos uma diferença sensível, e muitas vezes não atentamos
que possuem a mesma raiz. Porém, as grandes distâncias entre o Oriente Médio,
Península Ibérica e o extremo oriente combinadas com os séculos de história fizeram
que sequências de pequenas alterações na forma de representar os dígitos
resultassem em diferenças significativas na configuração final. Até o século todo o
material produzido era escrito a mão, e diferentes copistas possuíam diferentes
caligrafias que associadas á direção da escrita (direita para a esquerda ou esquerda
para a direita) contribuíram para as sucessivas alterações nas aparências dos dígitos
numéricos.
A invenção da imprensa por Gutenberg no século permitiu que tanto as letras
quanto os símbolos utilizados para representar os dígitos numéricos adquirissem uma
forma mais estável e definitiva.
A seguinte tabela ilustra as modificações sofridas na forma de representação dos
dígitos utilizados no sistema de numeração criado pelos hindus, adotado pelos árabes e
passado aos europeus:
Por volta do século , os hindus
representavam os algarismos assim, ainda
sem um símbolo para o nada:
No século , já com o , a
representação evoluiu para:
No século os hindus representavam os
dez dígitos assim:
No mesmo século , os árabes que
estavam no Ocidente representaram assim:
No século os árabes orientais
empregavam esta representação:
Introdução à Programação: Conceitos e Práticas. Página 32
As formas usadas pelos europeus nos
séculos e :
A representação ocidental atual:
1 2 3 4 5 6 7 8 9 0
A representação árabe escrita da esquerda
para a direita:
۱ ۲ ۳ ۴ ۵ ۶ ٩ ٨ ٧ ۰
Introdução à Programação: Conceitos e Práticas. Página 33
3. Conceito de Bases
Em nosso cotidiano lidamos com números representados na base decimal. Isto se deve a
razões e históricas, talvez fundamentadas na quantidade de dedos nas mãos. Porém, os
computadores consolidaram o sistema binário como elemento de referência no projeto dos
circuitos. A utilização da base proporciona grande facilidade na representação eletrônica
e digital dos números e . A possibilidade de representar os dígitos binários através de
valores de tensões ou correntes, por exemplo, e , simplifica a construção dos
processadores e demais elementos de uma arquitetura. Combinado com este fato há toda
uma álgebra desenvolvida no contexto da informação binária/digital, que inclui uma série
de operações sobre dados binários/lógicos que serviu de base para a construção das
principais funcionalidades computacionais (memória, circuitos aritméticos, ...).
Um sistema de numeração é determinado fundamentalmente pela base, que é o número
de símbolos utilizados.
Na notação posicional os dígitos assumem valores diferentes para cada posição que
ocupa na representação do número.
Tomando o ponto como separador para a parte inteira e a parte fracionária, temos que
a cada posição que se desloca o dígito a esquerda do ponto o seu valor é multiplicado
cumulativamente pela base. Da mesma forma, a cada posição que se desloca o dígito a
direita do ponto o seu valor é dividido cumulativamente pela base.
3.1. Sistema Decimal
O sistema decimal é o mais comumente utilizado nas operações do dia a dia. O conjunto
de dígitos utilizado é:
{ } → Base → Símbolos.
Por exemplo, partindo do número decimal , observa-se que cada dígito
possui um valor diferente dependendo da posição que ocupa no número, a saber:
Posição 2 Posição 0 Posição 1 Posição -1
Introdução à Programação: Conceitos e Práticas. Página 34
Esta característica é um dos pilares do sistema decimal utilizado, dando-lhe o aspecto
, que atribui a cada posição do número um peso distinto. Decompondo
em uma somatória, levando em conta o peso da posição, tem-se:
As parcelas mostram claramente, através da sua forma geral
, a contribuição de cada elemento do
sistema de numeração utilizado.
EXERCÍCIOS: Representar na forma de polinômio os seguintes números na base :
i.
ii.
iii.
3.2. Sistema Hexadecimal
O sistema hexadecimal tem todas as características do sistema decimal, porém utiliza
símbolos para a representação dos dígitos. Logo:
{ } → Base → Símbolos.
Os símbolos utilizados são os do sistema decimal, acrescidos dos seguintes símbolos,
com seus respectivos valores:
Símbolo
Valor
Absoluto
EXERCÍCIOS: Representar na forma de polinômio os seguintes números na base :
i.
ii.
iii.
Introdução à Programação: Conceitos e Práticas. Página 35
3.3. Sistema Binário
O sistema binário também tem todas as características do sistema decimal porém
utilizando dois dígitos:
{ } → Base → Símbolos.
É o sistema de numeração utilizado internamente pelos computadores modernos. Em
informática, cada dígito de um número representado neste sistema é denominado de bit
(binary digit). Além disso, alguns conjuntos destes bits recebem denominação específica,
a saber:
= conjunto de (exemplo: );
( ) = conjunto de , ou mais bytes, que determina a
capacidadede processamento do computador (exemplo: palavra =
bits, palavra = , Arquitetura Pentium = );
= conjunto de (KBytes);
= conjunto de (MBytes);
= conjunto de (GBytes);
De acordo com a norma : Quantities and units – Part 13: Information
science and technology, de , foram estabelecidos os seguintes prefixos:
Potência de 10 Potência de 2
Potência Valor
Nome Símbolo Nome Símbolo
quilo k kibi Ki 1024
mega M mebi Mi 1 048 576
giga G gibi Gi 1 073 741 824
tera T tebi Ti 1 099 511 627 776
peta P pebi Pi 1 125 899 906 842 624
exa E exbi Ei 1 152 921 504 606 846 976
zetta Z zebi Zi 1 180 591 620 717 411 303 424
yotta Y yobi Yi 1 208 925 819 614 629 174 706 176
Apesar de diferentes, é comum utilizar os termos das potências de para se referir às
potências de .
Introdução à Programação: Conceitos e Práticas. Página 36
EXERCÍCIOS: Representar na forma de polinômio os seguintes números na base :
i.
ii.
iii.
3.4. Base Qualquer
É possível generalizar os conceitos anteriores para números em qualquer base, bastando
selecionar a quantidade de símbolos adequada, e seguir os princípios do sistema de
numeração, cuja essência é a . Assim, uma base qualquer, pode ser
estabelecida da seguinte forma:
{ } → Base → Símbolos, onde o último símbolo
representa o valor .
Denotando um número na base com seus dígitos, temos:
,
sendo a parte inteira com dígitos e a parte fracionária com dígitos, sendo que
. Expandindo na forma de polinômio, tem-se:
⋯
⋯
Ou, colocando este sob a forma de
somatório, tem-se:
∑
Desta forma, temos todos os elementos para formar um sistema em qualquer base: os
dígitos a base e as posições em . Por exemplo, para a , teria:
{ } → Base → Símbolos/Dígitos.
Uma determinada quantidade pode ser representada por diferentes números em
qualquer base. Quanto menor a base mais dígitos são necessários para representar a
mesma quantidade.
Introdução à Programação: Conceitos e Práticas. Página 37
3.5. Conversão entre Bases
Uma determinada quantidade pode ser representada de forma distinta dependendo da
base utilizada. Por exemplo, a quantidade pode ser representada na base
como , enquanto que em outras bases sua representação será diferente. Mais
especificamente, no mundo computacional é muito comum sermos requisitados a
interpretar informações codificadas em binário ( ). Porém, estarmos habituados
ao sistema decimal, o que frequentemente demandará o uso de metodos de conversão
nos dois sentidos.
Além disso, é fundamental que um profissional da área de computação ou engenharia
relacionada, compreenda as transformações aplicadas a uma informação em todo o
seu trajeto, desde a forma natural no mundo real até a completa representação no
ambiente digital, tanto por software quanto pelo hardware.
Desta forma, serão apresentados métodos para realizar as seguintes conversões de
quantidades:
i.
ii.
iii.
3.5.1. Conversão de Qualquer Base para a Base 10
Para converter de uma base qualquer para a base , basta aplicar o
de um número na base :
∑
, e efetuar as operações tal como estamos habituados na base , e o
resultado obtido será nesta base .
Exemplos:
O número com as posições dos dígitos: (
)
O número com as posições dos dígitos: (
)
O número com as posições dos dígitos: (
)
Introdução à Programação: Conceitos e Práticas. Página 38
É importante conhecer os limites estabelecidos em termos de quantidade de dígitos em
uma determinada base. Por exemplo, sabemos que com três dígitos decimais é possível
representar quantidades distintas, que vão do número ao número .
Depreende-se que a maior quantidade representável é quando todas as posições forem
ocupadas pelo dígito de maior valor. Assim, para o sistema binário e para o tamanho de
um , a maior quantidade possível de ser representada é obtida quando todos os bits
forem iguais a . A questão é: ―Quanto é este valor em decimal ?‖. A resposta vem da
conversão deste binário para decimal, conforme segue:
De uma forma geral, o equivalente decimal do maior número possível de ser
representado por bits é obtido através da fórmula:
⋯
⋯
Observa-se que temos o somatório de uma Progressão Geométrica de termos:
Para o caso em que (1 ) temos:
.
Introdução à Programação: Conceitos e Práticas. Página 39
3.5.2. Conversão da Base 10 para Qualquer Base
A conversão da base para uma base qualquer é feita de forma distinta para a Parte
inteira e para a Parte fracionária:
i. Parte inteira:
Divide-se a parte inteira pela base desejada, obtendo-se assim um quociente
e um resto. Repita esta divisão com o quociente, e assim sucessivamente
com os demais quocientes até obter quociente zero. Agrupe os restos,
conforme exemplo a seguir, para obter o número convertido para a base
desejada, sabendo que o primeiro resto é o dígito menos significativo;
Exemplos:
47
2
1
23
2
1
11
2
1
5
2
1
2
2
0
1
2
1
0
33
2
1
16
2
0
8
2
0
4
2
0
2
2
0
1
2
1
0
O dígito menos significativo de um número inteiro é aquele que ocupa a posição ,
sendo que o dígito mais significativo é aquele que ocupa a posição de maior ordem
. Quando se trata de um número binário, os termos utilizado são
e , ou seus equivalentes em inglês:
e . Também podem ser
encontrados os termos bit de mais baixa ou alta ordem.
235
8
3
29
8
5
3
8
3
0
2030
16
14
126
16
14
7
16
7
0
Introdução à Programação: Conceitos e Práticas. Página 40
Vamos comprovar este processo utilizando a forma polinomial de representação do
número inteiro.
⋯
Efetuando a divisão deste polinômio por , temos:
⋯
⋯
⋯
Esta primeira divisão de inteiros produziu como resto o dígito . O quociente obtido foi
⋯
, que é um polinômio que representa um número composto
pelos demais dígitos, deslocados uma posição à direita
. De fato, a divisão por faz com que todos os dígitos sejam
deslocados uma posição à direita e elimina o dígito mais adireita. Assim, a lógica de
fundo é uma sequência de deslocamentos à direita e a obtenção de um dígito através
do resto. O término deste processo ocorre quando o quociente é zerado. Neste caso a
próxima divisão seria:
⋯
⋯
⋯
ii. Parte fracionária
Multiplica-se a parte fracionária pela base desejada, obtendo-se assim um
número composto por uma parte inteira e uma parte fracionária. Repita este
processo para esta parte fracionária, e assim sucessivamente até que o
resultado obtido seja um inteiro ou quando a quantidade de dígitos desejada
foi alcançada. Agrupe as partes inteiras, conforme exemplo a seguir, para
obter o número convertido para a base desejada:
Introdução à Programação: Conceitos e Práticas. Página 41
0.75 x 2 = 1.5
0.5 x 2 = 1.0
0.0378 x 16 = 0.6048
0.6048 x 16 = 9.6768
0.6768 x 16 = 10.8288
0.8288 x 16 = 13.2608
0.2608 x 16 = 4.1728
0.1728 x 16 = 2.7648
0.7648 x 16 = 12.2368
0.2368 x 16 = 3.7888
0.7888 x 16 = 12.6208
0.6208 x 16 = 9.9328
0.9328 x 16 = 14.9248
0.924805 x 16 = 14.7969
0.796875 x 16 = 12.7500
0.75 x 16 = 12.0000
0.15 x 2 = 0.3000
0. x 2 = 0.6000
0.60 x 2 = 1.2000
0.20 x 2 = 0.4000
0.40 x 2 = 0.8000
0.80 x 2 = 1.6000
0.60 x 2 = 1.2000
0.20 x 2 = 0.4000
0.40 x 2 = 0.8000
0.80
2 =
.6000
0.44 x 8 = 3.5200
0.52 x 8 = 4.1600
0.16 x 8 = 1.2800
0.28 x 8 = 2.2400
0.24 x 8 = 1.9200
0.92 x 8 = 7.3600
0.36 x 8 = 2.8800
0.88 x 8 = 7.0400
0.04 x 8 = 0.3200
0.32 x 8 = 2.5600
⋯
Observe a dízima
⋯
Podemos verificar facilmente este método de conversão partindo da expansão do
número na forma de polinômio.
⋯
⋯
Considerando somente a parte fracionária, temos:
⋯
Introdução à Programação: Conceitos e Práticas. Página 42
Multiplicando o polinômio pela base B, temos:
⋯
⋯
=
Podemos interpretar através da parcela
que o dígito foi deslocado para a
posição , ou seja, foi deslocado para assumir uma posição na parte inteira. De fato, a
multiplicação por faz com que todos os dígitos sejam deslocados uma posição à
esquerda. Logo, a lógica de fundo é uma sequência de deslocamentos à esquerda e a
obtenção de um dígito por vez. Aplica-se novamente este processo para a nova parte
fracionária . O término deste ciclo ocorre quando a parte fracionária
é zerada , ou a quantidade de dígitos obtida já alcançou a precisão estabelecida.
3.6. Exercícios
a) Converter para a base os seguintes números que estão na base informada:
i.
ii.
iii.
iv.
v.
vi.
b) Converter para base informada os seguintes números que estão na base :
i.
ii.
iii.
iv.
3.6.1. Conversão da Base 2n para a Base 2
A conversão de uma base com potência inteira de ( ) para a base pode ser feita
através do uso de uma tabela.
Sem a tabela, a conversão demandaria as seguintes transformações:
O uso da tabela evita os cálculos das duas conversões. Esta tabela de duas colunas
pode ser facilmente montada, como segue:
Introdução à Programação: Conceitos e Práticas. Página 43
i. Relacionar na primeira coluna os dígitos da base
ii. Escrever na segunda coluna o equivalente binário do valor de cada dígito
utilizando ;
3.6.2. Conversão da Base 4 para a Base 2
A tabela para a conversão da Base para a Base fica:
Ao converter um número na base para a base , basta substituir cada dígito pela
respectiva combinação de bits.
Ao converter um número na base para a base , primeiro ajusta-se a quantidade de
bits da parte inteira e parte fracionária para uma quantidade múltipla de , e em seguida
substitui-se cada agrupamento de pelo equivalente dígito da base . Este ajuste
não pode alterar o valor do número. Assim, quando necessário a parte inteira é
complementada com zeros a esquerda, e a parte fracionária é ajustada com zeros a
direita.
Exemplo:
Os agrupamentos e os complementos estão indicados em negrito.
3.6.3. Conversão da Base 8 para a Base 2
A tabela para a conversão da Base para a Base fica:
Introdução à Programação: Conceitos e Práticas. Página 44
Ao converter um número na base para a base , basta substituir cada dígito pela
respectiva combinação de bits.
Ao converter um número na base para a base , tomar os mesmos cuidados
anteriores e ajustar a quantidade de bits para um número múltiplo de .
Exemplo:
Os agrupamentos e os complementos estão indicados em negrito.
3.6.4. Conversão da Base 16 para a Base 2
A tabela para a conversão da Base para a Base fica:
Ao converter um número na base para a base , basta substituir os dígitos pela
respectiva combinação de bits.
Ao converter um número na base para a base , tomar os mesmos cuidados
anteriores e ajustar a quantidade de bits para um número múltiplo de .
Exemplo:
Introdução à Programação: Conceitos e Práticas. Página 45
A Conversão entre base e base , por exemplo, pode ser feita pela aplicação de
duas tabelas, ou seja:
3.7. Exercícios
1. Converter para a base os seguintes números que estão na base informada:
i.
ii.
iii.
iv.
v.
vi. ⋯
vii. ⋯
viii. ⋯
ix.
x.
xi. ⋯
xii.
xiii.
2. Converter para a base informada os seguintes números que estão na base :
i.
ii.
iii.
iv.
v.
3. Converter os seguintes números:
i.
ii.
Introdução à Programação: Conceitos e Práticas. Página 46
4. Operações com Números Binários
O conjunto de dígitos binários combinado com as operações aritméticas dão forma ao
sistema binário, cuja praticidade serviu de base para o surgimento da
e dos circuitos digitais. O seguinte diagrama ilustra as contribuições fundamentais para
o desenvolvimento da tecnologia digital.
Figura 24: Circuitos Digitais: Contribuições
4.1. Operações Aritméticas
Na sequência são apresentadas as quatro operações aritméticas aplicadas a números
binários. O entendimento dos detalhes relativos a estas operações contribui para uma
melhor compreensão do funcionamento dos computadores, uma vez que a aritmética
binária é a base para a lógica digital.
4.1.1. Soma
A soma de números binários é simplificada em relação à soma decimal, uma vez que é
necessário conhecer as somas possíveis para dois dígitos binários. Porém se o
resultado da soma de dois bits não couber em um bit, é produzido o bit de . Este
é o caso da soma de com , queresulta em com . Pode ocorrer que
somando com surja o bit da soma anterior, produzindo . Neste
caso, o resultado em binário é , ou em decimal. A interpretação do é que o
resultado é e ocorre o . Assim, têm-se as seguintes combinações:
Introdução à Programação: Conceitos e Práticas. Página 47
Exemplos:
11 1 1
111010101 469
+ 10010100 148
---------- ---
1001101001 617
11111111
11111111 255
+ 1 1
---------- ---
100000000 256
111111
10111100 188
+ 1111111 127
---------- ---
100111011 315
1 11
10011001 153
+ 10110100 180
---------- ---
101001101 333
4.1.2. Subtração
A subtração de números binários é semelhante à soma binária, com atenção especial
quando é necessário o empréstimo para habilitar a subtração. Assim, têm-se as
seguintes combinações:
–
–
–
–
-1 2
0
- 1
---
1
2
-1-1
0
- 1
---
0
Neste caso – não é possível,
então empresta 1 da conta futura.
Este empréstimo vale 2, o que gera
, produzindo 1 como
resultado.
Neste caso – , tendo ainda que
pagar um empréstimo anterior não
é possível, então empresta 1 da
conta futura. Este empréstimo vale
2, o que gera ,
produzindo 0 como resultado.
Exemplos:
111010101 469
- 10010100 148
---------- ---
101000001 321
11111111
11111111 255
- 1 1
---------- ---
11111110 254
222222
*******2
10111100 188
- 1111111 127
---------- ---
00111101 61
2 2
**2**2
10110100 180
- 10011001 153
---------- ---
00011011 27
O símbolo indica que houve um empréstimo para viabilizar a subtração.
Introdução à Programação: Conceitos e Práticas. Página 48
4.1.3. Multiplicação
O produto de dois números binários também fica simplificado uma vez que a tabela de
multiplicação dos dígitos se reduz a quatro combinações. A composição das parcelas é
feita através da soma. As combinações possíveis são:
Exemplos:
1101 13
x 11 3
------- ---
1101
1101
-------- ---
100111 39
11010 26
x 10 2
------- ---
00000
11010
-------- ---
110100 52
1001 9
x 101 5
------- ---
1001
0000
1001
-------- ---
101101 45
10111 23
x 100 4
------- ---
00000
00000
10111
-------- ---
1011100 92
Estes exemplos permitem constatar que a multiplicação binária é a soma de parcelas
oriundas da multiplicação de um dígito do primeiro operando pelo segundo operando.
Como o dígito será ou , logo as parcelas serão compostas ou por zero ou pelo
segundo operando. Além disso, observa-se o deslocamento dos bits a cada parcela.
Estas características reforça a simplificação proporcionada pela notação binária.
4.1.4. Divisão
A divisão entre binários, apesar de não ser tão direta quanto à multiplicação, também é
simplificada, uma vez que o quociente final é montado a partir de quocientes parciais
que vão agregando e . Além disso, cada dígito do quociente inicia uma subtração
para completar o cálculo. Desta forma, a divisão é executada através de uma série de
subtrações. Observe o seguinte exemplo:
-1+2
1 0 1 1 0
1 1
22
3
- 1 1
1 1 1
- 21
7
1 0 1
quociente 1
quociente
- 1 1
resto
0 1 0 0
- 1 1
0 0 0 1
resto
Introdução à Programação: Conceitos e Práticas. Página 49
O quadro da esquerda apresenta o processo de divisão binária e o quadro da direita
mostra os equivalentes decimais.
A divisão de por inicia pelo agrupamento da esquerda para a direita de uma
quantidade mínima de que seja divisível por , que no caso seriam três bits:
̅̅ ̅̅ ̅ .
10110 11
11 1
10
A divisão prossegue abaixando o próximo dígito e verificando se o grupo é maior ou
igual ao divisor. Não sendo, é abaixado o próximo dígito e adicionado o bit ao
quociente. Segue outro exemplo:
101101
101
101
1001
000101
101
0
O resultado pode ser certificado pelo equivalente decimal, onde neste caso é
dividido por , resultando em quociente e resto .
4.1.5. Exercícios
1. Somar os seguintes números em binário e hexadecimal:
i.
ii.
iii.
iv.
v.
vi.
vii.
2. Subtrair os seguintes números binários:
i. –
ii. –
iii. –
Introdução à Programação: Conceitos e Práticas. Página 50
3. Multiplicar os seguintes números binários:
i.
ii.
iii.
4. Dividir os seguintes números binários:
i.
ii.
iii.
4.2. Operações Lógicas
A ou combina uma série de operações,
denominadas operações lógicas, aplicáveis ao conjunto de números { } produzindo
como resultado ou . Constitui-se em um dos fundamentos mais importantes na
construção de circuitos digitais e consequentemente da arquitetura de um computador.
As operações lógicas podem ser implementadas em circuitos, denominados portas
lógicas, que por sua vez podem ser combinados na construção de circuitos mais
complexos, como circuito de soma, subtração dentre outros.
Além disso, o conjunto { }, também pode ser mapeado como { },
{ }, { }, { } ...
Na sequência a descrição das operações lógicas.
Introdução à Programação: Conceitos e Práticas. Página 51
4.2.1. NOT (NÃO)
A operação requer apenas um argumento e produz um resultado. Esta
operação retorna o complemento de (negação ou inversão) do bit de argumento.
Circuito Equivalente
A lâmpada acenderá ( ) quando a
chave estiver aberta ( ).
Circuito Eletrônico
Símbolo para o Circuito Lógico
Representação da Operação
̅
Tabela Verdade
NOT 0 1
1 0
𝐴
𝑋 �̅�
𝑉𝑐𝑐
Introdução à Programação: Conceitos e Práticas. Página 52
4.2.2. AND (E)
A operação requer dois argumentos e produz um resultado. O resultado será
quando as duas entradas forem , e para outras combinações.
Circuito Equivalente
A lâmpada acenderá ( ) quando as
chaves e forem fechadas ( ).
Circuito Eletrônico
Símbolo para o Circuito Lógico
𝐴
𝐵
𝑋 𝐴𝐵
𝑉𝑐𝑐
𝑋
Introdução à Programação: Conceitos e Práticas. Página 53
Representação da Operação
Tabela Verdade
AND 0 1
0 0 0
1 0 1
Característica da Operação
A representa todas as combinações da expressão lógica ,
onde os valores de , e estão representados nas seguintes células:
Nesta representação podemos constatar que terá valor somente quando os valores
de e também forem . As características da operação podem ser extraídas da
tabela, observando o comportamento de . Na operação , quando tem valor , a
linha correspondente em resulta inteira em , o que permite deduzir a característica
. Esta expressão pode ser lida como: independente do valor do bit , quando
fazemos um com , o resultado será sempre . Assim, o operador tem a
característica deser utilizado sempre que se precisa anular ( , , , ...)
bits.
Uma outra característica pode ser extraída analisando a linha de correspondente ao
valor de igual . Neste caso, esta segunda linha de é idêntica à linha do argumento
, o que permite deduzir a expressão , ou seja: independente do valor do bit
, quando fazemos um com , o resultado será sempre o valor do próprio bit ( ).
Observamos que a operação se assemelha à operação aritmética de
multiplicação, o que lhe confere também a denominação de produto lógico.
Introdução à Programação: Conceitos e Práticas. Página 54
Em muitas situações a tabela verdade pode ser construída no seguinte formato:
AND
A B X
0 0 0
0 1 0
1 0 0
1 1 1
A coluna expressa os resultados da operação aplicada linha a linha para cada
valor das colunas e . A tabela possui quatro linhas ( ) em virtude de estarmos
trabalhando com a expressão lógica contendo dois ( ) argumentos ( e
). Neste caso, as colunas e representam todas as combinações de e possíveis
para duas variáveis. Em uma expressão lógica envolvendo variáveis a tabela verdade
conteria linhas, e assim por diante.
Exemplo: Supor que temos um número inteiro com : , e
desejamos obter um composto desta forma: , ou seja com os quatro
bits altos iguais aos de e os quatro bits baixos zerados. Isto pode ser obtido fazendo a
operação de com um número bit a bit, tal que o resultado produza a
configuração desejada, ou seja:
ou
O número utilizado como argumento é normalmente chamado de ( ).
Introdução à Programação: Conceitos e Práticas. Página 55
4.2.3. OR (OU)
A operação ( ) requer dois argumentos e produz um resultado. O resultado será
quando as duas entradas forem , e para outras combinações.
Circuito Equivalente
A lâmpada apagará ( ) quando as chaves
e forem abertas ( ).
Circuito Eletrônico
Símbolo para o Circuito Lógico
𝐵 𝐴
𝑋 𝐴 𝐵
𝑉𝑐𝑐
𝑋 𝐴 𝐵̅̅ ̅̅ ̅̅ ̅̅
Introdução à Programação: Conceitos e Práticas. Página 56
Representação da Operação
Tabela Verdade
OR 0 1
0 0 1
1 1 1
Característica da Operação
Analisando a da operação podemos extrair suas principais
características.
Na operação , a primeira linha de , quando é igual a , é idêntica à linha do
argumento , o que permite deduzir a expressão , ou seja: independente do
valor do bit , quando fazemos um com , o resultado será sempre o valor do
próprio bit ( ).
Quando tem valor , a linha correspondente em resulta inteira em , o que permite
deduzir a característica . Esta expressão pode ser lida como: independente do
valor do bit , quando fazemos um com , o resultado será sempre . O operador
tem a característica de ser utilizado quando que se precisa ( , , ...)
bits.
Exemplo: Supor que temos um número inteiro com : , e
desejamos obter um composto desta forma: , ou seja com os quatro
bits altos iguais aos de e os quatro bits baixos ligados. Isto pode ser obtido fazendo a
operação de com um número bit a bit, tal que o resultado produza a configuração
desejada, ou seja:
ou
Introdução à Programação: Conceitos e Práticas. Página 57
4.2.4. XOR / OU EXCLUSIVO
A operação ( ) requer dois argumentos e produz um resultado. É
semelhante ao , porém quando os dois argumentos forem o resultado será . O
termo se refere ao fato que dois argumentos
produz o resultado .
Símbolo para o Circuito Lógico
Representação da Operação
Tabela Verdade
XOR 0 1
0 0 1
1 1 0
Característica da Operação
̅, onde ̅ significa a negação de
Analisando a da operação podemos extrair suas principais
características.
Na operação , a primeira linha de , quando é igual a , é idêntica à linha do
argumento , o que permite deduzir a expressão , ou seja: independente do
valor do bit , quando fazemos um com , o resultado será sempre o valor do
próprio bit ( ).
Quando tem valor , a linha correspondente em resulta em valores invertidos em
relação ao argumento , o que permite deduzir a característica ̅. O operador
Introdução à Programação: Conceitos e Práticas. Página 58
tem a característica de ser utilizado quando se quer inverter ( , , )
o valor de um bit específico.
Exemplo: Supor que temos um número inteiro com : , e
desejamos obter um composto desta forma: ̅̅ ̅̅ ̅̅ ̅̅ ̅̅ ̅̅ ̅, ou seja com os
quatro bits altos iguais aos de e os quatro bits baixos invertidos. Isto pode ser obtido
fazendo a operação de A com um número bit a bit, tal que o resultado produza a
configuração desejada, ou seja:
ou
A operação pode ser utilizada para realizar uma criptografia básica. Efetuando
, e em seguida calculando , produz-se novamente o valor de .
4.2.5. Exemplos
i. Montar a tabela verdade para a seguinte expressão lógica . Desenhar o
circuito lógico equivalente.
A tabela deverá considerar os valores possíves para , e e o resultado . A
quantidade de linhas da tabela deve conter todas as combinações para os valores de ,
e . Como cada uma destas variáveis admitem os valores , logo as três variáveis
produzirão ou linhas. Por conveniência podem ser adicionadas colunas com valores
intermediários o que confere maior clareza na obtenção da coluna com o resultado final.
Assim, a tabela final seria:
0 0 0 0 0
0 0 1 0 1
0 1 0 0 0
0 1 1 0 1
1 0 0 0 0
1 0 1 0 1
1 1 0 1 1
1 1 1 1 1
O circuito lógico equivalente é:
Introdução à Programação: Conceitos e Práticas. Página 59
ii. Montar a tabela verdade para a seguinte expressão lógica . Desenhar o
circuito lógico equivalente.
A tabela deverá considerar os valores possíves para e e o resultado . Serão ou
linhas. Assim, a tabela final seria:
0 0 1 1 0 0 0
0 1 1 0 0 1 1
1 0 0 1 1 0 1
1 1 0 0 0 0 0
Observe que o resultado de coincide com a tabela verdade do operador . De fato
a operação entre e equivale a expressão lógica .
O circuito lógico equivalente é:
Uma notação alternativa mais compacta utiliza um círculo na entrada da porta principal
para denotar a operação , conforme ilustra o seguinte diagrama:
Introdução à Programação: Conceitos e Práticas. Página 60
4.2.6. Exercícios
Montar a tabela verdade e o circuito lógico para cada uma das expressões abaixo:
i.
ii.
iii.
iv.
v.
vi.
vii.
viii.
ix.
x.
xi.
xii.
xiii. ̅̅ ̅̅ ̅̅ ̅̅
xiv.
xv.
xvi.
xvii.
4.3. Operações Aritméticas Binárias com Circuitos Lógicos
Este tópico faz a correlação entre as operações de soma e subtração de binários com
expressões lógicas e finalmente com circuitos lógicos. É importante destacar que este
material apresenta uma breve introdução aos conceitos de circuitos lógicos e não se
aprofunda nas questões relacionadas com redução de expressões.
Introdução à Programação: Conceitos e Práticas. Página 61
4.3.1. Somador Completo
É possível relacionar a operação de soma aritmética com as operações lógicas. Para
isto, temos que montar uma tabela verdade completa para a soma de com e ,
produzindocomo resultado e um novo .
A tabela abaixo mostra todas as combinações para . A coluna contém o
resultado da soma e a coluna o resultante.
0 0 0 0 0
0 0 1 1 0
0 1 0 1 0
0 1 1 0 1
1 0 0 1 0
1 0 1 0 1
1 1 0 0 1
1 1 1 1 1
Apenas com os conhecimentos até o momento apresentados, é possível observar que
a coluna pode ser obtida por . Já pode ser obtido por
para as quatro primeiras linhas, quando , e para as quatro últimas,
quando . Combinando as duas equações para , tem-se
que também pode ser escrita como . A seguinte tabela
verdade mostra que o valor de é o mesmo para as duas expressões e idêntico aos
valores obtidos na tabela verdade da soma binária completa:
0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 0 0 0 0 0
0 1 0 1 1 0 0 0 0 0
0 1 1 1 0 0 1 0 1 1
1 0 0 0 0 0 0 0 0 0
1 0 1 1 1 1 0 1 1 1
1 1 0 1 1 1 0 1 1 1
1 1 1 1 0 1 1 0 1 1
Utilizando as portas lógicas é possível montar facilmente um circuito somador completo,
que receba e produza , como na figura abaixo:
Introdução à Programação: Conceitos e Práticas. Página 62
Figura 25: Circuito Somador Completo de 1 bit.
Este circuito é capaz de realizar a soma de bit. Para somar um inteiro com mais bits,
basta sequenciar vários somadores completos. Por exemplo, se o projeto da prevê
a soma de dois inteiros de , então o circuito poderia ser similar á:
Figura 26: Circuito da Soma de N bits.
4.3.2. Subtrator Completo
A abordagem feita para o somador completo pode ser aplicada para o subtrator
completo. Para isto, temos que montar uma Tabela Verdade completa para a subtração
de por com eventual pagamento do , produzindo como
resultado e um novo indicador de .
Introdução à Programação: Conceitos e Práticas. Página 63
A tabela abaixo mostra todas as combinações para . A coluna contém o
resultado da subtração e a coluna o resultante.
0 0 0 0 0
0 0 1 1 1
0 1 0 1 0
0 1 1 0 0
1 0 0 1 1
1 0 1 0 1
1 1 0 0 0
1 1 1 1 1
Por exemplo, as linhas e com os valores e para são
entendidas como sendo as seguintes operações:
2
-1 0
0
- 1
---
1
2
-1-1
0
- 1
---
0
Neste caso – não é possível,
então empresta da conta futura.
Este empréstimo vem somando , o
que gera , produzindo
como resultado.
Neste caso – , tendo ainda que
pagar um empréstimo anterior não
é possível, então empresta da
conta futura. Este empréstimo vem
somando , o que gera
, produzindo como
resultado.
De modo similar à soma, é possível observar que a coluna pode ser obtida por
. Já pode ser obtido por para as quatro primeiras linhas
e para as quatro últimas. Combinando as duas equações para , tem-
se . Também é possível obter para as quatro
primeiras linhas e para as quatro últimas. Combinando as duas equações
para , tem-se ( ). Esta última expressão pode ser
trabalhada para reduzir o número de portas lógicas do circuito final e aproveitar uma
operação do cálculo de e chega-se a ( ).
Introdução à Programação: Conceitos e Práticas. Página 64
Um circuito lógico para o subtrator completo seria:
Figura 27: Circuito Subtrator Completo de 1 bit.
Introdução à Programação: Conceitos e Práticas. Página 65
5. Representação de Dados
A figura abaixo ilustra a separação entre as informações que circulam no mundo real e
seus equivalentes no mundo digital. Estão exemplificadas informações clássicas, tais
como números e textos. Para entrar no mundo digital é fundamental que o dado exterior
seja convertido para e através de algum método ou convenção.
Figura 28: Mundo Real x Mundo Digital.
Os métodos aqui apresentados serão agrupados conforme segue:
Representação
de Dados
Números
Inteiros
Inteiro sem
sinal
Binário Puro
Inteiro com
sinal
Módulo-
Sinal
Comple-
mento de 1
Comple-
mento de 2
Excesso de
2n-1
Reais
Textos
Figura 29: Métodos de Representação de Dados
Introdução à Programação: Conceitos e Práticas. Página 66
Na representação de dados no mundo digital é necessário definir uma quantidade de
bits disponíveis para o enquadramento do dado. No caso de números inteiros,
normalmente as arquiteturas e as linguagens disponibilizam os tamanhos com
e . Para os exemplos que seguem adotaremos o tamanho de , até
que se informe o contrário.
5.1. Representação de Números Inteiros
Na sequência são apresentados os métodos mais referenciados para representação de
números inteiros. Inicialmente será considerado tamanho .
5.1.1. Inteiros Sem Sinal – Binário Puro
A representação de inteiros sem sinal é simplesmente a conversão do número desejado
para no tamanho estabelecido.
Exemplo Representação
00001010
01011101
01111111
00000000
11111111
100000000 Não foi possível representar em 8 bits.
Assim, a faixa de representação é dada por:
5.1.2. Inteiro Com Sinal
A representação de inteiros com sinal demanda algum artifício para codificar o sinal no
padrão digital. Na sequência são apresentados os quatro métodos mais conhecidos.
5.1.2.1. Módulo Sinal
O método ( ) utiliza o bit mais significativo para codificar o sinal do
número e os demais bits codificam a magnitude do número em binário.
Introdução à Programação: Conceitos e Práticas. Página 67
Figura 30: Representação em Módulo-Sinal
Exemplo Representação
00001010
10001010
01111111
00000000
10000000
010000000 Não foi possível representar em 8 bits.
110000000 Não foi possível representar em 8 bits.
11111111
01100100
Assim, a faixa de representação é dada por:
5.1.2.2. Complemento de 1
O método consiste na conversão do valor absoluto
(magnitude) de para binário. Se o número for negativo, segue-se com a inversão dos
bits (toma-se o complemento do que falta para que cada bit de seja ).
Introdução à Programação: Conceitos e Práticas. Página 68
Exemplo Representação
00001010
: 00001010
C-1: 11110101 Ok
01111111
00000000
: 00000000
C-1: 11111111 Ok
10000000 Sinal inconsistente. Não foi possível
representar em 8 bits.
: 10000000
C-1: 01111111 Sinal inconsistente. Não foi
possível representar em 8 bits.
: 01111111
C-1: 10000000 Ok
01100100
Assim, a faixa de representação é dada por:
5.1.2.3. Complemento de 2
O método consiste na conversão do valor absoluto de para
binário. Se o número for negativo, segue-se com a inversão dos bits (toma-se o
complemento do que falta para que cada bit de seja ) e soma-se a este . O
último é desprezado.
Introdução à Programação: Conceitos e Práticas. Página 69
Exemplo Representação
00001010
: 00001010
C-1: 11110101
+ 1
--------
C-2: 11110110 Ok
01111111
00000000
:00000000
C-1: 11111111
+ 1
--------
C-2: 00000000 Ok
10000000 Sinal inconsistente. Não foi possível
representar em 8 bits.
: 10000000
C-1: 01111111
+ 1
--------
C-2: 10000000 Ok
: 01111111
C-1: 10000000
+ 1
--------
C-2: 10000001 Ok
01100100
Assim, a faixa de representação é dada por:
O método proporciona a mesma representação para o e .
5.1.2.4. Excesso de 2N-1
O método de Excesso de consiste em somar a o valor de , e então codificar
esta soma em binário. Se o resultado desta soma for negativo ou exceder a quantidade
de bits permitida, então o valor de estará fora da faixa para os definidos para a
representação.
Introdução à Programação: Conceitos e Práticas. Página 70
, verificar coerência do sinal
O método também proporciona a mesma representação para o e . A
notação de sinal na representação final é invertida em relação aos demais métodos.
Exemplo Representação
: 10001010 Ok
: 01110110 Ok
: 11111111 Ok
: 10000000 Ok
: 10000000 Ok
: 100000000 Não foi
possível representar em 8 bits.
: 00000000 Ok
: 00000001 Ok
: 11100100 Ok
Assim, a faixa de representação é dada por:
5.1.2.5. Visualização
Podemos facilmente visualizar a relação entre um número do mundo real e sua
representação no mundo digital. O quadro abaixo ilustra uma sala com várias cadeiras,
onde as cadeiras são numeradas em binário com .
Inteiro sem sinal
Binário Puro
0
1
...
127
00..00
00.01
...
01..11
128
129
...
255
10..00
10.01
...
11..11
Introdução à Programação: Conceitos e Práticas. Página 71
Do lado do mundo real uma determinada família de números quer adentrar ao mundo
digital e ocupar uma cadeira. A primeira restrição é o tamanho da sala. Não há cadeiras
para todos os elementos da família, apenas alguns poderão ocupar um assento. No
caso da família dos , a sala típica com cadeiras numeradas com
, estarão disponíveis cadeiras, desde a cadeira até a . O
de valor ocupará a cadeira , o valor ocupará a cadeira
, e assim por diante até o valor que ocupará a cadeira . O
protocolo de conversão determina qual a cadeira a ser ocupada por um determinado
valor. No caso dos vimos que o método utilizado para conversão é o
. As características da sala permitem que apenas os valores definidos por
, poderão ser representados. Caso seja necessário abranger uma faixa
maior, deverá ser utilizada uma sala com cadeiras maiores, ou seja com maior
quantidade de bits.
Para a família dos , temos uma sala para cada protocolo de
conversão: , , e
Inteiro com sinal
Módulo-Sinal
Complemento de 1
+0
+1
...
+127
+0
+1
...
+127
00..00
00.01
...
01..11
00..00
00.01
...
01..11
-0
-1
...
-127
-127
-126
...
-0
10..00
10.01
...
11..11
10..00
10.01
...
11..11
Complemento de 2
Excesso
±0
+1
...
+127
-128
-127
...
-1
00..00
00.01
...
01..11
00..00
00.01
...
01..11
-128
-127
...
-1
±0
+1
...
+127
10..00
10.01
...
11..11
10..00
10.01
...
11..11
Nas salas do e do podemos visualizar que o número
, tanto o quanto o , ocupam distintas cadeiras, o que caracteriza uma
desvantagem do método, pois uma destas cadeiras poderia ser ocupada por mais um
elemento dos . Nas salas do e do
constatamos que o , quer seja o ou o , são levados, como consequência do
método, para a mesma cadeira, o que acaba liberando um assento para o .
Introdução à Programação: Conceitos e Práticas. Página 72
5.1.3. Soma em Complemento de 2
A representação em traz uma grande vantagem para a operação de
soma, pois trata de forma transparente os inteiros sem sinal e com sinal.
Tomemos como exemplo a seguinte soma de binários, para um
Exemplo Inteiro sem Sinal
Binário Puro
Inteiro com Sinal
Complemento de 2
1111
00001111
+ 10001001
--------
10011000
15
+ 137
-----
152
15
+ -119
-----
-104
C-2 10001001
- 1
--------
C-1 10001000
|X| 01110111
X = -119
C-2 10011000
- 1
--------
C-1 10010111
|X| 01101000
X = -104
O que se observa é que independente da interpretação dada ao binário, se inteiro sem
sinal ou inteiro com sinal em , o resultado é coerente.
Na sequência são apresentados exemplos com algumas incoerências nos resultados
das somas de inteiros sem sinal e nas somas de inteiros com sinal. Pode ser observada
facilmente a lógica destas incoerências, a saber:
Quando somamos dois inteiros sem sinal e ocorre o no último bit então
estamos diante de um , ou estouro de capacidade.
Quando somamos dois inteiros com sinal deve ser observada a coerência dos
sinais: Soma de positivos deve gerar resultado positivo e soma de negativos
deve gerar resultado negativo.
Exemplo Inteiro sem Sinal
Binário Puro
Inteiro com Sinal
Complemento de 2
1111
01111001
+ 01101010
--------
11100011
121
+ 106
-----
227
(ok)
121
+ 106
------
-29
(overflow)
C-2 11100011
- 1
--------
C-1 11100010
|X| 00011101
X = -29
Introdução à Programação: Conceitos e Práticas. Página 73
11 1
11001001
+ 11101110
--------
10110111
201
+ 238
-----
183
(overflow)
-55
+ -18
------
-73
(ok)
C-2 11001001
- 1
--------
C-1 11001000
|X| 00110111
X = -55
C-2 11101110
- 1
--------
C-1 11101101
|X| 00010010
X = -18
C-2 10110111
- 1
--------
C-1 10110110
|X| 01001001
X = -73
1 11
10000101
+ 10001110
--------
00010011
133
+ 142
-----
19
(overflow)
-123
+ -114
------
+ 19
(overflow)
C-2 10000101
- 1
--------
C-1 10000100
|X| 01111011
X = -123
C-2 10001110
- 1
--------
C-1 10001101
|X| 01110010
X = -114
5.1.4. Exercícios
1. Obtenha os valores de X que a sequência de binário representa para cada um
dos métodos:
BP MS C – 1 C – 2
0000 1111
10001101
1111 0000
0111 1111
1000 0000
1111 1111
Introdução à Programação: Conceitos e Práticas. Página 74
5.2. Representação de Números Reais
A representação de números reais é mais complexa e exige a codificação de vários
componentes de . Os passos para se chegar a representação digital incluem:
i. Converter para a base
ii. Normalizar
iii. Enquadrar
Exemplo:
i. Converter para a base
0.32 x16 = 5.12
0.12 x16 = 1.92
0.92 x16 = 14.72
0.72 x
16 = 11.52
0.52 x16 = 8.32
ii. Normalizar
Normalizar equivale a reescrever o número binário na notação científica,
reposicionando o ―ponto decimal‖ a frente do primeiro dígito e fazendo a devida
compensação multiplicando-se o binário por , onde é o número de casas
deslocadas, sendo quando para esquerda e quando para a direita.
Assim, tem-se a seguinte forma geral para um número normalizado:
, onde:
, com exceção quando . Todas as partes serão .
Introdução à Programação: Conceitos e Práticas. Página 75
será um inteiro com sinal.
Os componentes que devem ser representados são o e o .
iii. Enquadrar
Agora, resta enquadrar as partes de em um determinado padrão codificado em
binário. Para os nossos exemplos, até que se coloque em contrário, adotaremos o
seguinte padrão para números reais:
Serão assim divididos:
S Expoente ( ) Mantissa ( )
o O bit de Sinal será para positivo ou para negativo
o Os bits do campo Expoente acomodarão a codificação de como inteiro
com sinal em algum dos métodos apresentados. Normalmente utiliza-se
ou ; Nos exemplos que seguem utilizaremos .
o O campo Mantissa deverá acomodar os bits que veem após o ―ponto
decimal‖ da notação normalizada.
S Expoente ( ) Mantissa ( )
0 0 0 0 0 1 1 0 1 0 1 1 1 0 0 1 0 1 0 0 0 1 1 1
o O Sinal ficou com , pois é positivo;
o O Expoente ficou , pois , e a codificação é em ;
o A Mantissa ficou , pois são os bits comportados
pela notação.
Exemplo:
i. Converter para a base
0.004600000 x 16 = 0.073600000
0.073600000 x 16 = 1.177600000
0.177600000 x 16 = 2.841600000
0
.841600000 x 16 = 13.465600000
Introdução à Programação: Conceitos e Práticas. Página 76
0.465600000 x 16 = 7.449600000
0.449600000 x 16 = 7.193600000
⋯
ii. Normalizar
iii. Enquadrar
Agora, resta enquadrar as partes de no padrão estabelecido em binário.
S Expoente (7) Mantissa (16)
1 1 1 1 1 0 0 1 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1 1
Onde a parte do Expoente foi obtida da seguinte forma:
BP: 0000111
C-1: 1111000
+ 1
--------
C-2: 1111001 Ok
Ou simplesmente:
No caso de representação de números reais, é importante conhecer a capacidade da
notação, principalmente as características de amplitude e precisão. A estrutura do
exemplo não é capaz de representar todos os números reais com precisão infinita.
Logo, a técnica utilizada mapeia apenas um subconjunto dos números reais. Considere
a reta dos reais abaixo indicada:
Introdução à Programação: Conceitos e Práticas. Página 77
Figura 31: Faixa de Representação de Números Reais.
Nem todos os pontos das retas dos reais são representados. Neste caso interessa
conhecer os maiores e os menores números representáveis. Estes limites são
calculados a partir da equação normalizada:
O Maior Positivo ( ) é obtido com o produto da maior Mantissa ( ⋯ ) com o
maior valor de , onde e a representação em . Neste caso temos a
seguinte faixa para o Expoente ( ): . Assim, tem-se:
Como:
Logo:
O Menor Positivo ( ) é obtido com o produto da menor Mantissa ( ) com o menor
valor de . Assim, tem-se:
Logo:
Introdução à Programação: Conceitos e Práticas. Página 78
5.2.1. Formato IEEE 754
O IEEE padronizou algumas representações de números reais na norma IEEE (IEC
). A maioria dos computadores modernos adota este padrão. O seguinte quadro
relaciona os tipos disponibilizados nesta norma:
Tipo Sinal Expoente Mantissa
Total
bits
Expoente
bias
Bits
Precisão
Dígitos
Significativos
Decimal
Half 1 5 10 16 15 11 ~3.3
Single 1 8 23 32 127 24 ~7.2
Double 1 11 52 64 1023 53 ~15.9
Double extended
(80-bit)
1 15 64 80 16383 64 ~19.2
Quad 1 15 112 128 16383 113 ~34.0
Detalhando o tipo , tem-se a seguinte estrutura:
S Expoente ( ) Mantissa ( )
o O bit de Sinal é para positivo ou para negativo
o Os do campo acomodam a codificação de como inteiro
com sinal utilizando um método semelhante ao , porém com um
de , que para é . A faixa de representação é de
.
o O campo acomoda , porém com um diferencial. Quando
se normaliza um número binário, a mantissa sempre iniciará com o dígito
, com exceção para o número zero. Assim, este dígito fica implícito,
sem a necessidade de ocupar fisicamente um posição na notação. Logo,
os representam uma precisão real de .
Exemplo:
i. Converter X para a base 2
11. 0.141592654 x 16 = 2.265482457
0.265482457 x 16 = 4.247719319
0.247719319 x 16 = 3.963509104
0.963509104 x 16 = 15.416145661
0.416145661
x 16 = 6.658330571
Introdução à Programação: Conceitos e Práticas. Página 79
0.658330571 x 16 = 10.533289135
0.533289135 x 16 = 8.532626152
0.532626152 x 16 = 8.522018433
0.522018433 x 16 = 8.352294922
⋯
⋯
ii. Normalizar
Observe que o primeiro bit da mantissa reposicionado na parte inteira ficará oculto na
representação.
iii. Enquadrar
Agora, resta enquadrar as partes de X no padrão binário IEEE.
Onde a parte do Expoente foi obtida da seguinte forma:
, em Excesso com bias de
Ou simplesmente:
5.2.2. Exercícios
i. Representar em números reais os seguintes números:
–
Introdução à Programação: Conceitos e Práticas. Página 80
–
ii. Que números estão representados pelas seguintes codificações binárias:
Introdução à Programação: Conceitos e Práticas. Página 81
5.3. Representação de Informações Textuais
A representação de informações textuais é feita através de um acordo que convenciona
atribuir para cada caractere um código numérico. A convenção mais aceita é dada por
uma tabela denominada Tabela (acrônimo para American Standard Code for
Information Interchange, ou em português "Código Padrão Americano para o
Intercâmbio de Informação"). Desenvolvida a partir de , grande parte das
codificações de caracteres modernas tomam esta tabela como base.
Caracteres não imprimíveis ( ):
Representados como a parte não imprimível da tabela , os caracteres de controle
tiveram sua origem nosprimórdios da computação, quando se usavam máquinas
teletipo e fitas de papel perfurado.
Binário Decimal Hexa Controle Abreviação Descrição
0000 0000 0 0 ^@ NUL Null Nulo
0000 0001 1 1 ^A SOH Start of Header Início do cabeçalho
0000 0010 2 2 ^B STX Start of Text Início do texto
0000 0011 3 3 ^C ETX End of Text - Fim do texto
0000 0100 4 4 ^D EOT End of Tape Fim de fita
0000 0101 5 5 ^E ENQ Enquire Interroga Term ID
0000 0110 6 6 ^F ACK Acknowledge Reconhecimento
0000 0111 7 7 ^G BEL Bell Campainha
0000 1000 8 8 ^H BS Back-space Espaço atrás
0000 1001 9 9 ^I HT Horizontal Tab Tabulação horizontal
0000 1010 10 0A ^J LF Line-Feed Alimenta linha
0000 1011 11 0B ^K VT Vertical Tabulation Tabulação vertical
0000 1100 12 0C ^L FF Form-Feed Alimenta formulário
0000 1101 13 0D ^M CR Carriage-Return Enter
0000 1110 14 0E ^N SO Shift-Out Saída do shift
0000 1111 15 0F ^O SI Shift-In Entrada no shift
0001 0000 16 10 ^P DLE Data-Link Escape
0001 0001 17 11 ^Q DC1 Device-Control 1
0001 0010 18 12 ^R DC2 Device-Control 2
0001 0011 19 13 ^S DC3 Device-Control 3
0001 0100 20 14 ^T DC4 Device-Control 4
0001 0101 21 15 ^U NAK Neg-Acknowledge Não-reconhecimento
0001 0110 22 16 ^V SYN Synchronous Idle
0001 0111 23 17 ^W ETB End-of-Transmission
0001 1000 24 18 ^X CAN Cancel
0001 1001 25 19 ^Y EM End-Of-Medium
0001 1010 26 1A ^Z SUB Substitute
0001 1011 27 1B ^[ ESC Escape
0001 1100 28 1C ^\ FS File Separator
0001 1101 29 1D ^] GS Group Separator
http://pt.wikipedia.org/wiki/Bin%C3%A1rio
http://pt.wikipedia.org/wiki/Sistema_de_numera%C3%A7%C3%A3o_decimal
http://pt.wikipedia.org/wiki/Sistema_hexadecimal
http://pt.wikipedia.org/wiki/Caractere_nulo
http://pt.wikipedia.org/wiki/SOH
http://pt.wikipedia.org/wiki/STX
http://pt.wikipedia.org/wiki/ETX
http://pt.wikipedia.org/w/index.php?title=EOT&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=ENQ&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=ACK&action=edit&redlink=1
http://pt.wikipedia.org/wiki/BEL
http://pt.wikipedia.org/w/index.php?title=BS_%28ascii%29&action=edit&redlink=1
http://pt.wikipedia.org/wiki/HT
http://pt.wikipedia.org/wiki/Line_Feed
http://pt.wikipedia.org/w/index.php?title=VT_%28ASCII%29&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=FF_%28ASCII%29&action=edit&redlink=1
http://pt.wikipedia.org/wiki/CR
http://pt.wikipedia.org/wiki/SI_e_SO_%28ascii%29
http://pt.wikipedia.org/wiki/SI_e_SO_%28ascii%29
http://pt.wikipedia.org/w/index.php?title=DLE&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=DC1&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=DC2&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=DC3_%28ascii%29&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=DC4&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=NACK&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=SYN&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=ETB&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=CAN_%28ascii%29&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=End-Of-Medium&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=Substitute&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=ESC_%28ascii%29&action=edit&redlink=1
http://pt.wikipedia.org/wiki/FS
http://pt.wikipedia.org/wiki/GS
Introdução à Programação: Conceitos e Práticas. Página 82
0001 1110 30 1E ^^ RS Record Separator
0001 1111 31 1F ^_ US Unit Separator
0111 1111 127 7F ^? DEL Delete
Caracteres não imprimíveis ( ), Caracteres imprimíveis ( ) e Caracteres
Estendidos ( ):
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 0 NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI
1 1 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US
2 2 ! " # $ % & ' ( ) * + , - . /
3 3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 4 @ A B C D E F G H I J K L M N O
5 5 P Q R S T U V W X Y Z [ \ ] ^ _
6 6 ` a b c d e f g h i j k l m n o
7 7 p q r s t u v w x y z { | } ~ •
8 8 Ç ü é â ä à å ç ê ë è ï î ì Ä Å
9 9 É æ Æ ô ö ò û ù ÿ Ö Ü ø £ Ø × ƒ
10 A á í ó ú ñ Ñ ª º ¿ ® ¬ ½ ¼ ¡ « »
11 B ░ ▒ ▓ │ ┤ Á Â À © ╣ ║ ╗ ╝ ¢ ¥ ┐
12 C └ ┴ ┬ ├ ─ ┼ ã Ã ╚ ╔ ╩ ╦ ╠ ═ ╬ ¤
13 D ð Ð Ê Ë È ı Í Î Ï ┘ ┌ █ ▄ ¦ Ì ▀
14 E Ó ß Ô Ò õ Õ µ þ Þ Ú Û Ù ý Ý ¯ ´
15 F ± ‗ ¾ ¶ § ÷ ¸ ° ¨ · ¹ ³ ² ▔
Caracter . Caracter .
Exemplo: O texto seria codificado como uma sequência de bytes
equivalentes aos seguintes códigos:
J o a o d a S i l v a
74 111 97 111 32 100 97 32 83 105 108 118 97
http://pt.wikipedia.org/w/index.php?title=RS_%28ascii%29&action=edit&redlink=1
http://pt.wikipedia.org/w/index.php?title=Unit_Separator&action=edit&redlink=1
http://pt.wikipedia.org/wiki/Delete
Introdução à Programação: Conceitos e Práticas. Página 83
Exemplo: O texto seria codificado com os binários
equivalentes aos seguintes códigos:
A v B r a s I l , 3 2 2 . A p t º 1 0 6 C
65 118 32 66 114 97 115 73 108 44 32 51 50 50 46 32 65 112 116 186 32 49 48 54 67
Os caracteres estendidos podem ser diferentes dependendo da fonte. Neste último
exemplo, os códigos foram obtidos através de um recurso da planilha Excel, o que
justifica algumas diferenças em relação à tabela acima.
Introdução à Programação: Conceitos e Práticas. Página 84
6. Introdução a Arquitetura
A arquitetura de um computador é um modelo da organização e funcionamento de um
sistema de processamento. A descrição da arquitetura destaca as funções dos
componentes básicos de um computador, a interconexão destes componentes e o
modo como os componentes interagem. A maioria das arquiteturas está baseada na
proposta de :
Figura 32: Organização típica de um computador.
A dinâmica da execução de um programa inicia quando o usuário submete um
determinado arquivo executável para ser processado pelo computador. Inicialmente
este programa está contido em um arquivo e o primeiro passo é carrega-lo para a
memória principal ( – ) onde ocupará um espaço
designado pelo Sistema Operacional. Neste espaço, uma área conterá os bytes
correspondentes às instruções do programa e outra área é reservada ao
armazenamento de resultados provenientes da execução destas instruções. Esta última
área também é denominada área de dados. A seguinte figura ilustra de forma
simplificada o uso da memória por um programa em execução:
Introdução à Programação: Conceitos e Práticas. Página 85
Figura 33: Programa na Memória RAM: Código + Dados.
A execução do programa inicia pelo processamento da instrução que estiver no
endereço de memória designado como início do código. A primeira instrução é então
levada da memória para a onde é tratada e processada, e quando for o caso, um
resultado é armazenado na memória ou na própria . Em seguida o mesmo
tratamento é dado para a segunda instrução e assim até o término do programa,
quando o mesmo é liberado da memória.
A gestão deste ambiente é feita pelo Sistema Operacional, enquanto que a é
responsável pelo ciclo de execução da instrução. Observa-se que durante a execução
de um programa há um forte trânsito de dados entre a e a memória principal
( ). A trabalha em uma velocidade muito superior a velocidade da memória
RAM. Assim, durante a execução do programa a passa muito tempo aguardando
os bytes chegarem da/na , o que acarreta desperdício deste recurso precioso que
é a . Este limitante onde é comumente chamado de Gargalo de Von
Newmann.
Segue uma descrição resumida dos elementos que formam a arquitetura apresentada:
Processador ( ) é o componente ativo, controlador de todo o sistema. É o
processador que realiza todas as operações sobre os dados, de acordo com oindicado pelas instruções no código do programa.
memória principal ( ) armazena as instruções que são executadas pelo
processador, e os dados que serão manipulados.
interfaces de entrada e de saída ( ) são as portas de comunicação para o
mundo externo, às quais estão conectados os dispositivos periféricos tais como
vídeo, teclado, discos e impressora.
RAM
A: 1111
B: 1110
C: 1101
X: 1100
Y: 1011
Z: 1010
T: 1001
mov eax, A 1000
add eax, B 0111
mov C, eax 0110
mov ebx, eax 0101
mov eax, X 0100
sub eax, Y 0011
mov Z, eax 0010
add ebx, eax 0001
mov T, eax 0000
D
A
D
O
S
IN
S
T
R
U
Ç
Õ
E
S
(c
ó
d
ig
o
)
Introdução à Programação: Conceitos e Práticas. Página 86
Estes componentes estão interconectados por meio de um , através
do qual o processador realiza o acesso a instruções e dados armazenados na
memória principal.
É também através deste mesmo barramento que o processador recebe ou envia
dados de ou para as interfaces de entrada/saída.
Instruções e dados estão armazenados em , na memória
principal.
6.1. Processador - CPU
A estrutura básica de um processador é composta normalmente por uma
e uma . A figura a seguir ilustra uma –
Central Processing Unit – simplificada:
Figura 34: Unidade Central de Processamento típica.
A pode ser vista como uma fábrica que processa matéria prima gerando produtos.
Neste cenário a é a responsável pelo gerenciamento da fábrica,
incluindo a entrada da matéria prima, a fabricação do produto e a entrega deste
produto. Cada instrução recebida pela se assemelha a chegada
de uma ordem de serviço para fabricação de um produto. Inicialmente o gerente da
fábrica busca compreender a instrução de fabricação do produto, partindo em seguida
Introdução à Programação: Conceitos e Práticas. Página 87
para o preparo da linha de produção para a fabricação, e uma vez pronto, o produto é
encaminhado para o depósito, de onde tomará seu destino final.
6.1.1. Seção de Controle
As operações básicas que ocorrem dentro da seção de processamento são todas
comandadas pela unidade de controle. Ao efetuar a busca da instrução, a unidade de
controle interpreta a instrução de modo a identificar quais as operações básicas que
devem ser realizadas, bem como ativa os sinais de controle que fazem uma operação
básica de fato acontecer.
Os sinais de controle que são ativados, bem como a sequência com que são ativados,
dependem de cada instrução em particular.
Assim, a execução de uma instrução envolve a realização de uma sequência de
passos, que podemos chamar de . Em geral, a execução de uma
instrução envolve quatro passos, como mostra a seguinte:
decodificação execuçãobusca resultado
Figura 35: Passos na execução de uma instrução.
No primeiro passo, denominado busca ( ), o processador realiza o acesso
ao código binário da instrução, armazenado na memória principal.
A etapa seguinte é a decodificação ( ) da instrução, na qual as
informações contidas no código da instrução são interpretadas.
No terceiro passo, denominado execução, no qual a operação indicada pela
instrução (por exemplo, uma operação na ) é efetuada.
Finalmente no quarto passo, chamado resultado ( ), é armazenado em um
registrador ou na memória o resultado produzido pela instrução.
Cada passo de execução envolve a realização de várias operações básicas.
transferência de dados entre os registradores e a ;
transferência de dados entre os registradores;
transferência de dados entre os registradores e a memória;
operações aritméticas e lógicas realizadas pela .
Introdução à Programação: Conceitos e Práticas. Página 88
6.1.2. Seção de Processamento
A seção de processamento é formada basicamente pela unidade lógica e aritmética
( ) e por diversos registradores.
A realiza as operações aritméticas, tais como adição e subtração, e operações
lógicas, tais como .
Os registradores são utilizados para armazenar informações internamente no
processador. Um registrador pode ser utilizado tanto para acesso de leitura quanto
para acesso de escrita. Os diversos registradores possuem um uso bem definido
dentro da arquitetura, e de uma maneira geral podem ser classificados em três tipos:
registradores de uso geral,
registradores de uso específico e
registradores auxiliares.
Os registradores de uso geral normalmente são usados para armazenar dados que
serão processados pela , bem como resultados produzidos pela . O
registrador de estado (status register) associado à é um registrador de uso
específico, e contém informações sobre o resultado produzido pela . Este
registrador possui bits sinalizadores que são ativados ou desativados3 de acordo com o
tipo de resultado produzido pela ( ...).
Um outro exemplo de registrador de uso específico é o contador de programa
( ), cuja função é guardar o endereço da locação de memória onde se
encontra a próxima instrução a ser executada pelo processador.
6.1.3. Barramentos
A interligação entre a e os registradores é feita através de barramentos internos.
Os barramentos internos permitem a transferência de dados dos registradores para a
e permite a transferência do resultado produzido pela , de um registrador
temporário, para outro registrador.
Uma arquitetura de processador é uma arquitetura de n bits quando todas as
operações da podem ser realizadas sobre operandos de até .
3
Considera-se aqui que um bit é ativado quando ele recebe o valor lógico 1, sendo desativado ao receber o valor
lógico 0.
Introdução à Programação: Conceitos e Práticas. Página 89
Normalmente, em uma arquitetura de os registradores de dados e os
barramentos internos também são de , de forma a permitir que os dados sejam
armazenados e transferidos de forma eficiente.
A arquitetura Intel, desde o até o Pentium, inclui três barramentos distintos:
Barramento de Dados ( ), Barramento de Endereço ( ) e
Barramento de Controle ( ).
Barramento de Dados: por onde trafega informação qualificada como dado;
Barramento de Endereço: trafega informação qualificada como endereço;
O movimento de dados é uma das instruções mais utilizadas em um programa.
Equivale ao comando de atribuição em uma linguagem de alto nível, como Pascal e C.
É comum trazer dados da memória para a e vice-versa.
Exemplo: Supondo que queiramos executar a seguinte instrução:
[ ]
Que corresponde a guardar o número na célula de memória do endereço . Os
seguintes passos serão realizados:
i. Colocar no barramento de endereço;
ii. Colocar no barramento de dados;
iii. Ativar o sinal no barramento de controle;
iv. Aguardar enquanto o controlador da memória armazena o dado na célula de
indicada;
v. Desativar o sinal no barramento de controle;
Esta sequência culmina com uma ordem ao controlador da memória em pegar estas
informações dos barramentos e efetuar a operação de gravar na memória.
De modo semelhante, a seguinte instrução: [ ], que determina que a
informação contida no endereço da memória seja copiada para um registrador,
iniciará a seguinte sequência de utilização dos barramentos:
i. Colocar no barramento de endereço;
ii. Ativar o sinal no barramento de controle;
iii. O controlador da memória lê este pedido e copia a informação solicitada para o
barramento de dados;
iv. Transferir o número que está no barramento de dados para o Registrador;
v. Desativar o sinal no barramento de controle
Introdução à Programação: Conceitose Práticas. Página 90
O tamanho em bits dos barramentos de dados e de endereço influenciam diferentes
aspectos da arquitetura.
O tamanho do barramento de dados está ligado diretamente a capacidade de
transferência de informações entre partes distintas da e entre a e outros
componentes do sistema de processamento, como a memória . Por exemplo, se
deseja trazer da memória para um registrador da , e o barramento de
dados comporta simultâneos, então este trabalho será feito em duas instruções,
Caso este mesmo barramento comportasse simultâneos o trabalho seria feito
em uma instrução.
O tamanho do barramento de endereços influencia diretamente na capacidade de
endereçamento físico de memória . Uma analogia pode ser feita com os Códigos
de Endereçamento Postal (CEP). Se cada cidade tivesse um CEP com apenas dígitos
decimais, então este sistema permitiria endereçar até cidades ( ).
Quando relacionamos cidades com células ( ) de memória e o CEP com a
informação colocada no barramento de endereços identificando um destino na
memória, logo vemos que quanto mais bits estiverem disponíveis no barramento de
endereços mais bytes de memória poderão ser endereçados. Por exemplo: se tivermos
de tamanho do barramento de endereços, então a capacidade de endereçamento
será de células (bytes). O endereço da primeira célula seria e da última
.
Na arquitetura Intel o tamanho dos barramentos evoluiu da seguinte forma:
Processador
Barramento de Endereço
Tamanho dos
Registradores
Barramento de
Dados
Tamanho Quantidade de Bytes
de Memória
8086 20 16 16
80286 24 16 16
80386 32 32 32
80486 32 32 32
Pentium 32/36/644 32/64 32/64
4
Primeiras implementações utilizam parte dos 64 bits. O uso integral permitiria 16 EBytes,
http://en.wikipedia.org/wiki/X86-64
Introdução à Programação: Conceitos e Práticas. Página 91
6.1.4. O sinal de Clock
Para atender as relações de tempo requeridas na ativação dos sinais de controle, a
unidade de controle opera em sincronismo com um sinal de .
1 ciclo
clock
leitura decodificação execução resultado
Figura 36: A sincronização da execução de uma instrução com o sinal de clock.
A execução de uma instrução consome certo número de ciclos de . O número de
ciclos de por instrução não é o mesmo para todas as instruções, já que cada
instrução pode envolver um número diferente de operações básicas em cada passo de
execução.
O tamanho do ciclo de é um dos fatores que determinam diretamente o
desempenho de um processador. Quanto menor o tamanho do ciclo de , menor
será o tempo de execução das instruções, e assim maior será o número de instruções
executadas por unidade de tempo.
Em um processador com frequência de clock de , o período será:
A frequência de clock dos processadores Intel era de nos primeiros
processadores chegando a no .
6.1.5. Organização do Computador
A busca por melhor desempenho impulsionou não só o desenvolvimento de
processadores com melhores características de frequência de , tamanho de
barramento, tamanho de registradores e quantidade de memória, mas também levou a
pesquisa de novos elementos de arquitetura que pudessem impactar no desempenho
do processador e na sua interação com a memória principal.
Segue um resumo das principais técnicas.
Introdução à Programação: Conceitos e Práticas. Página 92
6.1.5.1. Pipeline
Uma destas técnicas foi inspirada no conceito da linha de produção. Foi visto que o
processamento de uma instrução na passa pelas etapas de: , ,
e ; ou Busca, Decodificação, Execução e Resultado. Assim, uma
instrução quando trazida da memória para a ocupava a até o término da sua
execução. Então, os projetistas trabalharam no redesenho da de tal forma que os
circuitos fossem reagrupados em partes independentes, uma para cada etapa do ciclo
de execução. Esta técnica, inspirada na linha de produção, foi denominada de .
O seguinte diagrama ilustra o processamento de instruções na CPU com pipeline.
Figura 37: Execução e Pipeline.
Inicialmente uma instrução ocupa a unidade de Busca, e na sequencia a unidade de
Decodificação, liberando o estágio anterior, que pode ser ocupada pela instrução .
Enquanto avançam de estágio novas instruções podem ir ocupando os estágios
liberados. Neste exemplo, vemos que no tempo quatro instruções ocupam a ,
melhorando sensivelmente o desempenho do processamento.
A pipeline perde desempenho quando processa instrução de desvio, e somente terá a
confirmação que o desvio será feito no estágio de execução, e as instruções
anteriormente trabalhadas serão desperdiçadas. Isto se deve ao fato de que o desvio
para outro trecho do programa demandará uma nova sequencia de instruções.
Processadores modernos concentram esforços para contornar este problema,
geralmente adotando mais de uma pipeline.
6.1.5.2. Cache
Citamos anteriormente a existência de um limitante derivado do aspecto onde
, denominado . Para assegurar a disponibilidade de uma
Introdução à Programação: Conceitos e Práticas. Página 93
grande quantidade de memória , fundamental às aplicações atuais, os fabricantes
tiveram que utilizar tecnologias que contrastam custo quantidade tempo de
acesso. Para obter um custo reduzido e permitir o uso de grandes quantidades de
memória, foi utilizada tecnologia de memória que resulta em maior tempo de acesso,
quando comparada aos tempos de uma . O ideal seria que a memória tivesse
o mesmo desempenho de um Registrador da . Porém, os registradores são poucos
e específicos para alguns fins.
Desta forma, os projetistas avaliaram o comportamento dos programas quando estão
sendo executados e observaram um aspecto, onde trechos dos programas quando
processados permaneciam manipulando segmentos bem limitados (endereços
próximos) da memória por um longo tempo. É importante destacar que um
programa na memória tem o seu fluxo de processamento composto basicamente por
instruções executadas em sequencia ou por instruções de desvios. As instruções de
desvios normalmente se referem a uma seleção de caminhos de processamento ou
uma iteração. É neste último caso que situações típicas envolvem o processamento
repetitivo de uma área de memória com endereços próximos.
Logo os projetistas concluíram que era possível desenvolver uma memória com tempo
de acesso menor que o tempo de acesso da , e em menor quantidade, o suficiente
para acomodar o trecho da que está sendo referenciado num determinado
momento.
Figura 38: Memória Cache.
Assim, a memória se situa entre a e a memória , e possui um chip
controlador que implementa uma política que decide sobre a ocupação deste espaço de
memória. Esta lógica tem que decidir, quando encontra um acesso a um endereço da
não disponível no seu domínio, como se dará a substituição para que este novo
bloco passe a ocupar a .
Um programa com excesso de desvios para trechos de códigos que manipulam áreas
de memória distintas degradam o desempenho, uma vez que são produzidas muitas
faltas na .
Introdução à Programação: Conceitos e Práticas. Página 94
6.1.5.3. Outras Técnicas
Outras técnicas são empregadas visando elevar ao máximo o desempenho global de
um sistema de processamento. Muito resumidamente, seriam:
i. Processamento Paralelo: Esta técnica visa multiplicar uma parte ou toda uma
permitindo que trechos distintos de um programa ou mesmo distintos
programas possam ser processados simultaneamente. Além de ser uma
característicada , também os Sistemas Operacionais e as Linguagens de
Programação devem ser capazes de explorar este recurso. Processadores
modernos com tecnologia empregam esta técnica.
ii. Processamento Vetorial: A com processamento escalar é capaz de operar
sobre apenas um dado. Por exemplo: a instrução de soma é capaz de somar um
número com outro número produzindo um resultado. Em um processador
vetorial, um único registrador da é capaz de conter múltiplos dados, e as
instruções podem operar com estes múltiplos dados. Por exemplo: em uma
com processamento vetorial é possível trazer quatro inteiros da para a
e acomodá-los em um registrador; em seguida uma instrução de soma poderá
adicionar estes quatro inteiros a outros quatro inteiros produzindo como
resultado quatro inteiros, tudo ao mesmo tempo. As tecnologias
dos processadores incorporam esta característica,
onde uma instrução pode operar múltiplos dados.
Introdução à Programação: Conceitos e Práticas. Página 95
7. Introdução à Programação
Atualmente não restam mais dúvidas sobre o sucesso da tecnologia digital, pois em
curto espaço de tempo alcançou um grau de disseminação que transcende fronteiras,
culturas e classes sociais. Certamente o nível de desenvolvimento tecnológico que
presenciamos hoje representa apenas uma amostra do que há por vir.
Este avanço se deve ao surgimento do computador nos anos , que de forma inegável
marcou o início de uma revolução que se propagaria em toda a sociedade. Porém, a
linguagem de programação poderia ser igualmente qualificada como revolucionária,
pois a sua utilização proporcionou uma incrível flexibilidade aos computadores.
A linguagem 5 teve um papel relevante nesta revolução pelo fato ter sido a
primeira linguagem com grande aceitação, sucesso este em grande parte creditado a
sua maior apoiadora, a Após este sucesso, teve início uma onda de criação de
novas linguagens, cuja amostra pode ser observada na seguinte árvore genealógica.
Figura 39: Genealogia das Linguagens – Base FORTRAN.
5
http://pt.wikipedia.org/wiki/Fortran
FORTRAN (54)
COBOL (59)
1960 ALGOL 60
BASIC (64) PL/I (65)
Simula 67 ALGOL 68
1970
C (72) Pascal (71)
Modula 2 (75) Conc Pascal (75)
CLU (75) Ada (79)
1980
Ada (83) ANSI
C++(85)
Eifell (86)
1990
Java (95) Ada (95)
Introdução à Programação: Conceitos e Práticas. Página 96
Esta árvore destaca claramente a influência da linguagem , gerando uma
grande quantidade de descendentes, o que não se observa em linguagens com
diferentes propostas de estilos de programação, como ilustra a figura abaixo:
Figura 40: Genealogia das Linguagens.
Um dos fatores que foram determinantes no sucesso das linguagens de programação
foi a possibilidade de fazer com que o computador desempenhe diferentes funções,
bastando para isto programar o comportamento desejado. Além disso, a produtividade
proporcionada por uma linguagem de alto nível, como o , estimulou
largamente o emprego de programas para realizar funções das mais variadas.
A expansão e os aprimoramentos incorporados às linguagens fizeram com que a
atividade de programação saísse dos ambientes de pesquisa e começasse a permear
os mais diferentes setores da economia e incorporasse programadores originários de
várias outras profissões.
A seguinte figura ilustra como a produção de programas foi facilitada pelas linguagens
de programação de alto nível.
Figura 41: Níveis de Programação.
LISP (59)
APL (62) SNOBOL (62)
Smalltalk (69)
PROLOG (72)
OPS5 (77)
Perl (87) Tcl/Tk (88)
Introdução à Programação: Conceitos e Práticas. Página 97
Tanto o código quanto o código de máquina da figura são de uma compilação
real de uma expressão simples em linguagem de alto nível, que produziu três linhas em
e dezesseis bytes para o código de máquina.
Assim, com a facilidade proporcionada pelas linguagens de alto nível, as décadas de
e experimentaram uma expansão em larga escala no uso de programas de
computadores, gerando uma nova situação que era a preocupação em como gerenciar
este novo elemento, chamado programa de computador.
A ausência de um processo de engenharia no desenvolvimento de programas e a
ausência de boas práticas de gerenciamento levaram ao surgimento do termo
, inicialmente citado por Edsger Dijkstra em .
Esta constatação despertou para a necessidade de abordar a atividade de
programação como um processo de engenharia, demandando a aplicação de
metodologias, técnicas, ferramentas e padrões durante todo o ciclo de vida do software.
A figura a seguir ilustra um processo de desenvolvimento de software:
Figura 42: Ciclo de Desenvolvimento de Software.
O software passou a ser visto como um produto e rapidamente se converteu em
tecnologia presente nos mais diversificados dispositivos, tais como: aviões, carros,
televisões, telefone, micro-ondas, foguetes, trens, dentre outros.
Introdução à Programação: Conceitos e Práticas. Página 98
8. O Processo de Tradução
A Linguagem de Programação reduziu a lacuna entre o problema e o programa, pois
permitiu que o código ficasse mais próximo da formulação do problema. No caso
particular do a forma de programar se assemelhava a descrição dos
problemas em notação matemática. Isto lhe valeu o acrônimo de .
Porém, os computadores continuavam a compreender apenas a linguagem binária. As
instruções eram entendidas pelas apenas no formato numérico. Logo, era
necessário traduzir o programa expresso na forma textual para um formato adequado
para processamento na . A figura a seguir ilustra os diversos conversores e
montadores necessários para efetuar esta tradução:
Figura 43: Processo de Tradução
Atualmente estão disponíveis avançados ambientes de programação denominados
– que tornam transparente ao programador
as etapas de tradução. O programador interage com o digitando o programa em
uma linguagem de alto nível e aciona o comando ou a tecla de compilação. Assim
obtém o código final, que pode ser executado ou depurado no próprio ambiente do .
O depurador é uma importante ferramenta que permite ao programador executar
pausadamente cada linha de código, permitindo inspecionar a memória em busca de
erros na lógica.
Introdução à Programação: Conceitos e Práticas. Página 99
9. A Linguagem C
A linguagem foi por muito tempo utilizada no ensino de programação. Porém,
as versões iniciais da linguagem não proporcionavam mecanismos que ajudassem de
forma natural na escrita de programas com qualidade de produto. De forma simplificada
um programa em execução na memória pode ser visto em dois blocos, sendo um
composto pelas instruções e outro por uma área de dados para armazenamento e
recuperação de informações. As instruções, por sua vez podem ser de duas naturezas:
e . A de tipo é aquela que concluída a sua execução é dada
a vez para a próxima na memória. A de tipo é aquela que testa uma condição
para decidir qual a próxima instrução a ser executada. Conforme o resultado da
condição testada poderá ser executada a instrução subsequente ou uma instrução em
outro endereço.
Assim, programas nos primórdios do seu uso tinham a seguinte aparência,
onde uma série de instruções de desvio ( ou ) alterava o fluxo de execução
do programa, dificultando o entendimento da lógica:
Figura 44: Estilo de Programação Spaghetti.
Esta aparência rendeu a denominação de estilo de programação em função
da aparência confusa e semelhante a este típico prato italiano. Neste período, fins dos
anos e década de , o que norteava o desenvolvimento de programas era a
necessidadede economizar os recursos computacionais (memória e tempo de ),
pois o custava muito mais do que o tempo dos desenvolvedores. Para tal fim,
os programadores utilizavam técnicas de reaproveitamento de memória e otimização
das instruções que resultava no médio prazo em grande dificuldade de manutenção.
L01 instrução
L02 instrução
L03 jump L06
L04 instrução
L05 jump L16
L06 instrução
L07 jump L10
L08 instrução
L09 jump L04
L10 instrução
L11 jump L15
L12 instrução
L13 jump L02
L14 instrução
L15 jump L01
L16 instrução
L17 jump L10
L18 instrução
Introdução à Programação: Conceitos e Práticas. Página 100
Nas décadas seguintes foram surgindo novos estilos de programação com o objetivo de
melhorar a engenharia dos programas, e consequentemente elevar o grau de qualidade
e confiabilidade destes produtos. Estes estilos visavam a melhor organização das
entidades que compõem um programa, principalmente dados e rotinas, e também
almejavam melhorar a produtividade do programador através do reuso de códigos. O
seguinte quadro ilustra a evolução nos estilos de programação.
Figura 45: Evolução no estilo de programação.
Após o , o esforço seguinte que mereceu destaque na história das linguagens
foi o projeto do 6, que nasceu da intenção de prover uma linguagem que
conduzisse a programas melhores construídos.
O era uma linguagem grande e complexa e dadas as condições da época, não
houveram implementações completas de compiladores. Além disso, neste período o
tinha a como patrocinador, o que dificultava a aceitação e disseminação
de novas linguagens e compiladores. A computação evoluía e alcançava vários
segmentos, além da área científica, como a área comercial, estudos de inteligência
comercial, bem como o desenvolvimento de sistemas operacionais e software básico.
6
http://pt.wikipedia.org/wiki/ALGOL
Introdução à Programação: Conceitos e Práticas. Página 101
No setor específico de sistema operacional ( ) e software básico, havia um nível de
retrabalho considerável sempre que se alterava as características do hardware.
Praticamente toda a programação do , que era escrito em , tinha que ser
refeita para se adequar a um novo processador ou a um novo desenho da arquitetura
do computador.
A fim de buscar uma solução para minimizar o esforço de desenvolvimento de para
novos computadores, a , pertencente a empresa , investiu no projeto de
desenvolvimento do Sistema Operacional , a partir de um chamado .
A equipe de desenvolvimento incluía Ken Thompson, Dennis Ritchie, dentre outros.
Havia a necessidade de dispor de uma linguagem apropriada para desenvolvimento do
, visto que a linguagem mudava conforme a arquitetura do hardware.
Assim, nasceu a Linguagem , após várias tentativas com outras linguagens. Uma
sucessão de adaptações e melhorias levaram a uma versão estável da linguagem. Na
raiz da sua estrutura está a linguagem e a linguagem (Combined
Programming Language).
A linguagem também era complexa, e acabou dando origem a uma versão mais
simples, denominada (Basic ). Novas adaptações geraram a linguagem ,
para se consolidar em uma proposta de linguagem que foi denominada de .
Enfim, havia uma linguagem que tornava o desenvolvimento de menos desgastante.
Bastava ter um núcleo mínimo de para iniciar o computador, um compilador para a
Linguagem , e o esforço restante poderia ser feito na linguagem de alto nível, incluindo
o reuso de softwares já desenvolvidos. Em , o chamado desenvolvido
em por Thomson e Ritchie estava disponível em um computador . O teve
seu nome alterado para por Brian Kernighan.
O sucesso da Linguagem veio com a publicação de um livro clássico em , por
Brian Kernighan e Dennis Ritchie, com o título The C Programming Language. Esta
versão da linguagem ficou conhecida como C Kernighan e Ritchie ou C K&R.
Introdução à Programação: Conceitos e Práticas. Página 102
10. Estrutura de um Programa C
A Figura 33: Programa na Memória RAM: Código + Dados. ilustrou um programa na
memória separado em duas áreas distintas, sendo uma para acomodar a codificação
das instruções e a outra para conter os resultados produzidos por estas instruções.
Esta organização reflete também o título do livro
, de Niklaus Wirth, publicado em , afirmando que um programa é a
combinação de algoritmo e estrutura de dados, ou em termos mais simples:
.
De forma semelhante, um programa tem a seguinte estrutura básica:
#include <stdio.h>
/* área destinada a inclusão de bibliotecas */
/* área destinada a dados e estruturas globais */
/* área destinada a funções */
int main () {
/* área destinada a dados e estruturas locais */
/* área destinada às instruções */
}
Assim, para iniciar a programação em é necessário conhecer tanto os recursos
disponíveis para definição de dados quando os recursos para manipulação dos dados.
Uma das vantagens das linguagens de alto nível em relação às linguagens de baixo
nível foi a possibilidade de trabalhar com nomes em vez de endereços físicos de
memória. Assim, na sequência são apresentadas as regras para a criação de nomes ou
identificadores.
11. Identificadores
O programador terá que se habituar a dar nomes para as entidades de um programa,
tais como constantes, tipos, variáveis, e funções. A regra para nomes ou
identificadores é dada por:
Introdução à Programação: Conceitos e Práticas. Página 103
A notação7 acima indica que um identificador válido inicia com um dos caracteres
listados em seguido por zero ou mais caracteres em A definição
admite letra ou dígito. A linguagem distingue entre maiúsculo e minúsculo,
ou seja: uma entidade que tenha o nome todo em maiúsculo difere da entidade com o
nome todo em minúsculo. Seguem alguns exemplos:
Identificadores válidos e distintos Identificadores não válidos
A
B
Soma
soma
SOMA
SOMA_TOTAL
B20
A1
Qdade
I
_J
K_
SOMA TOTAL
9AUX
SOMA-TOTAL
Dado%
Valor$
#qdade
aluno@nome
12. Variáveis e Tipos de Dados
Associar nomes com endereços de memória facilitou a atividade de programação e deu
origem ao conceito de variável. Assim, variável é uma referência a um endereço de
uma área de memória, através da qual se pode armazenar ou recuperar o seu valor.
A seguinte figura ilustra uma sequência de bytes na memória (endereços de a )
e a associação com os nomes , , e , formando o conceito de variáveis. As células
de memória podem ser trabalhadas através destes nomes.
Nome Célula - RAM Endereço
a: 303
b: 302
c: 301
x: 300
A reserva destas células de memória em se dá pela declaração de variáveis no trecho
destinado aos dados globais ou locais. Além do nome que se deseja associar à área de
memória também é necessário indicar a natureza do dado que se deseja acomodar
7
Notação semelhante à BNF:
O símbolo “::=” significa “é definido como”.
O símbolo “*” significa “repetir 0 ou mais vezes”.
Introdução à Programação: Conceitos e Práticas. Página 104
nesta área, que em termos de linguagem de programação é o . A
declaração de variável é feita da seguinte forma:
Ou
Onde, é uma sequência de nomes de variáveis separados por
vírgula.
Faremos uma analogia da variável com uma cadeira. Observe o seguinde esquema:
Figura 46: Analogia variávelvs cadeira.
A cadeira pode ser referenciada de duas formas, através de um , assentado no
encosto, ou através de um número, assentado sob a base. Este número, sob a base, é
o da variável. No assento propriamente dito temos assentado diretamente na
base um valor , composto por e . É desta forma que o hardware
compreende. Sobre este binário temos uma camada, com o da poltrona, que
orienta como este binário será interpretado por um observador externo. Finalmente,
uma das formas de como este binário poderá ser interpretado é definido como o .
O operador que permite assentar um valor nesta cadeira é a atribuição, detalhada a
seguir.
13. Operador Atribuição
A primeira instrução a ser apresentada e uma das mais utilizadas é a operação de
atribuição, cuja função é armazenar valores na memória. A sintaxe é:
Introdução à Programação: Conceitos e Práticas. Página 105
O símbolo representa o operador atribuição, cujo significado é armazenar na
célula de memória indicada pela variável o valor final da expressão. O nome da
aparece no da operação de atribuição e o aparece no
. A instrução ou linha de código é finalizada com o símbolo (ponto-e-
vírgula). Para fazer uso efetivo da atribuição é preciso aprender a escrever expressões,
que podem ser de natureza numérica/aritmética, lógicas ou binárias.
Observe o exemplo a seguir:
Código Analogia variável vs cadeira
int v;
int main () {
v = +5;
}
Neste exemplo, temos na primeira coluna um pequeno trecho de programa em que
declara uma variável do tipo e associa ao nome . Na sequencia, o programa
principal ( ), que é o ponto de partida para a execução do programa, tem uma única
instrução, cujo efeito é atribuir o valor à variável .
Na segunda coluna temos a associação desta variável com o modelo da cadeira. O
nome identifica textualmente a cadeira. Esta cadeira também pode ser identificada
pelo número , que é o endereço da variável. Neste caso, o endereço é apenas
ilustrativo. O definirá o endereço de memória onde a variável ficará vinculada. O
programa é uma sequência de caracteres que descreve a lógica do programa
em um formato textual.
Assentado diretamente na cadeira na sua camada mais inferior temos o valor formado
por , que neste exemplo é . A camada
logo acima desta camada física indica a forma como a camada binária deve ser
interpretada. O tipo foi utilizado como modelo do assento da cadeira. Isto indica que
um observador externo quando se referir a cadeira poderá interpretar o binário como
um inteiro com sinal. A instrução determinou que fosse atribuído o valor à variável .
Logo, será este valor que será compreendido pelo observador externo.
Introdução à Programação: Conceitos e Práticas. Página 106
A atribuição, por ser um operador da linguagem, gera um resultado, que é o valor
atribuído. Assim, a expressão tem como resultado o valor . Esta abordagem
permite uma expressão com vários operadores de atribuição. Por exemplo:
int main () {
int x, y, z;
x = y = z = 0;
}
A expressão irá fazer com o valor
seja atribuído a variável , em seguida a e a .
É importante observar que a referência a um nome de variável, em uma expressão com
atribuição, possui significados distintos conforme a variável apareça no lado esquerdo
ou no lado direito do operador de atribuição. Na expressão a referência a no
lado direito da atribuição é substituída pelo de . Já a referência a no lado
esquerdo é interpretada como o de . Assim, esta expressão pode ser
descrita como: Armazenar no endereço de memória representado por o valor que
está armazenado no endereço de memória representado por .
14. Expressões Numéricas
As expressões numéricas combinam constantes, variáveis, operadores e funções a fim
de obter um novo valor calculado. A sintaxe da linguagem de programação deve ser
entendida para que uma expressão escrita em notação matemática normal seja
corretamente convertida para a forma computacional. Alguns exemplos de expressões
que serão reescritas em :
√
√
14.1. Tipos Numéricos
A linguagem disponibiliza tipos de dados numéricos que permitem a representação de
inteiros sem sinal, inteiros com sinal e reais, com diversos tamanhos e precisões.
Os tipos básicos permitidos pela linguagem são: , , , , e
. Os tipos e serão detalhados mais adiante.
Introdução à Programação: Conceitos e Práticas. Página 107
A seguinte tabela detalha o significado dos tipos primitivos:
Tipo Faixa #bytes
char Inteiro com sinal ⋯ 1
int Inteiro com sinal ⋯ 4
float real ⋯ , 4
double real ⋯ , 8
O tamanho do tipo é dependente da arquitetura e do compilador. No compilador de
bits o tipo ocupa bytes.
A linguagem C permite adicionar qualificadores de tamanho e/ou de sinal ao tipo base
gerando novos tipos. Os qualificadores de tamanho são e e os de sinal são
e . O qualificador de tamanho não pode ser aplicado ao tipo , cujo
tamanho é definido pela linguagem como sendo . Este qualificador também não
pode ser aplicado ao tipo . O qualificador de sinal não pode ser aplicado aos tipos
e . Desta forma, os tipos disponibilizados pela linguagem são:
Tipo Natureza Faixa #bytes
char Inteiro com sinal ⋯ 1
unsigned char Inteiro sem sinal ⋯ 1
signed char Inteiro com sinal ⋯ 1
int Inteiro com sinal ⋯ 4
short int Inteiro com sinal ⋯ 2
long int Inteiro com sinal ⋯ 4
long long int Inteiro com sinal ⋯ 8
unsigned int Inteiro sem sinal ⋯ 4
unsigned short int Inteiro sem sinal ⋯ 2
unsigned long int Inteiro sem sinal ⋯ 4
unsigned long long int Inteiro sem sinal ⋯ 8
float real ⋯ , 4
double real ⋯ , 8
long double real ⋯ , 10
Quando o qualificador de sinal não for explicitado, é assumido como .
Agora temos mais elementos para compreender o conceito de variável. Assim, ao
declararmos uma variável estamos fornecendo elementos ao compilador sobre a
natureza da célula de memória e orientando sobre as regras permitidas para o
manuseio deste espaço.
Introdução à Programação: Conceitos e Práticas. Página 108
14.2. Constantes
Agora podemos utilizar estas células de memória para armazenar valores ou recuperá-
los conforme a conveniência da lógica do processamento. Para guardar valores, o
nome da variável deve aparecer à esquerda do operador de atribuição, enquanto que
do lado direito a referência a variável é substituída pelo seu valor.
É importante conhecer as formas permitidas para escrever uma constante numérica. O
valor de uma constante inteira pode ser expresso em decimal, octal, hexadecimal ou
caracter ASCII. Além disso, a constante pode incorporar um caracter que define o seu
tamanho. Suponha a seguinte declaração de variável:
A seguinte instrução atribui o valor decimal na variável :
Na representação de cadeira, teríamos a seguinte ilustração, supondo que o endereço
reservado para seja :
A representação binária é resultante da conversão do valor para binário pelo método
de . A largura da cadeira é de bytes, uma vez que o assento é do tipo . Como
a memória é composta por uma sequência de bytes, logo esta cadeira ocupariaos
bytes localizados nos endereços , , e .
Variações desta atribuição poderia lançar mão de outras formas de representar a
quantidade . Por exemplo:
explicitar o sinal:
Quando uma constante iniciar com o dígito ele será entendido
como escrito em octal;
Introdução à Programação: Conceitos e Práticas. Página 109
Uma constante hexadecimal deve iniciar com ou ;
A representação equivale ao valor numérico estabelecido pela tabela
para o referido caractere. Neste caso é o decimal .
Estas cinco formas tem o mesmo efeito sobre a variável .
Como citado anteriormente, uma constante inteiro pode ter um sufixo, caracter
adicionado a direita, para designar como (U) e (L). Este sufixo pode ser
maiúsculo ou minúsculo e pode estar em qualquer ordem. Por exemplo: , e
.
No caso de constantes negativas, basta adicionar o sinal antes do número:
Na representação de cadeira, teríamos a seguinte ilustração:
Observe que o valor binário é formado por uma sequência de bits , como resultante
da codificação do em .
Um valor real pode ser representado de diversas formas. Considere a seguinte
definição de variável:
Algumas das formas de representação de constantes reais ( , ou
) são:
x = 3.1415;
x = -1234.4567;
x = +.4567;
x = +1234.;
x = 1.234e-10;
Introdução à Programação: Conceitos e Práticas. Página 110
x = -1.234E15;
x = 1.234e+5;
x = 10.0F;
x = 10.0L;
x = 100;
A maioria dos formatos coincide com o estilo utilizado na matemática. Destacamos a
notação 1.234e-10 que equivale à . O símbolo '.', quando presente, serve
como separador das partes inteira e fracionária. Não há separador para agrupamento
de milhar.
Introdução à Programação: Conceitos e Práticas. Página 111
14.3. Operadores Matemáticos
Vamos ampliar a capacidade de manipular dados através dos operadores aritméticos,
aplicados a um ou dois operandos para produzir um resultado.
Operador
Binário
Operação Observação
Soma Os operandos podem ser expressões com inteiro ou real. Se
ambos os operandos forem inteiros o resultado será inteiro. Se
um dos operandos for real, então o resultado será real.
Subtração
Multiplicação
Divisão O resultado será um número real se qualquer um dos operandos
for real.
Divisão Inteira Operandos e resultado são inteiros. Retorna o quociente da
divisão.
Resto Operandos e resultado são inteiros. Retorna o resto da divisão.
Os operadores e também podem ser aplicados a apenas um operando (unário),
passando a ter os seguintes significados:
Operador
Unário
Operação Observação
Identidade + Expressão retorna o mesmo valor de Expressão.
Inversão - Expressão retorna o valor de Expressão com sinal trocado.
Quando estiverem presentes em uma expressão vários operadores, a linguagem
executa primeiramente o operador com maior nível de precedência. Os operadores
tem precedência de execução em relação a e .
A seguinte tabela relaciona os operadores, as respectivas precedências e critério de
associatividade:
Introdução à Programação: Conceitos e Práticas. Página 112
Prece-
dência
Categoria Operador Descrição Associa-
tividade
1 Pós-fixo
[ ]
Incremento e decremento Sufixado/pós-fixado
Chamada de Função
Índice de Array
Acesso a membro de uma Struct ou Union
Acesso a membro de uma Struct ou Union com ponteiro
Esq. p/ Dir.
2 Unário
Incremento e decremento prefixado
Mais e menos unários
NOT Lógico e NOT Binário
Type cast – conversão de tipo
Indireção – derefêrencia
Endereço de variável
Tamanho de tipo/valor
Dir. p/ Esq.
3 Multiplicação * / % Multiplicação, divisão e resto Esq. p/ Dir.
4 Adição Soma e subtração Esq. p/ Dir.
5 Deslocamento Deslocamento de bits para esquerda e direita Esq. p/ Dir.
6 Relacional
Operadores relacionais e
Operadores relacionais e
Esq. p/ Dir.
7 Igualdade Operadores relacionais e Esq. p/ Dir.
8 AND Binário AND Binário Esq. p/ Dir.
9 XOR Binário XOR Binário (OR exclusivo) Esq. p/ Dir.
10 OR Binário OR Binário (OR inclusivo) Esq. p/ Dir.
11 AND Lógico AND Lógico Esq. p/ Dir.
12 OR Lógico OR Lógico Esq. p/ Dir.
13 Condicional Condicional ternário Dir. p/ Esq.
14 Atribuição
Atribuição simples
Atribuição acumulando soma e subtração
Atribuição acumulando produto, quociente e resto
Atribuição acumulando deslocamento a esq/dir
Atribuição acumulando AND, XOR e OR binário
Dir. p/ Esq.
15 Vírgula Vírgula Esq. p/ Dir.
Figura 47: Precedência de Operadores
8,9
.
Considere a seguinte expressão:
A operação de multiplicação é executada primeiro e depois a soma. Caso queira efetuar
primeiro a soma então se deve fazer uso de parêntesis, como na expressão:
8
https://en.cppreference.com/w/c/language/operator_precedence, acessado em 05-mar-2019 as 12:03
9
https://www.tutorialspoint.com/cprogramming/c_operators_precedence.htm, acessado em 05-mar-2019 as
12:15
https://en.cppreference.com/w/c/language/operator_precedence
https://www.tutorialspoint.com/cprogramming/c_operators_precedence.htm
Introdução à Programação: Conceitos e Práticas. Página 113
Neste outro exemplo:
quando escrita em tem-se:
Foram utilizados dois níveis de parêntesis a fim de agrupar adequadamente as
operações. Somente parêntesis podem ser usados para agrupar as operações. Os
símbolos que estamos acostumados na Álgebra tradicional [ { } ] não possuem esta
função em .
A associatividade serve para compreender a ordem das operação quando uma
expressão é composta por vários operadores de mesma precedência. Por exemplo:
A tabela de precedência informa que o operador segue a regra de associatividade da
esquerda para a direita. Isto significa que primeiro irá efetuar , para então
computar .
Ao longo deste material serão explanados quase todos os operadores citados na
tabela.
A fim de codificar programas minimamente funcionais, iremos antecipar alguns
conceitos relacionados a string. Uma constante string é uma sequência de caracteres
delimitados por aspas. Cada caractere utiliza byte de memória. Seguem alguns
exemplos:
"Ola mundo" é uma string formada por nove caracteres.
"" string vazio composta por zero caractere.
"texto \"entre\" aspas" a sequência \" equivale ao símbolo ". Neste
exemplo, resulta em texto "entre" aspas.
Alguns caracteres de controle, código entre e , podem ser incluídos na string.
Os efeitos de alguns destes caracteres podem ser observados na janela de execução
do programa. Vejamos alguns destes caracters:
'\n' significa nova linha.
'\0' equivale ao caracter de código zero ( ).
'\t' equivale a tecla .
'\\' equivale ao '\'
Introdução à Programação: Conceitos e Práticas. Página 114
14.4. Funções Matemáticas
A linguagem disponibiliza várias funções aritméticas que podem ser utilizadas na
construção de expressões matemáticas. Para fazer uso da maioria destas funções no
programa é necessário incluir a biblioteca . Os exemplos podem ser copiados e
compilados como atividade prática. A função será detalhada na sequência. No
momento basta saber que esta função é responsável em apresentar informações na
tela do computador. Segue uma descrição resumida das principais funções:
1)
Retorna o ângulo em radianos, cujo cosseno é o valor de . O resultado estará
no intervalo [ ].
Exemplo:
#include <stdio.h>#include <math.h>
int main () {
double ret;
ret = acos(-1);
printf("O valor de PI = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de PI = 3.141593
2)
Retorna o ângulo em radianos, cujo seno é o valor de . O resultado estará no
intervalo [ ].
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
Introdução à Programação: Conceitos e Práticas. Página 115
ret = asin(1.0);
printf("O valor de PI = %lf\n", 2*ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de PI = 3.141593
3)
Retorna o ângulo em radianos, cuja tangente é o valor de . O resultado estará
no intervalo [ ].
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
ret = atan(1.0);
printf("O valor de PI = %lf\n", 4*ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de PI = 3.141593
4)
Retorna o ângulo em radianos, cuja tangente é dada por . O resultado
estará no intervalo [ ].
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
Introdução à Programação: Conceitos e Práticas. Página 116
ret = atan2(0.0, -1.0);
printf("O valor de PI = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de PI = 3.141593
5)
Retorna o cosseno de um ângulo em radianos. O resultado estará no intervalo
[ ].
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret, pi;
pi = acos (-1.0);
ret = cos(pi/3);
printf("O valor de cos(PI/3) = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de cos(PI/3) = 0.500000
6)
Retorna o cosseno hiperbólico de um ângulo em radianos.
.
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
Introdução à Programação: Conceitos e Práticas. Página 117
double ret;
ret = cosh (1.0);
printf("O valor de cosh(1.0) = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de cosh(1.0) = 1.543081
7)
Retorna o seno de um ângulo em radianos. O resultado estará no intervalo
[ ].
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret, pi;
pi = acos (-1.0);
ret = sin (pi/6);
printf("O valor de sin(PI/6) = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de sin(PI/6) = 0.500000
8)
Retorna o seno hiperbólico de um ângulo em radianos.
.
Exemplo:
#include <stdio.h>
#include <math.h>
Introdução à Programação: Conceitos e Práticas. Página 118
int main () {
double ret;
ret = sinh (1.0);
printf("O valor de sinh (1.0) = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de sinh (1.0) = 1.175201
9)
Retorna a tangente hiperbólica de um ângulo em radianos.
.
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
ret = tanh (1.0);
printf("O valor de tanh (1.0) = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de tanh (1.0) = 0.761594
10)
Retorna o valor da constante elevado a potência .
.
Exemplo:
#include <stdio.h>
#include <math.h>
Introdução à Programação: Conceitos e Práticas. Página 119
int main () {
double ret, euler;
euler = exp (1.0);
ret = (exp(2.0) – exp(-2.0))/2 ;
printf("O valor da constante e = %lf\n", euler);
printf("O valor de sinh (2.0) = %lf\n", ret);
printf("O valor de sinh (2.0) = %lf\n", sinh(2.0));
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor da constante e = 2.718282
O valor de sinh (2.0) = 3.626860
O valor de sinh (2.0) = 3.626860
11)
Um número real escrito na forma normalizada na base tem a seguinte forma
geral:
, onde
Dado um valor , o valor da é retornado como resultado da função
, enquanto que o é guardado no endereço indicado em
.
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double mantissa, x = 1.0/16; int expoente;
mantissa = frexp (x, &expoente);
printf("x = %f = %f * 2^%d\n", x, mantissa, expoente);
return 0;
}
Após compilar e processar o resultado apresentado será:
x = 0.062500 = 0.500000 * 2^-3
Introdução à Programação: Conceitos e Práticas. Página 120
12)
Retorna o resultado da seguinte operação.
.
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x = 0.5, ret;
int n = 4;
ret = ldexp(x ,n);
printf("%f * 2^%d = %f\n", x, n, ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
0.500000 * 2^4 = 8.000000
13)
Retorna o logaritmo natural de um número .
.
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
ret = log (exp(3));
printf("O valor de ln (e^3) = %lf\n", ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
Introdução à Programação: Conceitos e Práticas. Página 121
O valor de ln (e^3) = 3.000000
14)
Retorna o logaritmo na base de um número .
.
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret, x = 0.0001;
ret = log10 (x);
printf("O valor de log10 (%g) = %lf\n", x, ret);
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor de log10 (0.0001) = -4.000000
15)
A função separa um número real em suas partes inteira e fracionária.
Dado um valor real , o valor real equivalente a parte fracionária é retornado
como resultado da função , enquanto que o equivalente real da parte
é guardada no endereço indicado em .
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x = 123.456, parte_frac, parte_int;
parte_frac = modf(x ,&parte_int);
printf("%g = %g + %g\n", x, parte_int, parte_frac);
return 0;
}
Introdução à Programação: Conceitos e Práticas. Página 122
Após compilar e processar o resultado apresentado será:
123.456 = 123 + 0.456
16)
A função retorna o valor de elevado a potência .
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("Raiz quadrada de 2 = %lf\n", pow(2, 1.0/2));
printf("Raiz cubica de 8 = %lf\n", pow(8, 1.0/3));
return 0;
}
Após compilar e processar o resultado apresentado será:
Raiz quadrada de 2 = 1.414214
Raiz cubica de 8 = 2.000000
17)
A função retorna a raíz quadrada de um número .
√
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("Raiz quadrada de 2 = %lf\n", sqrt(2));
printf("Raiz quadrada de 64 = %lf\n", sqrt(64));
return 0;
}
Após compilar e processar o resultado apresentado será:
Introdução à Programação: Conceitos e Práticas. Página 123
Raiz quadradade 2 = 1.414214
Raiz quadrada de 64 = 8.000000
18)
A função retorna o valor inteiro imediante superior ou igual a .
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("ceil (-1.4) = %lf\n", ceil (-1.4));
printf("ceil (-1.5) = %lf\n", ceil (-1.5));
printf("ceil (-1.6) = %lf\n", ceil (-1.6));
printf("ceil (1.4) = %lf\n", ceil (1.4));
printf("ceil (1.5) = %lf\n", ceil (1.5));
printf("ceil (1.6) = %lf\n", ceil (1.6));
return 0;
}
Após compilar e processar o resultado apresentado será:
ceil (-1.4) = -1.000000
ceil (-1.5) = -1.000000
ceil (-1.6) = -1.000000
ceil (1.4) = 2.000000
ceil (1.5) = 2.000000
ceil (1.6) = 2.000000
19)
A função retorna o valor absoluto de ( ).
A função retorna o valor absoluto de ( ).
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x = -11.11, y = 22.22;
int z = -123;
Introdução à Programação: Conceitos e Práticas. Página 124
printf("O valor absoluto de %lf\t = %lf\n", x, fabs(x));
printf("O valor absoluto de %lf\t = %lf\n", y, fabs(y));
printf("O valor absoluto de %d\t = %d\n", z, abs (z));
return 0;
}
Após compilar e processar o resultado apresentado será:
O valor absoluto de -11.110000 = 11.110000
O valor absoluto de 22.220000 = 22.220000
O valor absoluto de -123 = 123
20)
A função retorna o valor inteiro imediante inferior ou igual a .
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("floor (-1.4) = %lf\n", floor (-1.4));
printf("floor (-1.5) = %lf\n", floor (-1.5));
printf("floor (-1.6) = %lf\n", floor (-1.6));
printf("floor (1.4) = %lf\n", floor (1.4));
printf("floor (1.5) = %lf\n", floor (1.5));
printf("floor (1.6) = %lf\n", floor (1.6));
return 0;
}
Após compilar e processar o resultado apresentado será:
floor (-1.4) = -2.000000
floor (-1.5) = -2.000000
floor (-1.6) = -2.000000
floor (1.4) = 1.000000
floor (1.5) = 1.000000
floor (1.6) = 1.000000
21)
A função retorna o valor mais próximo de .
Introdução à Programação: Conceitos e Práticas. Página 125
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x;
x = 7.3; printf("round (%lf) = %lf\n", x, round(x));
x = 7.5; printf("round (%lf) = %lf\n", x, round(x));
x = -7.5; printf("round (%lf) = %lf\n", x, round(x));
x = -7.3; printf("round (%lf) = %lf\n", x, round(x));
return 0;
}
Após compilar e processar o resultado apresentado será:
round (7.300000) = 7.000000
round (7.500000) = 8.000000
round (-7.500000) = -8.000000
round (-7.300000) = -7.000000
22)
A função retorna a parte inteira de .
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x;
x = 7.3; printf("trunc (%lf) = %lf\n", x, trunc(x));
x = 7.5; printf("trunc (%lf) = %lf\n", x, trunc(x));
x = -7.5; printf("trunc (%lf) = %lf\n", x, trunc(x));
x = -7.3; printf("trunc (%lf) = %lf\n", x, trunc(x));
return 0;
}
Após compilar e processar o resultado apresentado será:
trunc (7.300000) = 7.000000
trunc (7.500000) = 7.000000
trunc (-7.500000) = -7.000000
Introdução à Programação: Conceitos e Práticas. Página 126
trunc (-7.300000) = -7.000000
23)
A função retorna o resto da divisão de por .
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x, y;
x = 7.5; y = 2.3;
printf("resto de %lf/%lf = %lf\n", x, y, fmod(x, y));
x = acos(-1.0); y = 1.0;
printf("resto de %lf/%lf = %lf\n", x, y, fmod(x, y));
return 0;
}
Após compilar e processar o resultado apresentado será:
resto de 7.500000/2.300000 = 0.600000
resto de 3.141593/1.000000 = 0.141593
Introdução à Programação: Conceitos e Práticas. Página 127
14.5. Exemplos
Os exemplos que seguem apresentam algumas expressões na forma usual e o
respectivo programa escrito em C. Para cada um dos itens é apresentada a saída
esperada para o programa. Todos os programas devem prever os :
Equação Programa
i.
√
#include <stdio.h>
#include <math.h>
int main () {
double x = 3, y = 3, z;
z = sqrt (x*x + y*y*y)/fabs(x+y);
printf("z = %lf\n", z);
return 0;
}
int main () {
double x = 3, y = 3, z;
z = sqrt (pow(x,2) + pow(y,3))/fabs(x+y);
printf("z = %lf\n", z);
return 0;
}
z = 1.000000
ii.
int main () {
double x = M_PI/2, z;
z = (1 + sin(x))/(1 + cos(x));
printf("z = %lf\n", z);
return 0;
}
z = 2.000000
iii.
int main () {
double x = 2, z;
z = 1 + 1/x + 1/(x*x) + 1/(x*x*x) +
1/(x*x*x*x);
printf("z = %lf\n", z);
return 0;
}
Introdução à Programação: Conceitos e Práticas. Página 128
Que pode ser reescrita como:
(
(
(
)))
int main () {
double x = 2, y, z;
y = 1/x;
z = 1 + y*(1 + y*(1 + y*(1 + y)));
printf("z = %lf\n", z);
return 0;
}
z = 1.937500
iv.
(
)
(
)
int main () {
double x = 2, y = 1, z, w;
w = x/y;
z = w - (x + pow(w,2))/(y - pow(w,2));
printf("z = %lf\n", z);
return 0;
}
z = 4.000000
v.
√ √ √ √
int main () {
double x = 25, z;
z = sqrt(M_PI +
sqrt(exp(3) +
sqrt(4 +
sqrt(x)
)));
printf("z = %lf\n", z);
return 0;
}
z = 2.818924
Observe na expressão do item uma codificação alternativa a partir da reescrita da
expressão original. Além disso, uma variável auxiliar ( ) foi utilizada, o que evitou a
repetição de uma mesma operação ao longo da expressão.
Algumas variáveis foram inicializadas, a fim de permitir ter alguma informação previsível
apresentada na tela. Na sequência serão apresentadas os funções que permitem incluir
recursos de entrada e saída.
Introdução à Programação: Conceitos e Práticas. Página 129
15. Comandos de Entrada e Saída
A seguinte figura ilustra um programa em execução interagindo com o usuário através
dos dispositivos básicos de Entrada e Saída, que são o teclado e o monitor de vídeo.
15.1. Entrada de Dados
A função permite que o programa obtenha dados digitados pelo usuário. Ao
processar o programa o sistema operacional abre uma janela denominada de ambiente
de execução. Quando o fluxo de execução do programa alcançar a linha de código com
a chamada à função o programa fica aguardando que o usuário digite uma linha
com os dados, conforme especificado, e pressione a tecla . Em seguida, esta
linha é interpretada e os dados são armazenados nas variáveis (endereços de
memória) indicadas.
A sintaxe do para leitura do dispositivo padrão de entrada de dados (teclado) é:
O primeiro argumento é uma string contendo a especificação do formato dos dados a
serem lidos. Na sequência temos uma lista de argumentos com os endereços de
memória onde a função deverá armazenar os dados contidos na string digitada.
Vamos adiantar alguns conceitos sobre string em a fim de fazer uso das funções de
entrada e saída. É necessário no momento compreender o uso de uma string literal ou
constante string. É bastante simples: é uma sequência de caracteres delimitados
a esquerda e a direita pelo símbolo aspas. É um texto entre aspas. Por exemplo:
Introdução à Programação: Conceitos e Práticas. Página 130
. Não confundir como símbolo apóstrofe usado para definir uma
constante do tipo ou , como em .
A string de formato conterá uma orientação de como extrair e converter as informações
da linha de texto digitada pelo usuário do programa. Por exemplo, se o programador
desejar que o usuário digite dois números inteiros decimal com possibilidade de sinal,
então ele deve indicar desta forma: O símbolo seguido de um ou mais
caracteres indica o que é esperado para aquela posição da string de entrada. A
indicação orienta o a extrair do texto digitado um número inteiro em decimal
para aquela posição relativa.
Logo após a string de formato vem uma lista com tantos endereços de memória
quantos forem os dados indicados na string de formato. Neste exemplo, usamos uma
string de formato indicando que serão fornecidos dois números inteiros em decimal.
Logo, deverão ser fornecidos dois endereços de memória separados por vírgula.
O endereço de uma variável é aquele número que indicamos sob o assento da cadeira,
no modelo de analogia variável vs cadeira. Este endereço pode ser obtido por meio de
uma expressão usando o operador seguido do nome da variável.
Considere as seguintes variáveis e a associação com as cadeiras:
Assim, se desejarmos que o usuário forneça dois números inteiros, a fim de armazená-
los nas variáveis e , teríamos que escrever a seguinte linha de cógigo:
Ao executar esta linha de código, o cursor ficará posicionado na janela de execução
aguardando que o usuário digite um texto com o formato indicado: dois números
inteiros em decimal e separados por um ou mais espaços em branco. Vamos assumir
que no momento que foi executado, o usuário digitou o seguinte texto, seguido da tecla
:
Introdução à Programação: Conceitos e Práticas. Página 131
35 56
A função efetuará a seguinte lógica: comparará o texto digitado com a string de
formato indicada no primeiro argumento e montará os números, caractere a caractere,
até encontrar um caractere separador. Neste caso, o espaço em branco é o separador
dos números.
Há uma extensa possibilidade de formatos, sendo as mais comuns estas que seguem:
Formato Descrição
Inteiro decimal com sinal
Inteiro decimal sem sinal
Inteiro – decimal, octal ou hexa
Inteiro octal
ou Inteiro hexa
Float
Double
Float com notação de engenharia. Ex. ou
Um caractere
Uma string, sequência de até encontrar um separador
A função também retorna a quantidade de dados lidos. Considere o seguinte
código:
Introdução à Programação: Conceitos e Práticas. Página 132
#include <stdio.h>
#include <math.h>
int main () {
int x, y, z, n;
n = scanf ("%d %d",&x,&y);
z = x + y;
printf ("foram fornecidos %d dados\n", n);
printf ("O valor de %d + %d = %d\n", x, y, z);
return 0;
}
A expressão utiliza o retorno da função e
armazena na variável . Na sequência o valor de é apresentado na tela com o .
Após compilar e processar o resultado apresentado será:
35 56
foram fornecidos 2 dados
O valor de 35 + 56 = 91
A primeira linha corresponde ao texto digitado pelo usuário e as duas seguintes foram
produzidas pelos dois . Vamos analisar um segundo caso, onde não tenha sido
digitado nenhum texto e a entrada de dados tenha sido interrompida pelo usuário
através da tecla – , seguido da tecla . Nesta situação teremos a
seguinte saída:
^Z
foram fornecidos -1 dados
O valor de 0 + 4202064 = 4202064
A primeira linha corresponde ao texto digitado pelo usuário, que neste caso não
forneceu nenhum número e teclou ( seguido de ). Neste caso, o
retorna , indicando que a digitação foi encerrada pelo usuário. Observe que as
variáveis e ficaram com resquícios da memória, comumente chamado e .
Vamos explorar mais um caso, onde o usuário digita um número e interrompe a entrada
teclando ( seguido de ). Neste caso teremos a seguinte saída:
35^Z
foram fornecidos 1 dados
O valor de 35 + 4202064 = 4202099
Fica a cargo do leitor interpretar a saída apresentada.
Introdução à Programação: Conceitos e Práticas. Página 133
15.2. Saída de Dados
A função permite que o programa escreva um ou mais valores na tela do
computador. Ao processar o programa o sistema operacional abre um ambiente de
execução com sua respectiva janela. Ao executar a função o programa escreve
a partir da posição atual do cursor os valores indicados nos argumentos..
A sintaxe do para escrita na tela é:
O primeiro argumento é uma string contendo a especificação de como deve ser
composto a string que será impressa na tela. Na sequência temos uma lista de valores
que combinados com a string de formato resulta no texto a ser impresso.
Complementando o exemplo anterior, temos o seguinte código:
#include <stdio.h>
#include <math.h>
int main () {
int x, y, z;
scanf ("%d %d",&x,&y);
z = x + y;
printf ("O valor de %d + %d = %d\n",x, y, z);
return 0;
}
Após compilar e processar o resultado apresentado será:
35 56
O valor de 35 + 56 = 91
O diagrama que segue apresenta os efeitos de cada uma das três linhas de códigos.
Na primeira linha, a função aguarda a digitação de um texto composto por dois
números inteiros em decimal. O texto fornecido é interpretado pela função , de
acordo com a orientação dada pela string de formato, e dois números inteiros são
extraídos, sendo que o primeiro é armazenado no endereço associado a variável e o
segundo no endereço associado a variável .
Introdução à Programação: Conceitos e Práticas. Página 134
A segunda linha computa a expressão , levando em conta os valores
contidos nestas variáveis, e armazena o resultado em .
Finalmente, a terceira linha com a função é responsável por apresentar um texto
na tela. Este texto terá a estrutura da string de formato, onde haverá a substituição dos
três pelos valores das três expressões indicadas nos argumentos seguintes: , e
. Uma vez feitas as substituições, obtém-se o texto final ,
que então é escrito na janela de execução a partir do ponto em que o cursor está
posicionado.
A lista de possibilidades de fomatos é bastante extensa. Segue uma relação das
principais.
Formato Descrição/Exemplo
Inteiro decimal com sinal
Inteiro decimal sem sinal
Inteiro – decimal, octal ou hexa
Inteiro octal
ou Inteiro hexa
Float
Double
Float com notação de engenharia. Ex. ou
Um caractere
Uma string, sequencia de até encontrar um separador
Vamos utilizar o para explorar algumas caracteríscas e limites da linguagem, por
exemplo a quantidade de bytes necessária para o tipo , o maior valor possível para
Introdução à Programação: Conceitos e Práticas. Página 135
um , e assim por diante. Seguem alguns programas que apresentam estes
valores:
O programa que segue explora os tamanhos dos tipos básicos da linguagem para o
compilado em uso. Os valores podem alterar conforme a plataforma utilizada.
#include <stdio.h>
#include <math.h>
int main () {
printf ("sizeof (char) = %d\n", sizeof (char));
printf ("sizeof (int) = %d\n", sizeof (int));
printf ("sizeof (float) = %d\n", sizeof (float));
printf ("sizeof (double) = %d\n", sizeof (double));
printf ("sizeof (void) = %d\n", sizeof (void));
printf ("sizeof (char *) = %d\n", sizeof (char *));
printf ("sizeof (long int) = %d\n", sizeof (long int));
printf ("sizeof (long long int) = %d\n",sizeof (long long int));
printf ("sizeof (long double) = %d\n", sizeof (long double));
return 0;
}
Após compilar e processar o resultado apresentado será:
sizeof (char) = 1
sizeof (int) = 4
sizeof (float) = 4
sizeof (double) = 8
sizeof (void) = 1
sizeof (char *) = 4
sizeof (long int) = 4
sizeof (long long int) = 8
sizeof (long double) = 12
O programa que segue explora os maiores valores positivo e negativo para , maior
para o , bem como os máximos positivo e negativo, e o menor valor para
e . Estas constantes estão definidas em e . Os
valores podem alterar conforme a plataforma utilizada.
#include <stdio.h>
#include <float.h>
#include <math.h>
#include <limits.h>
int main(void)
{
printf("INT_MIN = %+d\n", INT_MIN);
printf("INT_MAX = %+d\n", INT_MAX);
Introdução à Programação: Conceitos e Práticas. Página 136
printf("UINT_MAX = %u\n", UINT_MAX);
printf("\n");
printf("LONG_MIN = %+ld\n", LONG_MIN);
printf("LONG_MAX = %+ld\n", LONG_MAX);
printf("ULONG_MAX = %lu\n", ULONG_MAX);
printf("\n");
printf("FLT_MIN = %e\n", FLT_MIN);
printf("FLT_MAX = %e\n", FLT_MAX);
printf("FLT_EPSILON = %e\n", FLT_EPSILON);
printf("\n");
printf("DBL_MIN = %e\n", DBL_MIN);
printf("DBL_MAX = %e\n", DBL_MAX);
printf("DBL_EPSILON = %e\n", DBL_EPSILON);
return 0;
}
Após compilar e processar o resultado apresentado será:
INT_MIN = -2147483648
INT_MAX = +2147483647
UINT_MAX = 4294967295
LONG_MIN = -2147483648
LONG_MAX = +2147483647
ULONG_MAX = 4294967295
FLT_MIN = 1.175494e-038
FLT_MAX = 3.402823e+038
FLT_EPSILON = 1.192093e-007
DBL_MIN = 2.225074e-308
DBL_MAX = 1.797693e+308
DBL_EPSILON = 2.220446e-016
Observe que o formato %+ld expressa um em decimal.
No compilador utilizado para este programa, o tipo long double não correspondeu a
expectativa, que era apresentar os limites conforme tabela de tipo apresentada
anteriormente. Assim, foi testada uma versão semelhante, porém em C++, para o
mesmo compilador, com as devidas adaptações dos nomes das constantes. Segue o
código fonte alterado:
#include <iostream>
#include <limits>
int main(void)
Introdução à Programação: Conceitos e Práticas. Página 137
{
printf("INT_MIN = %+d\n", INT_MIN);
printf("INT_MAX = %+d\n", INT_MAX);
printf("UINT_MAX = %u\n", UINT_MAX);
printf("\n");
printf("LONG_MIN = %+ld\n", LONG_MIN);
printf("LONG_MAX = %+ld\n", LONG_MAX);
printf("ULONG_MAX = %lu\n", ULONG_MAX);
printf("\n");
printf("FLT_MIN = %e\n", __FLT_MIN__);
printf("FLT_MAX = %e\n", __FLT_MAX__);
printf("FLT_EPSILON = %e\n", __FLT_EPSILON__);
printf("\n");
printf("DBL_MIN = %e\n", __DBL_MIN__);
printf("DBL_MAX = %e\n", __DBL_MAX__);
printf("DBL_EPSILON = %e\n", __DBL_EPSILON__);
printf("\n");
printf("LDBL_MIN = %Le\n", __LDBL_MIN__);
printf("LDBL_MAX = %Le\n", __LDBL_MAX__);
printf("LDBL_EPSILON = %Le\n", __LDBL_EPSILON__);
return 0;
}
Após compilar e processar o resultado apresentado será:
INT_MIN = -2147483648
INT_MAX = +2147483647
UINT_MAX = 4294967295
LONG_MIN = -2147483648
LONG_MAX = +2147483647
ULONG_MAX = 4294967295
FLT_MIN = 1.175494e-038
FLT_MAX = 3.402823e+038
FLT_EPSILON = 1.192093e-007
DBL_MIN = 2.225074e-308
DBL_MAX = 1.797693e+308
DBL_EPSILON = 2.220446e-016
LDBL_MIN = 3.362103e-4932
LDBL_MAX = 1.189731e+4932
LDBL_EPSILON = 1.084202e-019
Introdução à Programação: Conceitos e Práticas. Página 138
15.3. Exemplos
i. Elaborar um programa que leia dados como: Peso, Altura e Sexo; e calcule o Índice de
Massa Corpórea.
#include <stdio.h>
#include <math.h>
int main () {
double peso, altura, imc;
char sexo;
printf ("Entre com o sexo (M ou F): ");
scanf ("%c", &sexo);
printf ("Entre com o peso: ");
scanf ("%lf", &peso);
printf ("Entre com a altura: ");
scanf ("%lf", &altura);
imc = peso / (altura * altura);
printf ("O seu Indice de Massa Corporea = %6.2lf\n", imc);
return 0;
}
Após compilar e processar o resultado apresentado será:
Entre com o sexo (M ou F): M
Entre com o peso: 80
Entre com a altura: 1.76
O seu Indice de Massa Corporea = 25.83
O programa inicia com a declaração das variáveis. São utilizadas três variáveis do tipo
para representar as grandezas peso, altura e o imc. Uma variável do tipo é
reservada para representar o sexo.
A linha de código apresenta a mensagem na
tela, mantendo o cursor ao final do texto.
Na sequência temos a linha de código que aguarda o usuário
digitar um texto contendo um caractere, seguido de . Este texto é processado
pelo , que irá extrair o caractere ( ) e armazená-lo no endereço de memória
indicado ( ).
Linhas de código semelhantes são feitas para o e . Com as variáveis
inicializadas com os dados fornecidos pelo usuário, o programa passa então para o
cálculo do , que é feito aplicando a expressão , que na sintaxe
Introdução à Programação: Conceitos e Práticas. Página 139
é . O quadrado da altura foi obtido com o operador
multiplicação, em vez de usar .
Por fim, o valor do é apresentado na tela através da linha de código
. O valor da variável
é escrito com o formato indicado que significa formato com seis
caractere, sendo dois deles para as casas decimais, o que resulta em:
0 2 5 . 8 3
Introdução à Programação: Conceitos e Práticas. Página 140
16. Implementando Funções
Com os recursos já apresentados é possível construirmos nossas próprias funções em
complemento às disponibilizadas com a linguagem. O uso de funções é fundamental
tanto para a modularização dos programas quanto para facilitar a reutilização de
códigos já escritos. Assim, ao depararmos com um problema devemos pensar na forma
de decompô-lo em unidades funcionais simples, de modo que o programa final trata
apenas de integrá-los adequadamente.
No exemplo anterior, onde é calculado o – a partir do
peso e altura de uma pessoa, o conceito de função permite que escondamos os
cálculos atrás de um nome, complementado com os argumentos necessários à
realização das contas. Deste modo, sempre que for preciso calcular este , basta
referenciar este nome com seus respectivos argumentos.
A forma geral de uma função é:
{
}
A seguinte figura ilustra os principais elementos e conceitos relacionados com funções:
Figura 48: Estrutura de uma função.
Introdução à Programação: Conceitos e Práticas. Página 141
No exemplo que segue serão feitas associações entre o código em e os conceitos
apresentados nesta figura.
16.1. Exemplos
O programa e a função para cálculo do poderiam adquirir a seguinte estrutura:
#include <stdio.h>
#include <math.h>
double getimc (double Peso, double Altura) {
double IMC;
IMC = Peso / (Altura * Altura);
return IMC;
};
int main () {
double peso, altura, imc;
printf ("Entre com o peso: "); scanf ("%lf", &peso);
printf ("Entre com a altura: "); scanf ("%lf", &altura);
imc = getimc (peso, altura);
printf ("O seu Indice de Massa Corporea = %6.2lf\n", imc);return 0;
}
Por conveniência, retiramos a entrada do dados sobre o sexo da pessoa.
Adicionalmente, o layout do código foi alterado, escrevendo dois comandos em
sequencia na mesma linha de código. O passo a passo deste programa, iniciando pelo
programa principal, é dado por:
1)
Escrever na tela o texto .
2)
Aguardar a digitação de uma linha até que seja teclado , para em seguida
extraria a informação desejada e armazenar na memória. É esperado que o texto
digitado contenha um número real compatível com o tipo . O valor extraído
do texto de entrada será armazenado no endereço de memória correspondente a
variável .
3)
Introdução à Programação: Conceitos e Práticas. Página 142
Estas duas linhas seguem a lógica já comentada, porém aplicável ao dado de
altura.
4)
É feita a para executar os cálculos com os
e . Estes são copiados para os
definidos no . Neste ponto, a execução do
programa passa o controle para a função , que ao terminar,
para o , que copia o da
para a variável .
5)
Escrever uma linha na tela, composta pelo texto
, seguido pelo valor de formatado com
seis posições sendo duas casas decimais.
Após compilar e processar o resultado apresentado será:
Entre com o peso: 80
Entre com a altura: 1.76
O seu Indice de Massa Corporea = 25.83
A estrutura da função ficou:
1)
É o onde são definidos: o
e os . Neste caso, temos:
a) Tipo do Resultado: Define que o será um .
b) Nome:
c) Parâmetros: são dois do tipo , onde:
primeiro parâmetro chama-se e serve para armazenar o do
primeiro argumento;
segundo parâmetro chama-se e e serve para armazenar o
do segundo argumento;
2) {
Início do .
3)
Introdução à Programação: Conceitos e Práticas. Página 143
Cria uma para uso exclusivo no corpo da função, sendo do tipo
e nome .
4)
É uma lógica simples onde o cálculo é dado por . O
resultado é provisoriamente armazenado na variável local .
5)
Antes de encerrar a lógica da função , o resultado, que está armazenado
provisoriamente na variável local , é copiado para um local apropriado, onde
a função chamadora possa acessar. Isto é feito com a instrução seguida
de uma expressão cujo valor corresponde ao trabalho esperado para a função.
6) }
Por fim vem o símbolo que indica o fim do bloco de código que corresponde ao
.
O seguinte diagrama ilustra os elementos que formam a função , bem como o
uso desta função a partir da função .
Figura 49: Estrutura da função getimc.
É importante constatar que os parâmetros ( e ) e a variável local ( ) são
visíveis apenas no corpo da função . A função é organiza a ordem com
Introdução à Programação: Conceitos e Práticas. Página 144
que a lógica principal será executada. É a partir da função que inicia o
processamento. Neste caso, a função possui dentro de seu escopo três variáveis
locais, que são elas: , e . Assim, caso o programador queira atribuir um
nome para uma variável local ou parâmetro que tenha o mesmo o nome de outra
variável local definida em uma segunda função, não haverá conflito algum. Isto se deve
ao fato de estarem em diferentes contextos.
Poderíamos dizer que o nome completo de uma variável ou parâmetro seria composto
de nome e sobrenome, onde o nome é o que está registrado na declaração e o
sobrenome é a identidade da função. Assim, teríamos a variável e o
parâmetro .
A opção foi dar nomes diferentes usando uma característica da linguagem que é fazer
distinção entre maiúsculo e minúsculo. Mas não teria problema algum se tivéssemos
optados em dar o mesmo nome, pois se trata de contextos distintos.
16.2. Exercícios
i. Elaborar um programa que calcule a distância entre dois pontos dados pelas suas
coordenadas
#include <stdio.h>
#include <math.h>
double getDIST (double x1, double y1, double x2, double y2) {
return sqrt(pow(x2-x1, 2) + pow(y2-y1, 2));
};
int main () {
double D, Ax, Ay, Bx, By;
printf ("Entre com as coordenadas dos pontos:\n");
scanf ("%lf %lf %lf %lf", &Ax, &Ay, &Bx, &By);
D = getDIST(Ax, Ay, Bx, By);
printf ("Distancia = %.4lf\n", D);
return 0;
}
O programa principal implementa uma interface simples, onde é solicitado ao usuário
informar as coordenadas de dois pontos e . As coordenadas informadas são
armazenadas nas variáveis . Em seguida é atribuído à variável o
resultado de uma expressão que contém apenas a chamada a função com os
copiando os argumentos para os parâmetros. Na execução da função
os argumentos já estão copiados para os parâmetros . A única
Introdução à Programação: Conceitos e Práticas. Página 145
linha de código faz efetivamente os cálculos ( √
) para
obtenção da distância.
Após compilar e processar o resultado apresentado será:
Entre com as coordenadas dos pontos:
3.5 4.5 6.5 8.5
Distancia = 5.0000
ii. Escrever uma function que receba dois números ( e ) e calcule o valor de .
Seria o equivalente ao papel da função .
#include <stdio.h>
#include <math.h>
double power (double X, double Y) {
return exp (Y*log(X));
};
int main () {
double x = 2, y = 0.5;
printf ("pow (%lf, %lf) = %.15lf\n", x, y, pow (x, y));
printf ("power (%lf, %lf) = %.15lf", x, y, power (x, y));
return 0;
}
Vamos implementar uma forma de calcular e comparar com o resultado com o
valor fornecido pela função da biblioteca . Para isto, faremos uma
transformação da expressão para fazer uso das funções que
corresponde a , e que corresponde ao logaritmo neperiano ou natural.
Assim, lançaremos mão das seguintes transformações até que se chegue a uma
combinação destas funções.
( )
A expressão final utiliza somente as funções e disponíveis no e a
função codifica esta lógica.
Introdução à Programação: Conceitos e Práticas. Página 146
Após compilar e processar o resultado apresentado será:
pow (2.000000, 0.500000) = 1.414213562373095
power (2.000000, 0.500000) = 1.414213562373095
Observe que o formato usado para apresentar o resultado é ,
produzindo uma saída com quinze casas decimais. A precisão alcançada pela
função , neste exemplo, equivale a da função .
iii. Escrever uma function que calcule a média aritmética entre dois números:
#include <stdio.h>
#include <math.h>
double media (double a, double b) {
return (a+b)/2;
};
int main () {
double x, y, z;
printf ("entre com dois numeros\n");
scanf ("%lf %lf", &x, &y);
z = media (x, y);
printf ("media (%lf, %lf) = %lf", x, y, z);
return 0;
}
Especificamente para a linha os e são
copiados para os e ; a função é executada e o resultado
final é copiado para a variável .
Após compilar e processar o resultado apresentado será:
entre com dois numeros
2.46 6.54media (2.460000, 6.540000) = 4.500000
iv. Escrever uma function que calcule a média aritmética entre quatro números:
Introdução à Programação: Conceitos e Práticas. Página 147
#include <stdio.h>
#include <math.h>
double media (double a, double b) {
return (a+b)/2;
};
double media4 (double a, double b, double c, double d) {
return media (media(a, b), media (c, d));
};
int main () {
double x, y, u, v, z;
printf ("entre com quatro numeros\n");
scanf ("%lf %lf %lf %lf", &x, &y, &u, &v);
z = media4 (x, y, u, v);
printf ("media (%lf, %lf, %lf, %lf) = %lf", x, y, u, v, z);
return 0;
}
A solução adotada para a média aritmética de quatro números foi o de aproveitar
a função já pronta que calcula a média de dois números, ou seja:
( )
Após compilar e processar o resultado apresentado será:
entre com quatro numeros
2.4 3.6 4.8 3.2
media (2.400000, 3.600000, 4.800000, 3.200000) = 3.500000
v. Escrever uma function que calcule o seno de um ângulo utilizando a seguinte série:
⋯
Introdução à Programação: Conceitos e Práticas. Página 148
Vamos limitar a série aos cinco primeiros termos, pois não temos recursos
adequados para ir muito além de forma estruturada.
#include <stdio.h>
#include <math.h>
double torad (double graus) {
return graus*M_PI/180;
};
double seno (double rad) {
return rad
- pow(rad, 3)/(3*2)
+ pow(rad, 5)/(5*4*3*2)
- pow(rad, 7)/(7*6*5*4*3*2)
+ pow(rad, 9)/(9*8*7*6*5*4*3*2);
};
int main () {
double ang_g, ang_r, y1, y2;
printf ("Entre com um angulo em graus\n");
scanf ("%lf", &ang_g);
ang_r = torad (ang_g);
y1 = sin (ang_r);
y2 = seno (ang_r);
printf ("sin (%lf) = %.14lf\n", ang_g, y1);
printf ("seno (%lf) = %.14lf\n", ang_g, y2);
return 0;
}
Para calcular o pela série dada, o ângulo deve estar em radianos. O
programa principal, que testará a lógica, solicitará ao usuário que forneça um
ângulo para o cálculo do . Não é razoável que o programa requeira a
entrada em radianos, uma vez que não é a forma mais direta de compreensão.
Assim, o programa solicita a entrada de um número que representa o ângulo em
graus e armazena na variável . Em seguida, uma expressão invocando a
função converte o ângulo para radianos e armazena o resultado na
variável . As duas linhas que seguem calculam o pela função da
biblioteca ( ) e pela função implementada ( ). Na sequência os resultados
são apresentados na tela. Observe a interface abaixo com três execuções
seguidas, para os ângulos e graus.
Introdução à Programação: Conceitos e Práticas. Página 149
Entre com um angulo em graus
30
sin (30.000000) = 0.50000000000000
seno (30.000000) = 0.50000000002028
Entre com um angulo em graus
60
sin (60.000000) = 0.86602540378444
seno (60.000000) = 0.86602544509978
Entre com um angulo em graus
1110
sin (1110.000000) = 0.50000000000000
seno (1110.000000) = 877678.04339667060000
Podemos observar que os resultados para e graus apresentam uma boa
precisão, mesmo com a grande simplificação feita nos cálculos. Porém, para o
ângulo de graus, cujo seno deveria ser o mesmo que o seno de graus
tem-se uma discrepância alarmante. Isto se deve ao fato de que a série possui
termos do tipo . No caso de temos uma convergência rápida da
série. Quando o valor de for muito grande os cálculos divergem e levam a
valores inconsistentes como o visto acima. A mistura de operações com números
muito grandes/pequenos em uma mesma expressão pode levar a resultados
inconsistentes. Neste caso, iremos contornar parte do problema, reduzindo o
ângulo para os quadrantes iniciais através da seguinte operação com o ângulo
em radianos:
(
)
A parte fracionária do número real pode ser obtida aplicando a função que
retorna o resto da divisão entre dois números reais. Assim, dado um número real
, podemos obter a parte fracionária com a expressão . Neste caso,
fazemos ser a divisão do ângulo por , e ficamos apenas com a parte
fracionária, descartando a parte inteira, que representa o número de voltas no
círculo trigonométrico. A parte fracionária multiplicada novamente por resulta
no ângulo reduzido à primeira volta.
A seguinte solução acrescenta a função que faz a redução do ângulo ( ) e
inclui este cálculo ( ) na função :
#include <stdio.h>
#include <math.h>
double reduzir (double rad) {
return 2 * M_PI * fmod (rad/(2 * M_PI), 1);
}
double torad (double graus) {
Introdução à Programação: Conceitos e Práticas. Página 150
return graus*M_PI/180;
};
double seno (double rad) {
rad = reduzir (rad);
return rad
- pow(rad, 3)/(3*2)
+ pow(rad, 5)/(5*4*3*2)
- pow(rad, 7)/(7*6*5*4*3*2)
+ pow(rad, 9)/(9*8*7*6*5*4*3*2);
};
int main () {
double ang_g, ang_r, y1, y2;
printf ("Entre com um angulo em graus\n");
scanf ("%lf", &ang_g);
ang_r = torad (ang_g);
y1 = sin (ang_r);
y2 = seno (ang_r);
printf ("sin (%lf) = %.14lf\n", ang_g, y1);
printf ("seno (%lf) = %.14lf\n", ang_g, y2);
return 0;
}
Processando novamente este programa para as entradas de dados para os
ângulos e graus tem-se:
Entre com um angulo em graus
30
sin (30.000000) = 0.50000000000000
seno (30.000000) = 0.50000000002028
Entre com um angulo em graus
60
sin (60.000000) = 0.86602540378444
seno (60.000000) = 0.86602544509978
Entre com um angulo em graus
1110
sin (1110.000000) = 0.50000000000000
seno (1110.000000) = 0.50000000002028
O seno de graus pela função ficou mais adequado e igual ao de
graus.
Mesmo com todos estes ajustes, esta forma de implementação da lógica do
ainda é muito primitiva e será consideravelmente melhorada na medida em que
forem conhecidos outros recursos da linguagem.
Introdução à Programação: Conceitos e Práticas. Página 151
17. O Tipo Ponteiro
O tipo ponteiro faz parte do conjunto de tipos básicos da linguagem . Este tipo é
destinado a criação de variáveis com capacidade de armazenar um endereço de
memória. O conceito é bastante simples e ao mesmo tempo com grande poder de
construção de estruturas complexas.
Vamos retornar ao nosso modelo que associa uma variável a uma cadeira.
Recordemos que a cadeira possui um nome que provê acesso direto ao valor
assentado sobre ela. Sob o assento tem uma outra identificação da cadeira que é o
número que representa o endereço de memória. Considerando as variáveis:
teríamos a seguinte representação visual:
Uma variável do tipo ponteiro é declarada da seguinte forma:
A natureza da variável é , que a designa como projetada para receber o endereço
de uma cadeira do modelo dado por . Por exemplo, observe a seguinte declaração:
Neste caso é declarada uma variável cujo nome é e cujo tipo é . Inicialmente o
seu conteúdo é desconhecido. Vamos representar no seguinte modelo:
Vamos atribuir um valor à esta variável. Pela declaração ela pode receber o endereço
de qualquer cadeira do tipo . O operador já foi apresentado e bastante utilizado
Introdução à Programação: Conceitos e Práticas. Página 152
junto com função , para infomar os endereços de memória onde os valores
correspondentes ao texto digitado pelo usuário deveriam ser guardados.
Agora vamos lançar mão deste operador para obter o endereço da variável e
armazenar este valor na variável :Ficaríamos com a seguinte configuração de memória, considerando o contexto com as
variáveis , e .
Apresentamos agora uma segunda maneira de acessar o conteúdo da cadeira . A
primeira forma é através do seu nome , o que chamamos de acesso direto. A segunda
forma é através de , também denominado acesso indireto. Desenhando apenas as
duas cadeiras, temos:
Assim, a expressão fornece acesso direto ao valor , que neste exemplo é o
endereço da cadeira . Porém, a expressão fornece acesso indireto ao conteúdo
da cadeira . Observe a seguinte ilustração:
Introdução à Programação: Conceitos e Práticas. Página 153
Vamos implementar este cenário e observar os valores na plataforma utilizada para a
compilação e execução:
#include <stdio.h>
#include <math.h>
int main () {
int x = 35, y = 56, z = 91;
int *ptr;
ptr = &x;
printf ("&x = %d\n", &x);
printf ("&x = %p\n", &x);
printf ("&y = %p\n", &y);
printf ("&z = %p\n", &z);
printf ("&ptr = %p\n", &ptr);
printf ("x = %d\n", x);
printf ("y = %d\n", y);
printf ("z = %d\n", z);
printf ("ptr = %p\n", ptr);
printf ("*ptr = %d\n", *ptr);
ptr = NULL;
printf ("ptr = %p\n", ptr);
return 0;
}
Os destinados para apresentar o valor de um endereço de memória utiliza o
formato (ponteiro) que imprime o endereço no formato acrescentando zeros
a esquerda para completar o tamanho ocupado pelo tipo ponteiro.
&x = 6422188
&x = 0061FEAC
&y = 0061FEA8
&z = 0061FEA4
&ptr = 0061FEA0
x = 35
y = 56
z = 91
ptr = 0061FEAC
*ptr = 35
ptr = 00000000
O endereço de foi impresso no formato inteiro decimal e no formato para efeito
de comparação. Os demais endereços foram impressos apenas no formato ponteiro.
Observe que a variável , também chamada variável tipo ponteiro, tem como seu
conteúdo ( ) o endereço da variável ( ).
Introdução à Programação: Conceitos e Práticas. Página 154
Os endereços apresentados estão em uma sequência distantes quatro unidades entre
si. O número quatro é decorrente do tamanho em do tipo e do tipo ponteiro,
que na plataforma utilizada é de quatro bytes. Ao final a instrução atribui
o valor ao ponteiro. A constante possui valor zero. A partir deste ponto um
acesso indireto acarretará em erro, uma vez que faz referência a um endereço de
memória não permitido ao programa.
Várias são as aplicações para ponteiros, sendo elas:
a) Acesso em uma função a variáveis declaradas em outra função, principalmente
para obter os benefícios da passagem de parâmetro por referência;
b) Retornar múltiplos valores de uma função;
c) Forma eficiente de acessar elementos de um array;
d) Alocação dinâmica de memória;
e) Construção de estruturas de dados dinâmicas;
f) Compartilhamento de memória para troca de informações entre processos ou
com aplicações externas;
O conceito de ponteiros foi apenas introduzido. É possível efetuar operações
aritméticas e lógicas com ponteiros. A medida que avançamos no conteúdo vamos
apresentando as funcionalidades adicionais.
No tópico que segue é apresentada uma aplicação fundamental para ponteiros no
contexto da passagem de parâmetro.
Introdução à Programação: Conceitos e Práticas. Página 155
18. Passagem de Parâmetros
O método de passagem de parâmetros estabelece como se dá a ligação entre
argumento e parâmetro. A linguagem permite apenas uma forma de passagem de
parâmetros, denominada passagem por cópia de .
Esta forma de passagem de parâmetro consiste na simples cópia do valor do
argumento para o espaço destinado ao parâmetro. As alterações feitas nos parâmetros
não refletem nos argumentos.
Em várias situações será necessário transferir valores calculados no corpo de uma
função chamada para variáveis que se encontram declaradas na função chamadora.
Grande parte das linguagens de programação disponibilizam uma segunda forma de
passagem de parâmetro, denominada passagem de parâmetro por ou por
.
No caso da linguagem , por não oferecer passagem de parâmetro por de
forma natural, é lançado mão de ponteiros para acesso indireto de memória, que
proporciona os efeitos equivalente à passagem de parâmetro por .
18.1. Passagem de Parâmetro por Cópia
A passagem de parâmetro por cópia é o método disponibilizado na linguagem . O
principal efeito desta forma de passagem de parâmetro é a separação entre argumento
e parâmetro. As alterações feitas nos parâmetros não afetamos argumentos
associados. Observe o seguinte exemplo:
#include <stdio.h>
#include <math.h>
int foo (int a, int b) { //(1)
a = a + 1;
b = b *2;
return a + b;
}; //(2)
int main () {
int x = 7, y = 3, z;
z = foo (x, y);
//(3)
printf ("x = %d\ny = %d\nz = %d", x, y, z);
return 0;
}
Introdução à Programação: Conceitos e Práticas. Página 156
Neste exemplo, tem-se a função que trabalha com dois parâmetros ( e ) e um
programa principal que utiliza esta função através da linha de código: . A
figura a seguir ilustra o contexto da função e de sua utilização.
Figura 50: Contexto da chamada da função foo.
A ligação entre os argumentos e os parâmetros se dá pela semântica do método de
, onde simplesmente o valor do é copiado para o
. Neste exemplo, no momento da chamada o valor do
primeiro argumento ( ) é copiado para o parâmetro , o mesmo acontecendo para o
segundo argumento, cujo valor é copiado para o parâmetro .
Ao executar este programa, a seguinte tela de saída será gerada decorrente do :
x = 7
y = 3
z = 14
Para melhor compreender estes valores, vamos apresentar a configuração de memória
em três momentos distintos, destacados no código fonte como pontos e .
i. Início da função :
Neste momento os valores dos argumentos foram copiados para os parâmetros.
Nesta forma de passagem de parâmetro não há compartilhamento de memória.
Introdução à Programação: Conceitos e Práticas. Página 157
Figura 51: Ponto 1 – Passagem por Cópia.
ii. Final da função :
Neste momento a função inteira já foi processada. Os valores dos parâmetros
e foram alterados e não afetam os argumentos, pois são áreas distintas de
memória. A área destinada ao resultado da função contém o valor final.
Figura 52: Ponto 2 – Passagem por Cópia.
iii. Após a execução da linha de código que utiliza a função :
Após o término da função e retorno ao programa chamador as
áreas de memória destinadas aos parâmetros e resultado da função são
liberadas. Particularmente o resultado da função é copiado para uma área
temporária onde o programa chamador pode acessar o valor e armazenar em
uma área definitiva, no caso a variável .
Figura 53: Ponto 3 – Passagem por Cópia.
Introdução à Programação: Conceitos e Práticas. Página 158
18.2. Passagem de Endereço como Parâmetro
A linguagem não implementa de forma nativa a forma de passagem de parâmetro por
endereço. Logo, é necessário emular esta técnica utilizando ponteiros. A técnica
consiste em passar argumentos, ponteiros para variáveis definidas no contexto do
programa chamador, para os parâmetros da função chamada. Por sua vez, a função
chamada terá acesso indireto às variáveis da função chamadora, sendo que tudo que
for alterado via parâmetros afetará de forma indireta as variáveis da função chamadora.
Vamos modificar o programa anterior que utiliza a função e a própria função ,
para que as atribuições feitas internamente por meio dos parâmetros reflitam nos
argumentos passados.
#include <stdio.h>
#include <math.h>
int foo (int *a, int *b) { //(1)
*a= *a + 1;
*b = *b * 2;
return *a + *b;
}; //(2)
int main () {
int x = 7, y = 3, z;
z = foo (&x, &y);
//(3)
printf ("x = %d\ny = %d\nz = %d", x, y, z);
return 0;
}
Observe as alterações feitas nas definições dos parâmetros. Nesta versão, a função
aguarda como parâmetros os endereços das variáveis que ela terá possibilidade de
alterar. Ao executar este programa, a seguinte tela de saída será gerada decorrente
dos :
x = 8
y = 6
z = 14
A diferença em relação ao programa anterior está nos valores finais das variáveis e ,
que passoaram a conter os valores e respectivamente. Isto foi consequência do
forma de passagem de parâmetro. Observar na sequência a visualização do
comportamento do programa, nos pontos e , perante esta modificação.
Introdução à Programação: Conceitos e Práticas. Página 159
i. Início da função :
O diagrama abaixo apresenta uma diferença visual entre as duas formas de
passagem de parâmetros. O parâmetro está definido como ponteiro e é
utilizado para acesso indireto às variáveis declaradas em outras funções. A
mesma definição foi feita para o parâmetro . A característica principal da
passagem de ponteiro como parâmetro é permitir o
. Na representação gráfica é utilizada
uma notação que indica que o conteúdo do parâmetro é o endereço da variável
no momento da chamada. Neste exemplo, a função ativa a função
vinculando o parâmetro com o endereço de e o parâmetro com a variável .
Figura 54: Ponto 1 – Passagem por “Referência”.
ii. Final da função :
Neste momento a função já foi processada. Os valores das células vinculadas
aos parâmetros e foram alterados. A área destinada ao resultado da função
contém o valor final.
Figura 55: Ponto 2 – Passagem por “Referência”.
iii. Após a execução da linha de código que utiliza a função :
Após o término da função e retorno ao programa chamador as
áreas de memória destinadas aos parâmetros e resultado da função são
liberadas. Particularmente o resultado da função é copiado para uma área
temporária onde o programa chamador pode acessar o valor e armazenar em
Introdução à Programação: Conceitos e Práticas. Página 160
uma área definitiva, no caso a variável . As variáveis e que foram utilizadas
como argumentos reterão os valores atribuídos de forma indireta pelos
parâmetros e . Observar a forma de uso nas expressões, onde significa
acesso à célula vinculada.
Figura 56: Ponto 3 – Passagem por “Referência”.
Assim, fica demonstrada a razão por que apresentará o valor e o valor ao
final do processamento da função.
A simulação da passagem de parâmetro por referência é utilizada quando se deseja
capturar os cálculos feitos por uma função. Nesta situação, basta o programador utilizar
o endereço de uma variável como argumento e estabelecer a ligação
argumento/parâmetro por meio do acesso indireto.
Quando a função produz apenas um resultado de interesse do programa chamador, é
usual que o programador utilize uma o retorno da para implementar a solução.
Porém, se a deve produzir mais de um resultado que interessa ao programa
chamador, então a passagem do endereço pode ser utilizada para capturar estes
valores.
Introdução à Programação: Conceitos e Práticas. Página 161
18.3. Exemplo
Vamos utilizar o problema do cálculo das raízes de uma equação do segundo grau para
ilustrar bem o caso de um módulo que deve produzir mais de um resultado. É esperado
que uma lógica deste tipo produzisse pelo menos as duas raízes. Assim, segue uma
solução para o problema de obter as raízes de uma equação do segundo grau a partir
dos coeficientes , e .
As raízes são calculadas pela expressão:
√
Segue a primeira versão de um programa para o cálculo das raízes.
#include <stdio.h>
#include <math.h>
void raizes (double a, double b, double c,
double *r1, double *r2) { //(1)
double delta;
delta = b*b - 4*a*c;
*r1 = (-b + sqrt(delta))/(2*a);
*r2 = (-b - sqrt(delta))/(2*a);
}; //(2)
int main () {
double x1, x2;
raizes (1, -12, 35, &x1, &x2);
//(3)
printf ("raiz 1 = %.5lf\nraiz 2 = %.5lf", x1, x2);
return 0;
}
A solução adotada utiliza uma função para cálculo das raízes com retorno . Desta
forma todo o trabalho da função será capturado por meio de acesso indireto via
parâmetros. Os parâmetros , e são passados por cópia de vaor e os parâmetros
e são cópia de ponteiros. Estes últimos parâmetros servirão como mecanismo para
transferir o resultado para a função chamadora, que neste caso é a função . O
diagrama mais adiante ilustra o contexto da .
Introdução à Programação: Conceitos e Práticas. Página 162
Observar na sequência a visualização do comportamento do programa, nos pontos
e :
i. Início da função :
O diagrama abaixo destaca as duas formas de passagem de parâmetros. Os
parâmetros , e utilizam o mecanismo de passagem de cópia do valor do
argumento e os parâmetros e utilizam o mecanismo de passagem de cópia
de endereço. A chamada da função na função é dada por:
Os argumentos e são copiados para os parâmetros , e , enquanto
que os dois argumentos seguintes expressam os endereços das variáveis e
. Desta forma, através dos parâmetros e a função terá acesso
indireto às variáveis e . Os valores iniciais d são desconhecidos,
uma vez que não foram inicializadas. É criada uma variável local chamada
para armazenar o valor do delta de equação de grau.
Figura 57: Ponto 1 – Calculo Raizes.
Introdução à Programação: Conceitos e Práticas. Página 163
ii. Final da função :
Neste momento a função já foi processada. As raízes foram calculadas e
armazenadas nas células apontadas pelos parâmetros e : e .
Figura 58: Ponto 2 – Calculo Raizes.
iii. Após a execução da linha de código que utiliza a função :
Após o término da execução da função e retorno ao programa chamador,
imediatamente após a linha as áreas de memória
destinadas aos parâmetros e variáveis locais são liberadas. As variáveis e
que tiveram seus endereços como argumentos reterão os últimos valores defidos
pelas atribuições a * e .
Figura 59: Ponto 3 – Calculo Raizes.
Introdução à Programação: Conceitos e Práticas. Página 164
Ao imprimir os valores de e , será visualizada a seguinte tela:
raiz 1 = 7.00000
raiz 2 = 5.00000
Introdução à Programação: Conceitos e Práticas. Página 165
19. Expressões Lógicas
Expressão lógica é um recurso fundamental nas linguagens de programação, pois é
elemento presente na construção de qualquer algoritmo significativo. O tipo de dado
e os operadores lógicos formam a base para estas expressões. Os operadores
lógicos trabalham sobre os valores e , que representam condições ou
, produzindo resultados deste mesmo conjunto.
19.1. Tipo de Dado Boolean
Em , o tipo de dado é a base para as expressões lógicas. Uma variável
admite os valores { } e ocupa de memória. O seguinte
exemplo ilustra o tipo .
Program ExBoolean;
Var B : Boolean;
Begin
B := False;
Writeln (Ord (B));
B := True;
Writeln (Ord (B));
End.
Observe no exemplo a atribuição dos valores e à variável . O tipo
é um ordinal, sendo que o é zero e o é . É importante observar
que as constantes são e sem o uso de apóstrofes.
De forma semelhante ao descrito anteriormente, uma expressão lógica combinavariáveis, operadores lógicos, constantes e funções a fim de produzir um resultado que
também é um valor lógico.
Introdução à Programação: Conceitos e Práticas. Página 166
19.2. Operadores Lógicos
O tipo de dado admite as seguintes operações lógicas e respectivas tabelas
verdade:
Operadores Tabelas-Verdade
i.
O resultado é quando todos
os operandos forem .
AND F T
F F F
T F T
AND
A B X
F F F
F T F
T F F
T T T
ii.
O resultado é quando
todos os operandos forem
.
OR F T
F F T
T T T
OR
A B X
F F F
F T T
T F T
T T T
iii.
É um exclusivo, ou seja difere
do quando os dois operandos
forem , produzindo neste
caso um resultado .
OR F T
F F T
T T F
OR
A B X
F F F
F T T
T F T
T T F
iv.
Retorna a negação do valor do
operando.
NOT F T
T F
NOT
A X
F T
T F
Introdução à Programação: Conceitos e Práticas. Página 167
O seguinte exemplo ilustra o uso de operadores lógicos:
Var B : Boolean;
Begin
B := (True Or False) And (True Xor True);
Writeln (B); // imprimirá False
B := Not B;
Writeln (B); // imprimirá True
End.
A expressão é avaliada da seguinte forma:
O operador também é chamado de e o operador de
. Esta denominação permite compreender melhor a prioridade na execução
das operações. O operador é executado antes que o operador quando
estiverem no mesmo nível na expressão. O parêntesis deve ser utilizado quando se
deseja priorizar outra ordem.
Está claro que codificar expressões lógicas operando somente sobre constantes
e não permitirá construir programas práticos. Assim, normalmente utilizamos os
para produzir os valores e que necessitamos em
uma expressão lógica.
19.3. Operadores Relacionais
Os operadores relacionais funcionam como se fossem perguntas feitas sobre seus
operandos a fim de produzir respostas do tipo ou . A sintaxe destes
operadores demanda a presença de dois operandos, sendo um à esquerda e outro à
direita do operador. Os operandos podem ser expressões de qualquer tipo, porém
compatíveis entre si. Assim, se um operando é de natureza textual ( ou ), o
outro também deverá ser do tipo textual. Da mesma forma, se um operando produz um
valor numérico ( ou ), o outro também deverá ser do tipo numérico. Os
operadores relacionais do são: e , cuja descrição é:
Introdução à Programação: Conceitos e Práticas. Página 168
Operador Relacional Exemplos
i.
Avalia se o valor do primeiro argumento é
ao valor do segundo argumento.
Aplica-se tanto a valores numéricos quanto
textuais.
Var FA, FB, FC : Boolean;
S : String;
A : Integer;
X : Real;
Ch : Char;
Begin
// FA será True se a string S conter
// 'Joao' e a variável A for maior
// que 30 e X diferente de zero.
FA := (S = 'Joao') And
(A > 30) And
(X <> 0);
// FB será True se o char em Ch for o
// caractere 'a' ou 'e' ou 'i'
FB := (Ch = 'a') Or
(Ch = 'e') Or
(Ch = 'i');
// FC será True se o char em Ch
// estiver entre o caractere '0'
// e '9' na tabela ASCII.
FC := (Ch >= '0') And (Ch <= '9');
// FA será True se o valor de X
// for maior que raiz quadrada de 2
// e menor que raiz quadrada de 3
FA := (X > Sqrt(2)) And
(X < Sqrt(3));
// FB será True se o valor de X
// for menor ou igual a
// OU maior ou igual a
FB := (X <= PI/2) OR
(X >= PI);
// FC será True se a string S
// contiver uma sequencia de
// caracteres que resulte
// alfabeticamente a frente (maior)
// da string 'Pedro'
FC := (S > 'Pedro');
// FA será True se o primeiro
// caractere da string S estiver
// entre o caracter 'a' e 'z' na
// tabela ASCII.
FA := (S[1] >= 'a') AND
(S[1] <= 'z');
End.
ii.
Avalia se o valor do primeiro argumento é
que o valor do segundo argumento.
No caso de argumentos o
entendimento é o que normalmente
utilizamos em matemática. Quando os
argumentos forem , o termo se
aplica a posição do elemento na tabela
. Um char é que outro quando
ocupa uma posição superior na tabela .
Se os argumentos forem , o termo
equivale a perguntar se o primeiro
argumento vem após o segundo argumento
no caso de ordem alfabética, também
orientada pela tabela .
iii. l)
Explicação semelhante ao .
iv.
Explicação semelhante ao .
v.
Explicação semelhante ao .
vi.
Explicação semelhante ao
Introdução à Programação: Conceitos e Práticas. Página 169
19.4. Exemplos
i. Elaborar um programa que informa se um determinado caractere é uma vogal.
A solução proposta utiliza uma função chamada que avalia se o caractere dado
é ou não uma vogal. O programa principal serve para testar alguns casos com o objetivo
de validar a lógica. A função pode ser codificada de várias maneiras, utilizando
apenas os recursos já apresentados.
Program ExLOGICA;
Function IsVogal0 (Ch : Char) : Boolean;
Begin
Ch := UpCase (Ch);
IsVogal0 := Pos(Ch, 'AEIOU') <> 0;
End;
Function IsVogal (Ch : Char) : Boolean;
Begin
Ch := UpCase (Ch);
IsVogal := (Ch = 'A') OR (Ch = 'E') OR
(Ch = 'I') OR (Ch = 'O') OR
(Ch = 'U');
End;
Begin
Writeln (IsVogal('a'));
Writeln (IsVogal('O'));
Writeln (IsVogal('0'));
Writeln (IsVogal('T'));
End.
A primeira versão da função utiliza a função para determinar se um
caractere é vogal ou não. Neste caso a pergunta que se faz é se a posição do caractere
na string é diferente de zero. O caso afirmativo indica que é um dos
caracteres indicados, ou seja, é uma vogal. Observar que antes disso o conteúdo da
variável é substituído pelo seu equivalente maiúsculo, com o uso da função .
A segunda solução utiliza uma expressão lógica completa composta por cinco
perguntas, sendo cada uma delas avaliando se o caractere é uma das vogais. Um
principiante em programação normalmente pergunta se a expressão não poderia ser
escrita na forma:
A resposta é , pois os operadores lógicos necessitam de operandos booleanos e
não do tipo como utilizado.
A interface de entrada e saída para o programa é:
Introdução à Programação: Conceitos e Práticas. Página 170
TRUE
TRUE
FALSE
FALSE
ii. Elaborar um programa que informa se um determinado caractere é uma consoante.
A solução proposta utiliza duas funções de apoio, sendo uma a e outra a
que avalia se um caractere é ou não uma letra do alfabeto. Assim, um caractere
será uma consoante se for ao mesmo tempo uma letra e não for vogal. O programa
principal serve para testar alguns casos com o objetivo de validar a lógica.
Program ExLOGICA;
Function IsLetra (Ch : Char) : Boolean;
Begin
Ch := UpCase (Ch);
IsLetra := (Ch >= 'A') AND (Ch <= 'Z');
End;
Function IsVogal (Ch : Char) : Boolean;
Begin
Ch := UpCase (Ch);
IsVogal := (Ch = 'A') OR (Ch = 'E') OR
(Ch = 'I') OR (Ch = 'O') OR
(Ch = 'U');
End;
Function IsConsoante (Ch : Char) : Boolean;
Begin
Ch := UpCase (Ch);
IsConsoante := IsLetra(Ch) And (Not IsVogal(Ch));
End;
Begin
Writeln (IsConsoante('a'));
Writeln (IsConsoante('B'));
Writeln (IsConsoante('x'));
Writeln (IsConsoante('3'));
End.
A função testa se um caractere está entre o e o na tabela . A função
combina adequadamente as funçõese através da
expressão:
Introdução à Programação: Conceitos e Práticas. Página 171
A função retornará se retornar e
retornar .
A interface de entrada e saída para o programa é:
FALSE
TRUE
TRUE
FALSE
Introdução à Programação: Conceitos e Práticas. Página 172
20. Instruções de Controle
Até o momento foram apresentados recursos de programação que permitem escrever
algoritmos compostos por linhas de código cuja execução ocorre em sequência, uma
linha após a outra. Ao concluir o processamento de uma linha a próxima será
executada, e assim sucessivamente. A seguinte figura ilustra o fluxo de processamento
de um módulo contendo apenas instruções executadas em .
O processamento inicia no e flui executando cada uma das linhas de código,
até alcançar o final do módulo no .
Estas linhas de código, com os recursos vistos, podem ser do tipo comandos de
atribuição ou chamadas de procedures. O lado direito dos comandos de atribuição pode
incluir expressões com funções. Assim, quando uma linha de código invoca uma
ou , tem-se um fluxo semelhante ao da figura que segue:
Ainda nesta situação, tem-se o seguinte fluxo único para a execução do programa.
Os problemas práticos demandam programas robustos o suficiente, que não produzam
erros de execução que causam a parada do sistema. Isto significa dizer que as lógicas
devem ser completas no sentido de incluir o tratamento adequado de possíveis erros e
quando possível prevenir que instruções sejam executadas na iminência de falhas.
Como exemplo, citamos a lógica já implementada de cálculo das duas raízes de uma
equação do segundo grau. A lógica codifica apenas as expressões que levam ao
cálculo das raízes. Porém, há erros potenciais que podem se manifestar quando as
Introdução à Programação: Conceitos e Práticas. Página 173
expressões forem avaliadas, tais como a divisão por zero e a raiz de um delta negativo.
Assim, o programador deve pensar na lógica que desvie o fluxo de processamento para
um caminho seguro que evita a avaliação das expressões nos casos que levam a erro
de execução. As linguagens de programação disponibilizam instruções que permitem
ao programador fazer a entre caminhos alternativos de processamento.
Outra categoria de problemas práticos requer a codificação de cálculos que repetem um
mesmo padrão da lógica por inúmeras vezes. Um exemplo deste comportamento é o
cálculo da função através de uma série, onde as parcelas possuem uma forma
padrão. Para estes problemas o algoritmo normalmente submete a forma padrão a um
processamento repetitivo de cálculo. Assim, é fundamental que a linguagem permita
que uma ou mais instruções sejam processadas várias vezes sem que o programador
tenha que duplicar códigos semelhantes. A estrutura que permite este recurso é
denominada de .
Em síntese, uma linguagem de programação de alto nível deve prover mecanismos de
controle do fluxo de execução que se enquadrem nas seguintes categorias:
;
; e
.
Assim, é possível codificar qualquer problema computável combinando estas três
construções de controle.
20.1. Seleção – IF-THEN-ELSE
O disponibiliza uma instrução de seleção que possibilita ao usuário definir dois
caminhos alternativos que serão escolhidos no momento da execução do programa
dependendo do valor de uma expressão lógica. Como a expressão lógica admite dois
valores possíveis, então um caminho de processamento é associado ao valor e o
outro caminho é associado ao valor .
20.1.1. Sintaxe e Semântica
A sintaxe da instrução é:
Desenhando um diagrama para melhor visualizar a estrutura da , tem-se:
Introdução à Programação: Conceitos e Práticas. Página 174
Se qualquer um dos caminhos tiver mais de uma instrução associada, então as
instruções deverão estar definidas como blocos através do uso do ⋯ .
A execução do inicia com a avaliação da expressão lógica e em
seguida com o teste do valor resultante. Caso o valor da expressão seja então
serão processadas as instruções associadas ao caminho . Caso contrário, serão
executadas as instruções associadas ao caminho .
A cláusula é opcional, o que proporciona uma variação da instrução:
O diagrama para a é dado por:
A execução do inicia com a avaliação da expressão lógica e em seguida com
o teste do valor resultante. Caso o valor da expressão seja então serão
processadas as instruções associadas ao caminho . Caso contrário, o
processamento segue para a próxima instrução após a instrução .
Introdução à Programação: Conceitos e Práticas. Página 175
20.1.2. Exemplos
Os exemplos abaixo relacionados apresentam várias formas de utilização da instrução de
controle a partir de diagramas com as lógicas.
Diagrama Programa
Program exIF_THEN;
Var X, Y : Real;
A, B : Integer;
Begin
i.
If X > Y
Then A := 1
Else A := 2;
// Se a expressão for então será
// executada a instrução caso contrário
// será executada a instrução .
ii.
If X > 10 Then
Begin
A := 1;
B := 2;
End
Else A := B + 1;
// Se a expressão for então
// serão executadas as duas instruções
// associadas a cláusula , caso contrário
// será executada a única instrução associada ao
// . Observe o ⋯ envolvendo as
// duas instruções associadas ao .
iii.
If X <= 10
Then A := 1
Else Begin
A := 2;
B := 1;
End;
// Observe a ausência do antes do
Introdução à Programação: Conceitos e Práticas. Página 176
iv.
If X >= 10 Then
Begin
A := -1;
B := -2;
End
Else Begin
A := 3;
B := 4;
End;
End.
. Observe nos exemplos
anteriores que o nunca vem precedido deste símbolo. Outra situação de destaque é a
presença de , como nos seguintes casos:
If X > Y Then
If X <> 0
Then A := 1
Else A := 2;
If X > Y Then
If X <> 0
Then A := 1
Else A := 2;
Observe que as soluções acima são idênticas, pois possuem exatamente a mesma sequência
de palavras ( ). O deslocamento ( ) do texto mais para esquerda ou para a
direita não afeta em nada a forma como a linguagem interpreta a sentença. Assim, uma das
duas construções não representa o diagrama correspondente. Está claro no desenho que são
lógicas distintas. Este caso ficou conhecido no Algol como . A linguagem
resolve esta ambiguidade afirmando que na dúvida a que pertence o , o fica
vinculado ao mais interno. Havendo a necessidade em vincular o ao mais externo,
um bloco ⋯ deverá ser utilizado, como indicado no código abaixo:
If X > Y Then
Begin
If X <> 0
Then A := 1
End
Else A := 2;
If X > Y Then
If X <> 0
Then A := 1
Else A := 2;
Introdução à Programação: Conceitos e Práticas. Página 177
v. Calcular as raízes e de uma equação do segundo grau a partir dos coeficientes ,
e . A solução deverá produzir um valor que indica o dos cálculos de
acordo com a seguinte convenção:
0 sem erro: raízes calculadas
1 se
2 se
A solução adotada anteriormente para o cálculo das raízes poderá em fim ser
completada com o tratamento adequado das condições que poderiam levar a um erro na
execução da lógica.
Assim, devemos construiruma nova lógica que desvia o fluxo de execução do módulo
para que não ocorram nem nem .
Observe no diagrama abaixo proposto, que a cláusula é utilizada para
testar inicialmente o valor do coeficiente e desviar para o cálculo das raízes somente
no caso de passar pelo teste de . Superado este primeiro teste, o é
calculado e um novo teste é feito direcionando o fluxo para o cálculo das raízes somente
em caso de . Nos casos de falha nos testes a cláusula else é executada
atribuindo ao parâmetro a condição do erro.
Figura 60: Diagrama lógico para o cálculo das raízes de uma equação do 2º grau.
Program ExRaizes;
procedure raizes (a, b, c : real;
var r1, r2 : real;
var code : integer);
var d : real;
begin
Introdução à Programação: Conceitos e Práticas. Página 178
if a <> 0 then
begin
d := b*b - 4*a*c;
if d >= 0 then
begin
code := 0;
r1 := (-b + sqrt(d))/(2*a);
r2 := (-b - sqrt(d))/(2*a);
end
else code := 2;
end
else code := 1;
end;
procedure show (code : integer; x1, x2 : real);
begin
If code = 0 Then
begin
Writeln ('Raiz 1 = ', x1:8:4);
Writeln ('Raiz 2 = ', x2:8:4);
end
Else If Code = 1
Then writeln ('erro: a = 0')
Else If Code = 2
Then writeln ('erro: delta < 0');
end;
var a1, b1, c1, x1, x2 : real;
code : integer;
begin
Writeln ('Entre com os coeficientes A, B e C:');
Readln (a1, b1, c1);
raizes (a1, b1, c1, x1, x2, code);
show (code, x1, x2);
end.
A solução proposta inclui o módulo que permite apresentar na tela os resultados
do processamento da seguinte forma:
Se mostrar as raízes calculadas
Se mostrar a mensagem
Se mostrar a mensagem
Introdução à Programação: Conceitos e Práticas. Página 179
A seguinte tela mostra os resultados de quatro processamentos independentes do
programa completo:
Entre com os coeficientes A, B e C:
1 4 4
Raiz 1 = -2.0000
Raiz 2 = -2.0000
Entre com os coeficientes A, B e C:
1 6 3
Raiz 1 = -0.5505
Raiz 2 = -5.4495
Entre com os coeficientes A, B e C:
4 6 3
erro: delta < 0
Entre com os coeficientes A, B e C:
0 3 5
erro: a = 0
vi. Uma família de problemas muito comum em situações reais é a relacionada com a
manipulação de datas. Assim, este exemplo pede uma solução para o cálculo da
quantidade de dias de um determinado mês e ano.
A solução consiste em retornar a quantidade de dias conforme o valor do parâmetro
mês. Para os meses e a quantidade de dias é ; para os meses
e a quantidade de dias é ; e para o mês a quantidade de dias é para anos
normais e para ano bissexto. Assim, a solução proposta inclui uma função para
indicar se um ano é bissexto ou não.
Em função das inúmeras alterações que o calendário foi submetido ao longo dos
tempos, acabou sendo consolidado o padrão solar. O ano solar, ou seja, o tempo que a
leva para completar um ciclo em torno do te a duração de
ou dias. Para sincronizar o calendário e o tempo solar foi introduzido, pelos
imperadores e , o ano bissexto para compensar a fração de dia não
considerada no total de dias do ano. Esta mudança fez com que os anos que fossem
múltiplos de 4 passassem a ter dias, e os demais dias. Mesmo com esta
alteração, a diferença entre a duração média do novo calendário com dias e o
valor real de conduzia a defasagem significativa no longo prazo. Deste modo,
o , em , suprimiu dias do calendário e
determinou que o dia fosse seguido pelo dia . As regras para
foram ajustadas e ficou estabelecido que10:
de em anos é ano bissexto.
de em anos não é ano bissexto.
de em anos é ano bissexto.
10
http://pt.wikipedia.org/wiki/Ano_bissexto
Leap Years: the rule. U.S. Naval Observatory (14 September 2007).
http://pt.wikipedia.org/wiki/Ano_bissexto
http://aa.usno.navy.mil/faq/docs/leap_years.php
Introdução à Programação: Conceitos e Práticas. Página 180
prevalecem as últimas regras sobre as primeiras.
De forma simplificada os anos múltiplos de são bissextos. Os anos múltiplos de
são excluídos, e os múltiplos de incluídos novamente. Segue a implementação
proposta:
Program exDATA;
Function IsLeap (A : Integer) : Boolean;
Begin
IsLeap := (A Mod 4 = 0) And
((A Mod 100 <> 0) Or (A Mod 400 = 0));
End;
Function NumberOfDays (M, A : Integer) : Integer;
Begin
If (M = 1) OR (M = 3) OR (M = 5) OR (M = 7) OR
(M = 8) OR (M = 10) OR (M = 12) Then
NumberOfDays := 31
Else If (M = 4) OR (M = 6) OR (M = 9) OR (M = 11) Then
NumberOfDays := 30
Else If (M = 2) Then
If IsLeap (A) Then
NumberOfDays := 29
Else
NumberOfDays := 28
Else
NumberOfDays := 0;
End;
Begin
Writeln (NumberOfDays ( 2, 2000));
Writeln (NumberOfDays ( 1, 1996));
Writeln (NumberOfDays ( 2, 1996));
Writeln (NumberOfDays (12, 1600));
Writeln (NumberOfDays ( 2, 2013));
Writeln (NumberOfDays (15, 2000));
End.
A saída para o programa é:
29
31
29
31
28
0
O seguinte pode ser substituído por uma expressão:
Introdução à Programação: Conceitos e Práticas. Página 181
If IsLeap (A)
Then NumberOfDays := 29
Else NumberOfDays := 28
A função retorna ou , que se convertido para ou pode dar origem a
seguinte expressão:
NumberOfDays := 28 + Ord(IsLeap (A));
A expressão faz a conversão do valor retornado por
para inteiro ( ou ). Assim, a função ficaria conforme segue:
Function NumberOfDays (M, A : Integer) : Integer;
Begin
If (M = 1) OR (M = 3) OR (M = 5) OR (M = 7) OR
(M = 8) OR (M = 10) OR (M = 12) Then
NumberOfDays := 31
Else If (M = 4) OR (M = 6) OR (M = 9) OR (M = 11) Then
NumberOfDays := 30
Else If (M = 2) Then
NumberOfDays := 28 + Ord(IsLeap (A))
Else
NumberOfDays := 0;
End;
vii. Implementar uma solução que incrementa em um dia uma data válida (dia, mês e ano).
A solução consiste em incrementar o dia em , e testar se o resultado não extrapolou a
quantidade de dias permitida para o mês. Em caso positivo, o dia passa para e o mês
é incrementado de , testando agora se o mês não excedeu aos permitidos para o
ano. Em caso positivo, o mês passa para e o ano é incrementado de . Segue esta
solução:
Program exDATA;
Function IsLeap (A : Integer) : Boolean;
{ igual a implementação anterior}
Function NumberOfDays (M, A : Integer) : Integer;
{ igual a implementação anterior}
Procedure IncDate (Var D, M, A : Integer);
Begin
Inc (D);
If D > NumberOfDays (M, A) Then
Begin
D := 1;
Introdução à Programação: Conceitos e Práticas. Página 182
Inc (M);
If M > 12 Then
Begin
M := 1;
Inc (A);
End;
End;
End;
Var Dia, Mes, Ano : Integer;
Begin
Writeln ('Entre com uma data: dd mm aaaa');
Readln (Dia, Mes, Ano);
IncDate (Dia, Mes, Ano);
Writeln ('O dia seguinte é: ', Dia, Mes:3, Ano:5);
End.
A solução adotada utiliza como opção, uma vez que a melhor modelagem
para este problema é fornecer três variáveis para a rotina e esperar que a lógica da
modifique estas variáveis com os novos valores para a data adiantada em um
dia. Em função disso, a adotada é a ,
permitindo que toda alteração nos parâmetros possa ser captada pelos argumentos.
Os módulos e tiveram seus códigos omitidos para melhor
visualização da solução.A seguinte tela ilustra as entradas e saídas para alguns testes:
Entre com uma data: dd mm aaaa
1 1 2000
O dia seguinte é: 2 1 2000
Entre com uma data: dd mm aaaa
10 10 2010
O dia seguinte é: 11 10 2010
Entre com uma data: dd mm aaaa
31 1 2012
O dia seguinte é: 1 2 2012
Entre com uma data: dd mm aaaa
31 12 2009
O dia seguinte é: 1 1 2010
Introdução à Programação: Conceitos e Práticas. Página 183
20.2. Seleção – CASE
A construção de controle do tipo proporciona dois caminhos
alternativos para o fluxo de processamento. Uma expressão do tipo é utilizada
como ponto de decisão para a escolha do trajeto a ser tomado. Um dos caminhos está
associado ao valor e o caminho alternativo está associado ao valor .
Em determinados problemas é comum que o código resultante se enquadre em um
aninhamento (estruturas embutidas) de , como o visto na função
. Especificamente para situações semelhantes, a linguagem
disponibiliza a construção que permite a escolha de um entre múltiplos caminhos
possíveis. O ponto de decisão deixa de ser uma expressão booleana, que admite
apenas dois valores, e passa a ser uma expressão ordinal, que no caso dos inteiros e
char, admitem múltiplos valores.
20.2.1. Sintaxe e Semântica
A sintaxe da construção de controle é dada por:
A versão original do não apresenta a cláusula . Algumas versões de
compiladores aceitam a palavra como variação do . Se várias
instruções estiverem associadas à condição, então deve ser utilizado o bloco
⋯ para envolver as instruções. O seguinte diagrama ilustra o significado da
construção :
Introdução à Programação: Conceitos e Práticas. Página 184
Figura 61: Instrução CASE
Em grande parte dos problemas a expressão ordinal utilizada para direcionar o é
do tipo inteiro ou char. Além destes, a expressão pode ser do tipo enumerado, cujo
conceito será visto mais adiante. Não é permitido o uso de expressão do tipo ou
, pois estes tipos não são ordinais.
O diagrama anterior indica que o inicia com uma expressão ordinal, cujo resultado
é testado quanto a pertinência a um conjunto de valores, que em caso positivo as
instruções associadas serão executadas. Em caso negativo, é feito o teste seguinte em
relação a outro conjunto de valores, que em caso positivo as instruções associadas
serão executadas, e assim sucessivamente. Opcionalmente, pode ser incluída uma
cláusula que será executada no caso de falha dos testes anteriores.
Os exemplos que seguem destacam as formas de uso desta construção.
Introdução à Programação: Conceitos e Práticas. Página 185
20.2.2. Exemplos
i. Refazer o programa que informa se um determinado caractere é uma consoante.
As funções e serão reescrita utilizando a construção , conforme o
código listado abaixo:
Program ExLOGICA;
Function IsLetra (Ch : Char) : Boolean;
Begin
Case UpCase(Ch) Of
'A'..'Z' : IsLetra := True;
Else
IsLetra := False;
End;
End;
Function IsVogal (Ch : Char) : Boolean;
Begin
Case UpCase(Ch) Of
'A', 'E', 'I', 'O', 'U' : IsVogal := True;
Else
IsVogal := False;
End;
End;
Function IsConsoante (Ch : Char) : Boolean;
Begin
Ch := UpCase (Ch);
IsConsoante := IsLetra(Ch) And (Not IsVogal(Ch));
End;
Begin
Writeln (IsConsoante('a'));
Writeln (IsConsoante('B'));
Writeln (IsConsoante('x'));
Writeln (IsConsoante('3'));
End.
O utilizado na função testa se um caractere está entre o e o na
tabela :
Case UpCase(Ch) Of
'A'..'Z' : IsLetra := True;
Else
IsLetra := False;
End;
Introdução à Programação: Conceitos e Práticas. Página 186
A expressão avaliada é dada por . O primeiro e único teste é pela
pertinência deste valor no conjunto de valores dado pelos elementos formados pelos
caracteres de a . O uso de dois pontos seguidos equivale ao
conjunto formado por todos elementos neste intervalo. Se este teste falhar, será
executada a instrução associada à cláusula .
O utilizado na função avalia se o caractere dado pertence ao conjunto
formado pelas vogais.
Case UpCase(Ch) Of
'A', 'E', 'I', 'O', 'U' : IsVogal := True;
Else
IsVogal := False;
End;
A expressão avaliada é dada novamente por . O primeiro e único teste é
pela pertinência deste valor no conjunto formado pelas vogais { }.
A função não foi alterada, e retornará se o resultado de
for e o resultado de for .
A saída para o programa é:
FALSE
TRUE
TRUE
FALSE
ii. Refazer o programa para o cálculo da quantidade de dias de um determinado mês e
ano.
Segue a solução proposta:
Program exDATA;
Function IsLeap (A : Integer) : Boolean;
Begin
IsLeap := (A Mod 4 = 0) And
((A Mod 100 <> 0) Or (A Mod 400 = 0));
End;
Function NumberOfDays (M, A : Integer) : Integer;
Begin
Case M Of
1, 3, 5, 7, 8, 10, 12 : NumberOfDays := 31;
4, 6, 9, 11 : NumberOfDays := 30;
2 : NumberOfDays := 28 + Ord(IsLeap(A))
Else
Introdução à Programação: Conceitos e Práticas. Página 187
NumberOfDays := 0;
End;
End;
Procedure IncDate (Var D, M, A : Integer);
Begin
Inc (D);
If D > NumberOfDays (M, A) Then
Begin
D := 1;
Inc (M);
If M > 12 Then
Begin
M := 1;
Inc (A);
End;
End;
End;
Var Dia, Mes, Ano : Integer;
Begin
Writeln ('Entre com uma data: dd mm aaaa');
Readln (Dia, Mes, Ano);
IncDate (Dia, Mes, Ano);
Writeln ('O dia seguinte é: ', Dia, Mes:3, Ano:5);
End.
Observe que a função , reescrita com o , apresenta uma sensível
melhora na legibilidade em relação a versão anterior com .
iii. Fornecer uma solução para a conversão de um número decimal de a para o dígito
hexadecimal em forma de caractere. Incluir também a conversão de um caractere
contendo um dígito hexadecimal ( a ) em seu equivalente inteiro decimal.
Segue a solução proposta:
Program ExHEXA;
Function HexDigToDec (Ch : Char) : Integer;
Begin
Ch := UpCase (Ch);
Case Ch Of
'0'..'9' : HexDigToDec := Ord(Ch) - Ord('0');
'A'..'F' : HexDigToDec := Ord(Ch) - Ord('A') + 10;
Else
HexDigToDec := -1;
End;
Introdução à Programação: Conceitos e Práticas. Página 188
End;
Function DecToHexDig (X : Integer) : Char;
Begin
Case X Of
0..9 : DecToHexDig := Chr(Ord('0') + X);
10..15 : DecToHexDig := Chr(Ord('A') + X - 10);
Else
DecToHexDig := 'X';
End;
End;
Begin
Writeln (HexDigToDec ('A'));
Writeln (HexDigToDec ('0'));
Writeln (HexDigToDec ('f'));
Writeln (HexDigToDec ('B'));
Writeln (DecToHexDig (0 ));
Writeln (DecToHexDig (10));
Writeln (DecToHexDig (9 ));
Writeln (DecToHexDig (15));
Writeln (DecToHexDig (20));
End.
A função recebe um caractere qualquer e o processa de acordo com a
convenção para os dígitos hexadecimais, ou seja:
Se for um dos caracteres de a então o resultado é o valor da expressão
, onde é o número com a posição de na tabela
, que subtraído de , fornecerá o resultado de 0 a 9, conforme
desejado.
Se for um dos caracteres de a então o resultado é o valor da expressão
, onde é o número com a posição de na
tabela , que subtraído de , fornecerá um valor de a , que
somado com , retornará o resultado dea , conforme desejado.
Qualquer outro caractere resultará em .
A função codifica o comportamento inverso da função anterior. Fica a
cargo de o leitor estudar e entender a lógica utilizada.
Introdução à Programação: Conceitos e Práticas. Página 189
A saída para o programa é:
10
0
15
11
0
A
9
F
X
As conversões pedidas podem ter lógicas alternativas utilizando outros recursos já
apresentados. Segue outra versão possível:
Program ExHEXA;
Function HexDigToDec (Ch : Char) : Integer;
Begin
HexDigToDec := Pos (UpCase(Ch), '0123456789ABCDEF') - 1;
End;
Function DecToHexDig (X : Integer) : Char;
Var S : String;
Begin
S := '0123456789ABCDEF';
Case X Of
0..15 : DecToHexDig := S[X+1];
Else
DecToHexDig := 'X';
End;
End;
Begin
{mesmo programa principal anterior}
End.
A solução para utiliza a função para obter a posição do caractere
na , cujo resultado será um número de a , dependendo
do caractere. Para obter o equivalente decimal basta subtrair deste valor. Caso o
caractere não seja nenhum dos relacionados na , então retornará que
subtraído , resultará em para esta condição.
A função retorna um dos caracteres da de apoio
quando o valor de estiver entre 0 e 15. O caractere desejado é
o que se encontrar na posição , uma vez que quando é , o caractere
correspondente se encontra na posição .
Introdução à Programação: Conceitos e Práticas. Página 190
Quando se deseja apenas um caractere de uma , , mas
sim o . Por exemplo, se for necessário atribuir a variável o caractere
que está na posição de , a forma [ ] é preferível em relação a
.
20.3. Repetição – FOR⋯DO
A estrutura de repetição completa os recursos de uma linguagem tipo e amplia
as possibilidades de controle sobre o fluxo de execução de um programa. A estrutura
de repetição permite que uma ou mais instruções sejam executadas várias vezes de
acordo com a necessidade do problema. Assim, ao modelar um problema e desenhar o
algoritmo, deve ser pensada uma lógica que represente a situação de uma forma geral.
Por exemplo, se é necessário obter as notas de alunos para calcular a média da
turma, não é razoável construir uma lógica com . Deve haver um
mecanismo que determine a repetição de vezes a instrução desejada.
As linguagens da família disponibilizam várias estruturas de repetição, sendo
elas:
Repetição controlada por contador;
Repetição controlada por expressão lógica;
o Pré-teste;
o Pós-teste
A repetição controlada por contador também é conhecida como estrutura tipo , e é
apresentada neste tópico.
20.3.1. Sintaxe e Semântica
A sintaxe da instrução de repetição tipo FOR é:
Onde,
é uma variável do tipo ordinal denominada da repetição ( ) ou
apenas ;
é uma expressão ordinal compatível com o tipo de , cujo resultado indica o valor
inicial de ;
é uma expressão ordinal compatível com o tipo de , cujo resultado indica o valor
final de ;
Introdução à Programação: Conceitos e Práticas. Página 191
A seguinte figura ilustra os componentes da instrução , bem como destaca a
dinâmica da estrutura com os vários ciclos de execução da instrução controlada, onde
cada ciclo tem o valor do contador incrementado de .
Figura 62: Repetição do tipo FOR.
O seguinte diagrama lógico permite visualizar o fluxo do processamento:
Figura 63: Estrutura de Controle do tipo FOR
Neste diagrama podemos verificar o funcionamento da instrução . A variável de
controle é inicializada com o valor , em seguida é realizado um teste para certificar
que . Sendo verdadeiro, então as instruções controladas são executadas e o
valor de é incrementado de . O fluxo é novamente direcionado para a pergunta sobre
o valor de em relação a . A repetição se encerra quando .
Introdução à Programação: Conceitos e Práticas. Página 192
20.3.2. Exemplos
i. Implementar a seguinte somatória para N termos:
⋯
A série possui termos, onde pode ser qualquer valor maior que um. Os termos da
série possuem a seguinte forma geral:
Assim, é necessário agora formular uma solução para gerar cada um dos termos, e
somá-los a fim de atender a especificação do problema. Segue solução implementada:
Program ExSERIE;
Function SomaSerie (N : Integer) : Real;
Var I : Integer;
S : Real;
Begin
S := 0;
For I := 1 To N Do
S := S + 1/I;
SomaSerie := S;
End;
Begin
Writeln (SomaSerie(2) :8:6);
Writeln (SomaSerie(5) :8:6);
Writeln (SomaSerie(100):8:6);
End.
A função utiliza duas variáveis, sendo a variável de controle do e a
variável para acumular os termos gerados em cada ciclo de execução. A função
recebe como parâmetro a quantidade de termos que se pretende considerar para a
série. A seguinte tabela ilustra os cinco ciclos de execução da chamada .
I Instrução S
1 S := S + 1/I; S := 0 + 1/1 1
2 S := S + 1/I; S := 1 + 1/2 1.5
3 S := S + 1/I; S := 1.5 + 1/3 1.833333
4 S := S + 1/I; S := 1.8333 + 1/4 2.083333
5 S := S + 1/I; S := 2.0833 + 1/5 2.283333
Introdução à Programação: Conceitos e Práticas. Página 193
A coluna contém os valores da variável de controle em cada ciclo. A coluna
apenas reforça que a cada ciclo a mesma instrução é executada. A
coluna destaca como a variável vai sendo atualizada a cada ciclo pela atribuição do
valor da expressão a direita e acumulando o valor de cada termo. Ao final do
processamento do , a variável conterá o valor .
A saída para o programa é:
1.500000
2.283333
5.187378
ii. Implementar a seguinte somatória para termos:
⋯
Esta série difere da anterior pela alternância dos sinais dos termos. Uma solução
possível é utilizar a instrução com a variável de controle assumindo os valores
{ }, e para cada um destes valores escrever um mecanismo para produzir o
termo e o respectivo sinal, bem como acumular estas parcelas.
Assim, é necessário agora formular uma solução para gerar cada um dos termos, e
soma-los a fim de atender a especificação do problema. Segue a implementação desta
lógica:
Program ExSERIE;
Function SomaSerie (N : Integer) : Real;
Var I : Integer;
S : Real;
Begin
S := 0;
For I := 1 To N Do
If I Mod 2 = 0
Then S := S - 1/I
Else S := S + 1/I;
SomaSerie := S;
End;
Begin
Writeln (SomaSerie(2) :8:6);
Writeln (SomaSerie(5) :8:6);
Writeln (SomaSerie(100):8:6);
End.
Introdução à Programação: Conceitos e Práticas. Página 194
A lógica adotada aproveita o valor de para deduzir o sinal da parcela. Assim, quando
for par o termo deve ser diminuído (negativo) de e quando for impar deve ser somado
(positivo). O teste se é par é feito avaliando o resto da divisão de por .
Uma alternativa a esta lógica se baseia no uso de uma variável adicional para fornecer o
sinal correto a cada ciclo. Segue esta versão:
Function SomaSerie (N : Integer) : Real;
Var I : Integer;
S : Real;
T : Integer;
Begin
S := 0;
T := 1;
For I := 1 To N Do
Begin
S := S + T/I;
T := -T;
End;
SomaSerie := S;
End;
A variável é inicializa com e a cada ciclo a instrução é executada com o
objetivo de alternar o valor de entre e . As parcelas passam a apresentar o
seguinte termo geral , que constrói exatamente os termos com o sinal.
A saída para o programa é:
0.500000
0.783333
0.688172
iii. Implementara seguinte somatória para a quantidade de termos indicada:
⋯
Esta série poderia ser reescrita para ficar explícita a ordem de cada termo, como segue:
⋯
O termo geral desta série tem a seguinte forma:
Introdução à Programação: Conceitos e Práticas. Página 195
Uma solução possível é utilizar a instrução com a variável de controle assumindo
os valores { }, e para cada um destes valores escrever um mecanismo para
produzir o termo geral, bem como acumular estas parcelas. A função
implementada anteriormente pode ser utilizada para compor a expressão que constrói
cada um dos termos.
Program ExSERIE;
function power (base, expoente : real) : real;
begin
power := exp (expoente*ln(base));
end;
Function SomaSerie (X : Real; N : Integer) : Real;
Var I : Integer;
S : Real;
Begin
S := 0;
For I := 0 To N Do
S := S + Power (X, I);
SomaSerie := S;
End;
Begin
Writeln (SomaSerie(2, 2) :8:6);
Writeln (SomaSerie(2, 5) :8:6);
Writeln (SomaSerie(1, 100):8:6);
End.
Neste exemplo, a variável de controle assume valores de a , tendo associação direta
com o expoente do termo geral. A lógica fica reduzida a acumular em o valor de
.
A desvantagem desta solução é que a cada ciclo é calculado integralmente o valor de
, o que torna esta lógica ineficiente. Por exemplo, quando , o termo é
calculado de forma completa, com as nove multiplicações. Porém, no ciclo anterior foi
calculado o valor de , caso fosse aproveitado o esforço computacional feito, bastaria
agregar mais uma multiplicação no ciclo atual para chegar em . Assim, a seguinte
lógica melhora a eficiência para esta série:
Function SomaSerie (X : Real; N : Integer) : Real;
Var I : Integer;
P : Real;
S : Real;
Begin
S := 0;
P := 1;
For I := 0 To N Do
Introdução à Programação: Conceitos e Práticas. Página 196
Begin
S := S + P;
P := P*X;
End;
SomaSerie := S;
End;
Esta solução utiliza dois acumuladores: e . A variável representa cada parcela, que
é calculada acumulando o produto de ao longo dos ciclos. Esta lógica está codificada
na instrução . O cuidado que deve ser tomado ao acumular um produto, é o
de inicializar a variável adequadamente, que neste caso é inicializado com . A variável
acumula a soma das parcelas ( ).
Uma nova variante para a implementação desta série é dada pela seguinte
transformação, para alguns termos:
( ( ( )))
Assim, a seguinte lógica melhora o desempenho para esta série:
Function SomaSerie (X : Real; N : Integer) : Real;
Var I : Integer;
S : Real;
Begin
S := 0;
For I := 0 To N Do
S := S*X + 1;
SomaSerie := S;
End;
Esta solução é a mais eficiente, pois apresenta , e
por ciclo de execução do .
A saída para o programa é:
7.000000
63.000000
101.000000
iv. Implementar uma solução para o cálculo do fatorial de um inteiro:
⋯
Introdução à Programação: Conceitos e Práticas. Página 197
O fatorial de um é o produto de todos os inteiros entre e . A proposta é utilizar a
instrução com a variável de controle produzindo os valores { }, e para
cada um destes valores utilizar a técnica de acumular o produto de em uma variável. O
seguinte código implementa o cálculo do fatorial.
Program ExFATORIAL;
Function Fat (N : Integer) : LongInt;
Var F : LongInt;
I : Integer;
Begin
F := 1;
For I := 1 To N Do
F := F*I;
Fat := F;
End;
Var K : Integer;
Begin
For K := 0 To 30 Do
Writeln (K:3, ' ', Fat(K));
End.
Porém há um problema crítico, que reside no fato que o cálculo do fatorial produz um
rápido crescimento dos valores, uma vez que há um acúmulo de multiplicação. A função
retorna um resultado do tipo , que tem de comprimento e admite
uma faixa de a . Este tamanho em bits para o resultado
limita ao valor , a partir da qual há um estouro de capacidade. A solução é utilizar
um tipo com faixa maior. Assim, a função foi testada para os tipos e . As
seguintes saídas foram obtidas para cada um destes tipos:
LongInt Int64 Real
0 1
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
11 39916800
12 479001600
13 1932053504
14 1278945280
15 2004310016
16 2004189184
17 -288522240
18 -898433024
19 109641728
0 1
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
11 39916800
12 479001600
13 6227020800
14 87178291200
15 1307674368000
16 20922789888000
17 355687428096000
18 6402373705728000
19 121645100408832000
0 1
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
11 39916800
12 479001600
13 6227020800
14 87178291200
15 1307674368000
16 20922789888000
17 355687428096000
18 6402373705728000
19 121645100408832000
Introdução à Programação: Conceitos e Práticas. Página 198
20 -2102132736
21 -1195114496
22 -522715136
...
30 1409286144
20 2432902008176640000
21 -4249290049419214848
22 -1250660718674968576
...
30 -8764578968847253504
20 2432902008176640000
21 51090942171709400000
22 1124000727777610000000
...
30 265252859812191000000000000000000
31 8222838654177920000000000000000000
32 263130836933694000000000000000000000
33 8.68331761881189E+036
34 2.95232799039604E+038
35 1.03331479663861E+040
...
97 9.61927596824821E+151
98 9.42689044888324E+153
99 9.33262154439441E+155
100 9.33262154439441E+157
A linha sublinhada indica o valor de a partir da qual há um estouro de capacidade.
Assim, a opção é pela versão abaixo com o uso do tipo para o retorno da função:
Function Fat (N : Integer) : Real;
Var F : Real;
I : Integer;
Begin
F := 1;
For I := 1 To N Do
F := F*I;
Fat := F;
End;
O programador deve observar no manual do compilador quais são os tipos de dados
mais adequados entre os vários da família .
A listagem para o tipo também chama atenção para o fato de que a partir de certo
valor perde-se a precisão, pois a quantidade de dígitos significativos é bastante
limitada, mesmo que a faixa de representação admita números de alta magnitude. Para
o tipo escolhido neste exemplo a capacidade é de dígitos significativos, e
fica evidente na listagem o complemento feito com zeros a partir de igual a .
A recomendação é sempre evitar algoritmos que acumulem extensos produtos que
podem levar ao estouro de capacidade. A indicação é buscar mecanismos que
aproveitam os cálculos de ciclos anteriores, e observar se há possibilidade de priorizar
operações na expressão que contribua para manter os resultados intermediários dentro
de limites seguros. Isto pode ser obtido em algumas situações priorizando divisões antes
das multiplicações. Por exemplo, na seguinte expressão:
Na situação em que os valores de , , e estejam próximos ao limite do tipo
definido, é praticamente certo que os produtos levarão ao estouro de capacidade e
consequentemente a um erro de lógica ou de execução. Esta mesma expressão pode
ser reescrita para avaliar primeiro as divisões:
Introdução à Programação: Conceitos e Práticas. Página 199
v. Refazer o programa que calcula o seno de um ângulo através da série:
⋯
A lógica do já codificada foi limitada aos primeiros seis termos e se resumiu a
transcrição da série em uma expressão aritmética com a soma explícita de todas as seis
parcelas. Agora, com o recurso da repetição podemos calcular o seno com melhor
precisão. A primeira versão será a mais básica e intuitiva, pois utilizará epara gerar os termos.
Program ExSENO;
function power (base, expoente : Extended) : Extended;
begin
If base <> 0
Then power := exp (expoente*ln(base))
Else power := 0;
end;
Function Reduzir (X : Extended) : Extended;
Begin
Reduzir := Frac(X/(2*PI))*(2*PI);
End;
Function GrausToRad (G : Extended) : Extended;
Begin
GrausToRad := G*PI/180;
End;
Function Fat (N : Integer) : Extended;
Var F : Extended;
I : Integer;
Begin
F := 1;
For I := 1 To N Do
F := F*I;
Fat := F;
End;
Function MySin (X : Extended) : Extended;
Var S : Extended;
T, I : Integer;
Begin
X := Reduzir (X);
Introdução à Programação: Conceitos e Práticas. Página 200
T := 1;
S := 0;
For I := 1 To 15 Do
Begin
S := S + T*Power (X, 2*I-1)/Fat(2*I-1);
T := -T;
End;
MySin := S;
End;
Var Graus, Rad, Y1 : Extended; Y2 : Extended;
I : Integer;
Begin
For I := 0 To 24 Do
Begin
Graus := I*15;
Rad := GrausToRad (Graus);
Y1 := Sin (Rad);
Y2 := MySin (Rad);
Writeln (I:4, ' ', Graus:4:0, Y1:21:20, Y2:21:20, Y1-Y2:21:20);
End;
End.
A função inclui uma repetição para o cálculo de termos, onde o termo geral
tem a seguinte forma:
A menos da obtenção do sinal, a expressão que codifica cada termo tem exatamente
este formato. O sinal foi gerado a partir da variável que alterna entre e a cada
ciclo. Isto evita utilizar apenas para obter e .
O testa a função em comparação à função interna do
, e apresenta os valores dos senos e a diferença entre estes valores. O de
vezes ( a ) serve para gerar os ângulos de a de em graus, através da
expressão .
A saída obtida foi:
I X SIN(X) MYSIN(X) ERRO
0 0 0.00000000000000000 0.00000000000000000 0.00000000000000000
1 15 0.25881904510252076 0.25881904510252076 0.00000000000000000
2 30 0.50000000000000000 0.50000000000000000 0.00000000000000000
3 45 0.70710678118654752 0.70710678118654752 0.00000000000000000
4 60 0.86602540378443865 0.86602540378443865 0.00000000000000000
5 75 0.96592582628906829 0.96592582628906829 0.00000000000000000
6 90 1.00000000000000000 1.00000000000000000 0.00000000000000000
7 105 0.96592582628906829 0.96592582628906829 -0.00000000000000000
Introdução à Programação: Conceitos e Práticas. Página 201
8 120 0.86602540378443865 0.86602540378443865 0.00000000000000000
9 135 0.70710678118654752 0.70710678118654752 0.00000000000000000
10 150 0.50000000000000000 0.50000000000000000 -0.00000000000000000
11 165 0.25881904510252076 0.25881904510252076 -0.00000000000000000
12 180 -0.00000000000000000 0.00000000000000000 -0.00000000000000000
13 195 -0.25881904510252076 -0.25881904510252076 -0.00000000000000001
14 210 -0.50000000000000000 -0.49999999999999997 -0.00000000000000003
15 225 -0.70710678118654752 -0.70710678118654721 -0.00000000000000031
16 240 -0.86602540378443865 -0.86602540378443635 -0.00000000000000230
17 255 -0.96592582628906829 -0.96592582628905322 -0.00000000000001506
18 270 -1.00000000000000000 -0.99999999999991161 -0.00000000000008839
19 285 -0.96592582628906829 -0.96592582628859701 -0.00000000000047127
20 300 -0.86602540378443865 -0.86602540378213313 -0.00000000000230552
21 315 -0.70710678118654752 -0.70710678117611210 -0.00000000001043543
22 330 -0.50000000000000000 -0.49999999995598198 -0.00000000004401802
23 345 -0.25881904510252076 -0.25881904492839897 -0.00000000017412179
24 360 0.00000000000000000 0.00000000000000000 0.00000000000000000
A diferença em relação a função da biblioteca aumenta a medida que o ângulo se
aproxima de .
A lógica implementada é ineficiente, pois não aproveita os cálculos feitos no ciclo
anterior, o que aumenta consideravelmente a quantidade de multiplicações e divisões
para obter o resultado final. Este mesmo resultado pode ser obtido usando uma
onde um termo atual pode ser definido em função do termo
calculado anteriormente. Observa-se nas equações abaixo, que dois termos
consecutivos possuem uma relação entre eles cujo estilo se conserva ao longo da série.
O termo atual é o termo anterior multiplicado por e dividido pelo produto de dois
números, que também podem ser generalizados em função da posição do termo na
série. Assim, temos a seguinte relação de recorrência:
Onde:
Assim, a seguinte solução é preferida em relação a anterior que utiliza e :
Program ExSENO;
Function Reduzir (X : Extended) : Extended;
𝑥 𝑥
𝑥
𝑥
𝑥
𝑥
⋯
𝑥
𝑥
𝑥
𝑥
Introdução à Programação: Conceitos e Práticas. Página 202
{igual a anterior}
Function GrausToRad (G : Extended) : Extended;
{igual a anterior}
Function MySin (X : Extended) : Extended;
Var P, S : Extended;
I : Integer;
Begin
X := Reduzir (X);
S := 0;
P := X;
For I := 1 To 15 Do
Begin
S := S + P;
P := -P*X*X/((2*I)*(2*I+1));
End;
MySin := S;
End;
Var Graus, Rad, Y1 : Extended; Y2 : Extended;
I : Integer;
Begin
{igual ao programa principal anterior}
End.
A saída obtida foi:
I X SIN(X) MYSIN(X) ERRO
0 0 0.00000000000000000 0.00000000000000000 0.00000000000000000
1 15 0.25881904510252076 0.25881904510252076 0.00000000000000000
2 30 0.50000000000000000 0.50000000000000000 0.00000000000000000
3 45 0.70710678118654752 0.70710678118654752 0.00000000000000000
4 60 0.86602540378443865 0.86602540378443865 0.00000000000000000
5 75 0.96592582628906829 0.96592582628906829 0.00000000000000000
6 90 1.00000000000000000 1.00000000000000000 0.00000000000000000
7 105 0.96592582628906829 0.96592582628906829 0.00000000000000000
8 120 0.86602540378443865 0.86602540378443865 0.00000000000000000
9 135 0.70710678118654752 0.70710678118654752 0.00000000000000000
10 150 0.50000000000000000 0.50000000000000000 0.00000000000000000
11 165 0.25881904510252076 0.25881904510252076 -0.00000000000000000
12 180 -0.00000000000000000 0.00000000000000000 -0.00000000000000000
13 195 -0.25881904510252076 -0.25881904510252076 -0.00000000000000000
14 210 -0.50000000000000000 -0.49999999999999996 -0.00000000000000004
15 225 -0.70710678118654752 -0.70710678118654721 -0.00000000000000031
16 240 -0.86602540378443865 -0.86602540378443634 -0.00000000000000230
17 255 -0.96592582628906829 -0.96592582628905323 -0.00000000000001506
18 270 -1.00000000000000000 -0.99999999999991161 -0.00000000000008839
19 285 -0.96592582628906829 -0.96592582628859700 -0.00000000000047128
20 300 -0.86602540378443865 -0.86602540378213311 -0.00000000000230554
Introdução à Programação: Conceitos e Práticas. Página 203
21 315 -0.70710678118654752 -0.70710678117611212 -0.00000000001043541
22 330 -0.50000000000000000 -0.49999999995598203 -0.00000000004401797
23 345 -0.25881904510252076 -0.25881904492839899 -0.00000000017412177
24 360 0.00000000000000000 0.00000000000000000 0.00000000000000000
Além da sensível redução na quantidade de cálculos esta solução apresentou um erro
levemente menor em relação a função interna quando comparado ao erro apresentado
pela lógica anterior. Mais adiante, no tópico seguinte, será apresentada uma solução
que reduz ainda mais o erro.
vi. Elaborar um programa que leia vários números fornecidos via teclado e apresente ao
final a somatória destes valores. Considere este esboço de interface:
A lógica deste problema é bastante simples e consiste em umloop onde deve ser lido
um dado e acumulado em uma variável. Ao encerrar a repetição o resultado é impresso.
Segue a solução proposta:
Program ExLERDADOS;
Procedure LerDados (N : Integer);
Var I : Integer;
X, S : Real;
Begin
S := 0;
Writeln ('Entre com os dados');
For I := 1 To N Do
Begin
Readln (X);
S := S + X;
End;
Writeln ('SOMA = ', S:7:2);
End;
Begin
LerDados (10);
End.
Introdução à Programação: Conceitos e Práticas. Página 204
A seguinte figura ilustra uma saída real:
Entre com os dados
10
23
54
98
102
75
98
17
34.5
56.25
SOMA = 567.75
vii. Elaborar um programa semelhante ao anterior, porém o usuário pode interromper a
entrada de dados fornecendo o valor .
A lógica do problema anterior é adaptada para encerrar o loop antes do seu final,
testando pelo valor de .
O recurso da linguagem que permite encerrar o e desviar o processamento para a
próxima instrução após o fim do é a instrução . Normalmente o vem
combinado com um teste de condição, que em caso positivo desvia o fluxo para fora do
. O seguinte diagrama ilustra visualmente a instrução inserida em um .
Figura 64: Instrução BREAK.
Segue a solução proposta:
Program ExLERDADOS;
Introdução à Programação: Conceitos e Práticas. Página 205
Procedure LerDados (N : Integer);
Var I : Integer;
X, S : Real;
Begin
S := 0;
Writeln ('Entre com os dados');
For I := 1 To N Do
Begin
Readln (X);
If X = -1
Then Break;
S := S + X;
End;
Writeln ('SOMA = ', S:7:2);
End;
Begin
LerDados (10);
End.
A seguinte figura ilustra uma saída real:
Entre com os dados
10
20
30
40
10.5
20.5
30.5
-1
SOMA = 161.50
viii. Elaborar um programa semelhante ao anterior, porém fornecendo ao final a soma e a
média aritmética dos valores fornecidos.
O cálculo da média é feito dividindo a soma dos dados pela quantidade de dados
fornecidos. A primeira impressão poderia levar a uma solução utilizando o valor do ,
variável de controle do loop, acreditando que ela reterá a quantidade de termos. Porém
esta não é uma boa prática de programação, pois não há garantias para o uso da
variável de controle fora do . Ao encerrar a repetição, esta variável não contém um
valor confiável. Assim, a solução demanda que dentro do loop resolvamos esta questão.
Segue a solução proposta:
Program ExLERDADOS;
Procedure LerDados (N : Integer);
Var I, T : Integer;
Introdução à Programação: Conceitos e Práticas. Página 206
X, S, M : Real;
Begin
S := 0; T:= N;
Writeln ('Entre com os dados');
For I := 1 To N Do
Begin
Readln (X);
If X = -1 Then
Begin
T := I-1;
Break;
End;
S := S + X;
End;
M := S/T;
Writeln ('SOMA = ', S:7:2);
Writeln ('MEDIA = ', M:7:2);
End;
Begin
LerDados (10);
End.
Observe que antes de sair do com o , vem a instrução que
armazena em o número de termos dado por . Se o término do ocorrer
naturalmente, sem o , a variável permanecerá com o valor inicial , indicando
que foram lidos dados. A seguinte figura ilustra uma saída real:
Entre com os dados
10
20
30
10.5
20.5
30.5
-1
SOMA = 121.50
MEDIA = 20.25
ix. Elaborar um programa semelhante ao anterior, porém fornecendo ao final a soma, a
média aritmética e o maior valor dentre os valores fornecidos.
Deve ser agregada ao programa anterior a lógica para obter o maior entre os números
fornecidos. A estratégia consiste em inicializar uma variável com um valor pequeno, e
dentro do loop atualizar esta variável toda vez que um valor fornecido ( ) for superior ao
atual maior. Segue a lógica proposta:
Program ExLERDADOS;
Introdução à Programação: Conceitos e Práticas. Página 207
Procedure LerDados (N : Integer);
Var I, T : Integer;
Maior, X, S, M : Real;
Begin
S := 0; T:= N;
Writeln ('Entre com os dados');
Maior := 0;
For I := 1 To N Do
Begin
Readln (X);
If X = -1 Then
Begin
T := I-1;
Break;
End;
If X > Maior
Then Maior := X;
S := S + X;
End;
M := S/T;
Writeln ('SOMA = ', S:7:2);
Writeln ('MEDIA = ', M:7:2);
Writeln ('MAIOR = ', Maior:7:2);
End;
Begin
LerDados (10);
End.
No código acima foram destacadas as linhas que tratam a lógica do maior:
Inicializa a variável que conterá o maior valor:
Maior := 0;
Para cada valor fornecido, verificar se o mesmo é maior que o maior atual, e em
caso positivo faça a substituição:
If X > Maior
Then Maior := X;
Introdução à Programação: Conceitos e Práticas. Página 208
A seguinte figura ilustra uma saída real:
Entre com os dados
20
30
10
30.5
20.5
10.5
-1
SOMA = 121.50
MEDIA = 20.25
MAIOR = 30.50
Esta lógica tem um problema quando os valores fornecidos forem todos negativos.
Neste cenário, o programa informará que o maior número é zero. Uma melhoria na
estratégia seria inicializar a variável com o primeiro número informado, pois assim
fica assegurada a consistência da lógica.
Program ExLERDADOS;
Procedure LerDados (N : Integer);
Var I, T : Integer;
Maior, X, S, M : Real;
Begin
S := 0; T := N;
Writeln ('Entre com os dados');
Readln (X);
Maior := X;
For I := 1 To N Do
Begin
If X = -1 Then
Begin
T := I-1;
Break;
End;
If X > Maior
Then Maior := X;
S := S + X;
If I < N
Then Readln (X);
End;
M := S/T;
Writeln ('SOMA = ', S:7:2);
Writeln ('MEDIA = ', M:7:2);
Writeln ('MAIOR = ', Maior:7:2);
End;
Introdução à Programação: Conceitos e Práticas. Página 209
Begin
LerDados (10);
End.
A alteração introduzida para melhorar a lógica do consiste em efetuar uma leitura de
dados antes de iniciar a repetição, e deslocar a leitura interna à repetição para o final do .
Com isto fica resolvido o problema da inicialização da variável . Foi adicionado um teste
para evitar a leitura, já que uma leitura válida foi
realizada antes de iniciar a repetição.
20.4. Repetição – WHILE⋯DO
A estrutura de controle do tipo repetição controlada por expressão lógica é essencial
em uma linguagem de programação do paradigma imperativo ou procedural, da qual
e fazem parte. O objetivo desta construção de controle é prover um
mecanismo onde uma ou mais instruções são executadas zero ou mais vezes
dependendo do valor de uma expressão lógica. O ciclo é repetido enquanto o valor da
expressão lógica for .
20.4.1. Sintaxe e Semântica
A sintaxe da instrução de repetição tipo é:
Onde é a condição que determina o controle da repetição.
A seguinte figura ilustra os componentes da instrução , bem como destaca que a
dinâmica da estrutura inclui vários ciclos de execução da instrução controlada, onde o
fim da repetição ocorre quanto a expressão lógica assume o valor .
O seguinte diagrama lógico permite visualizar o fluxo do processamento:
Introdução à Programação: Conceitos e Práticas. Página 210
O valor da expressão lógica do é testado. Caso seja o fluxo do
processamento segue para as instruções controladas, retornando ao final para um novo
teste do valor da expressão lógica. O loop é encerrado quando o valor da expressão
lógica for .
Introdução à Programação: Conceitos e Práticas. Página 211
20.4.2. Exemplos
i. Refazer a lógica da leitura dos dados e cálculo da soma, da média aritmética e do maior
valor dentre os valores fornecidos, porém utilizando a repetição controlada por
expressão lógica. A única forma de encerrar a entrada de dados é através do dado .
A utilizaçãoda instrução irá simplificar a última implementação feita com o ,
pois deverá dispensar o . A permanência no loop será definida pela condição
. Segue a lógica proposta:
Program ExLERDADOS;
Procedure LerDados;
Var T : Integer;
Maior, X, S, M : Real;
Begin
S := 0;
T := 0;
Writeln ('Entre com os dados');
Readln (X);
Maior := X;
While X <> -1 Do
Begin
Inc (T);
If X > Maior
Then Maior := X;
S := S + X;
Readln (X);
End;
M := S/T;
Writeln ('SOMA = ', S:7:2);
Writeln ('MEDIA = ', M:7:2);
Writeln ('MAIOR = ', Maior:7:2);
End;
Begin
LerDados;
End.
A procedure foi alterada, pois o parâmetro que indica a quantidade de dados a
serem lidos não é mais necessária. O final da repetição se dá unicamente pela entrada
do valor . A lógica tem as seguintes características:
São inicializadas as variáveis e . Não há mais contador, assim a variável
armazenará a quantidade de dados válidos digitados. É útil para o cálculo da
média;
É feita uma leitura antes da repetição, que servirá de inicialização para a variável
;
Introdução à Programação: Conceitos e Práticas. Página 212
Dentro do a variável é incrementada de a cada entrada válida.
A saída de dados na tela é semelhante aos programas anteriores.
ii. Elaborar um programa que converta um número inteiro decimal em uma
contendo o equivalente .
A seguinte figura ilustra o processo de conversão da base decimal para a binária. São
feitas divisões sucessivas do número que se deseja converter pela base destino. Os
restos destas divisões fornecem os dígitos binários, e o processo se encerra quando o
quociente alcançar zero:
Figura 65: Conversão Decimal - Binário
Segue a lógica proposta:
Program ExBASE;
Function DecToBin (X : Integer) : String;
Var S : String;
Begin
S := '';
While X <> 0 Do
Begin
If X Mod 2 = 0
Then S := '0' + S
Else S := '1' + S;
X := X Div 2;
End;
DecToBin := S;
End;
Begin
Writeln (DecToBin(15));
Writeln (DecToBin(67));
Introdução à Programação: Conceitos e Práticas. Página 213
Writeln (DecToBin(1030));
Writeln (DecToBin(255));
End.
A função codifica a lógica de conversão. A que conterá o binário
convertido é inicializado com vazio ( )11. A repetição se encerra quando for igual
a zero, e este é atualizado ao final do com o novo quociente. Antes, porém, o
resto da divisão de por é utilizado para decidir se deve ser adicionado o caractere
ou a esquerda de a fim de montar o resultado corretamente.
A saída para o programa é:
1111
1000011
10000000110
11111111
iii. Elaborar um programa que converta um número inteiro decimal em uma
contendo o equivalente .
A lógica é semelhante ao do problema anterior bastando substituir o divisor pela base
desejada. Segue a lógica proposta:
Program ExBASE;
Function DecToHexDig (X : Integer) : Char;
Begin
Case X Of
0..9 : DecToHexDig := Chr(Ord('0') + X);
10..15 : DecToHexDig := Chr(Ord('A') + X - 10);
Else
DecToHexDig := 'X';
End;
End;
Function DecToHex (X : Integer) : String;
Var S : String;
Begin
S := '';
While X <> 0 Do
Begin
S := DecToHexDig (X Mod 16) + S;
X := X Div 16;
End;
DecToHex := S;
11
Observar que são dois caracteres ' (apóstrofe) seguidos sem espaço intermediário.
Introdução à Programação: Conceitos e Práticas. Página 214
End;
Begin
Writeln (DecToHex(15));
Writeln (DecToHex(67));
Writeln (DecToHex(1030));
Writeln (DecToHex(255));
End.
A principal modificação em relação à conversão para binário, é no trecho que obtém o
caractere da base desejada. Na conversão para base , resolveu-se o problema com um
simples , em função do binário ter dois símbolos. A conversão para hexadecimal
utiliza a função já codificada anteriormente que fornece um digito
hexadecimal a partir do valor decimal de a .
A saída para o programa é:
F
43
406
FF
iv. Elaborar um programa que complete uma string com brancos a esquerda a fim de
ajustar o tamanho do texto para uma quantidade estabelecida de caracteres. Faça o
mesmo para complemento a direita.
Segue a lógica proposta:
Program ExPAD;
Function PadLeft (S : String; N : Integer) : String;
Begin
While Length(S) < N Do
S := ' ' + S;
PadLeft := S;
End;
Function PadRight (S : String; N : Integer) : String;
Begin
While Length(S) < N Do
S := S + ' ';
PadRight := S;
End;
Begin
Writeln ('[', PadLeft ('Ola Mundo', 30),']');
Writeln ('[', PadRight ('Ola Mundo', 30),']');
Introdução à Programação: Conceitos e Práticas. Página 215
End.
A lógica principal para a função consiste em concatenar o caractere espaço em
branco a esquerda da até que ela fique com o comprimento definido. A função
recebe a inicial e o comprimento desejado. A repetição é encerrada
quando o comprimento do novo alcançar , e em cada ciclo o comprimento de
é incrementado pela concatenação de um caractere a esquerda. A mesma lógica é
utilizada para a função . O programa principal imprime a envolvida com
os símbolos [ e ] para melhor visualização dos efeitos das funções.
A saída para o programa é:
[ Ola Mundo]
[Ola Mundo ]
v. Elaborar um programa que calcule a raiz quadrada (√
) de um número pelo método de
.
O método de calcula a raiz quadrada de um através de um método
iterativo (repetitivo) onde cada ciclo calcula um novo valor a partir do valor do ciclo
anterior, convergindo em direção ao resultado desejado. A fórmula de recorrência é
dada por:
(
)
A repetição deve ser encerrada quando a diferença entre o valor atual e o anterior for
inferior a um determinado . Normalmente o erro estabelecido é o menor número
possível de ser representado na arquitetura em que o programa está rodando. Este erro
pode ser obtido por uma função que executa divisões sucessivas a partir de até
alcançar o , sendo que o valor anterior ao é o menor número representável
naquela arquitetura. A solução proposta é dada por:
Program ExSQRT;
Function GetEPS : Extended;
Var EPS, X : Extended;
Begin
X := 1;
While X <> 0 Do
Begin
EPS := X;
X := X/2;
End;
GetEPS := EPS;
Introdução à Programação: Conceitos e Práticas. Página 216
End;
Function Raizq (X : Extended) : Extended;
Var EPS, R, Rant : Extended;
begin
If X >= 0 Then
Begin
RAnt := 0; R := 1;
EPS := GetEPS;
While ABS (R - RAnt) >= EPS Do
Begin
RAnt := R;
R := (Rant + X / Rant) / 2;
End;
End Else R := 0;
Raizq := R;
End;
Eegin
Writeln (Raizq(2));
Writeln (Sqrt (2));
End.
Na função , antes de caminhar para o cálculo da raiz, o valor de é testado para
evitar o processamento de número negativo. A lógica principal da função é semelhante a
fórmula apresentada:
Os valores das variáveis que representam o valor da raiz no ciclo atual ( ) e o
valor do ciclo anterior ( ) são inicializados com valores distintos para que o
erro ( ) seja diferente de .
RAnt := 0; R := 1;
A variável que estabelece qual a precisão requerida para o cálculo é inicializada
com o ) da máquina, que é o menor número real antes de chegar ao
zero.
EPS := GetEPS;
O principal determina que seja calculada uma nova raiz a cada ciclo
a diferença entre o valor do ciclo atual e o do ciclo anterior seja
superior à precisão estabelecida. A é muito semelhante
à expressão codificada.
WhileABS (R - RAnt) >= EPS Do
Begin
RAnt := R;
Introdução à Programação: Conceitos e Práticas. Página 217
R := (Rant + X / Rant) / 2;
(
)
End;
A saída para o programa é:
1.4142135623730950E+0000
1.4142135623730950E+0000
Os valores calculados pela função implementada e pela função interna do são
idênticos.
vi. Elaborar um programa que calcule a raiz (√
) de um número pelo método de
.
O método de calcula a raiz de um através de um método
iterativo (repetitivo), similar ao método da raiz quadrada. A fórmula de recorrência é
dada por:
(
)
Segue a solução proposta:
Program ExRAIZN;
Function GetEPS : Extended;
Var EPS, X : Extended;
Begin
X := 1;
While X <> 0 Do
Begin
EPS := X;
X := X/2;
End;
GetEPS := EPS;
End;
Function PowerInt (X : Extended; T : Integer) : Extended;
Var P : Extended; I : Integer;
Begin
P := 1;
For I := 1 To T Do
P := P*X;
PowerInt := P;
End;
Introdução à Programação: Conceitos e Práticas. Página 218
Function RaizN (X : Extended; N : Integer) : Extended;
var EPS, R, Rant : Extended;
begin
If X >= 0 Then
Begin
RAnt := 0; R := 1;
EPS := GetEPS;
while ABS (R - RAnt) >= EPS do
begin
RAnt := R;
R := ((N-1)*Rant + X / PowerInt(Rant, N-1)) / N;
End;
End Else R := 0;
RaizN := R;
end;
Var Y : Extended;
begin
Y := RaizN (25, 3);
Writeln (Y);
Writeln (PowerInt(Y, 3));
end.
A utiliza um cálculo do tipo na parte
. Como o
expoente é um número inteiro positivo, preferiu-se implementar uma versão da função
exclusivamente para este caso ( ), evitando o uso das funções e .
A saída para o programa é:
2.9240177382128661E+0000
2.5000000000000000E+0001
No programa principal, o teste foi feito calculando os valores de √
e de . Os
valores apresentados indicam que o cálculo foi realizado com boa precisão.
A função pode ser implementada de outra forma, tal como a que segue,
baseada no exemplo do (I), publicado pelo prof. Niklaus Wirth.
Function PowerInt (X : Extended; T : Integer) : Extended;
Var W, Z : Extended; I : Integer;
Begin
W := X;
Z := 1;
i := T;
While I <> 0 Do
Introdução à Programação: Conceitos e Práticas. Página 219
Begin
If Odd (i)
Then Z := Z*W;
I := I Div 2;
W := W*W;
End;
PowerInt := Z;
End;
vii. Refazer o programa que calcula o seno de um ângulo a fim de que não tenha diferença
com a função interna do :
Para zerar o erro em relação a função interna do , faremos uma transformação no
cálculo do seno. A série será mais rapidamente convergente nos casos que ,
e de preferência com o ângulo mais perto de zero. Assim, faremos algumas
transformações para reduzir o ângulo a um menor valor equivalente. Segue a
transformação proposta:
( )
(
) (
) (
)
(
)√ (
) (
)
A equação indica que se calcularmos o podemos chegar ao pela última
expressão, ou seja:
(√ )
Onde:
(
)
Segue a solução proposta, cujos detalhes ficam a cargo do leitor analisar:
Program ExSENO;
Function Reduzir (X : Extended) : Extended;
Begin
Reduzir := Frac(X/(2*PI))*(2*PI);
End;
Function GrausToRad (G : Extended) : Extended;
Introdução à Programação: Conceitos e Práticas. Página 220
Begin
GrausToRad := G*PI/180;
End;
Function MySinx (X : Extended) : Extended;
Var P, S, SAnt : Extended;
I : Integer;
Begin
S := 0;
SAnt := X;
P := X;
I := 2;
While S <> SAnt Do
Begin
SAnt := S;
S := S + P;
P := -P*X*X/(I*(I+1));
I := I + 2;
End;
MySinx := S;
End;
Function MySin (X : Extended) : Extended;
Var S : Extended;
Begin
X := Reduzir(X)/4;
S := MySinx(X);
S := 4*S*Sqrt(1-S*S)*(1-2*S*S);
MySin := S;
End;
Var Graus, Rad, Y1 : Extended; Y2 : Extended;
I : Integer;
Begin
For I := 0 To 24 Do
Begin
Graus := I*15;
Rad := GrausToRad (Graus);
Y1 := Sin (Rad);
Y2 := MySin (Rad);
Writeln (I:4, ' ', Graus:4:0, Y1:21:20, Y2:21:20, Y1-Y2:21:20);
End;
End.
A função que calcula o seno de um ângulo implementa uma lógica indireta, onde
primeiro o ângulo é reduzido ao primeiro quadrante ( ). Na sequência
o seno desejado é obtido levando este valor de para a expressão que relaciona
e , conforme indica o código abaixo:
Introdução à Programação: Conceitos e Práticas. Página 221
A lógica do módulo principal, que calcula o valor da série, foi alterada para utilizar o
, sendo o critério de parada a situação em que dois cálculos sucessivos não
acrescentam precisão alguma ao resultado.
Observar na saída obtida que o erro foi zerado.
I X SIN(X) MYSIN(X) ERRO
0 0 0.00000000000000000 0.00000000000000000 0.00000000000000000
1 15 0.25881904510252076 0.25881904510252076 0.00000000000000000
2 30 0.50000000000000000 0.50000000000000000 -0.00000000000000000
3 45 0.70710678118654752 0.70710678118654752 0.00000000000000000
4 60 0.86602540378443865 0.86602540378443865 -0.00000000000000000
5 75 0.96592582628906829 0.96592582628906829 -0.00000000000000000
6 90 1.00000000000000000 1.00000000000000000 0.00000000000000000
7 105 0.96592582628906829 0.96592582628906829 0.00000000000000000
8 120 0.86602540378443865 0.86602540378443865 0.00000000000000000
9 135 0.70710678118654752 0.70710678118654752 -0.00000000000000000
10 150 0.50000000000000000 0.50000000000000000 0.00000000000000000
11 165 0.25881904510252076 0.25881904510252076 0.00000000000000000
12 180 -0.00000000000000000 0.00000000000000000 -0.00000000000000000
13 195 -0.25881904510252076 -0.25881904510252076 -0.00000000000000000
14 210 -0.50000000000000000 -0.50000000000000000 0.00000000000000000
15 225 -0.70710678118654752 -0.70710678118654752 -0.00000000000000000
16 240 -0.86602540378443865 -0.86602540378443865 -0.00000000000000000
17 255 -0.96592582628906829 -0.96592582628906829 0.00000000000000000
18 270 -1.00000000000000000 -1.00000000000000000 0.00000000000000000
19 285 -0.96592582628906829 -0.96592582628906829 -0.00000000000000000
20 300 -0.86602540378443865 -0.86602540378443865 0.00000000000000000
21 315 -0.70710678118654752 -0.70710678118654753 0.00000000000000000
22 330 -0.50000000000000000 -0.50000000000000000 0.00000000000000000
23 345 -0.25881904510252076 -0.25881904510252076 -0.00000000000000000
24 360 0.00000000000000000 0.00000000000000000 0.00000000000000000
viii. Elaborar um programa que formate uma . O filtro para a formatação é a
eliminação dos caracteres espaço em branco duplicados em sequência. Qualquer
sequência de mais de um espaço em branco deverá ser substituída por apenas um
caractere.
A solução proposta utiliza a função para localizar a primeira ocorrência de dois
espaços em branco. Uma vez encontrada é devolvida uma string com apenas um destes
dois caracteres. O processo deve se repetir até não haver mais espaços em branco em
sequência. Segue o código proposto:
Program ExBRANCO;
Function Erase (S : String; Ini, N : Integer) : String;
Begin
Introdução à Programação: Conceitos e Práticas. Página 222
Erase := Copy (S, 1, Ini-1) + Copy (S, Ini+N, 255);
End;
Function Repete (Ch : Char; N : Integer) : String;
Var S : String;
Begin
S := '';
While N> 0 Do
Begin
S := S + Ch;
Dec (N);
End;
Repete := S;
End;
Function DelSpace (S : String) : String;
Var P : Integer;
Begin
P := Pos (' ', S);
While P <> 0 Do
Begin
S := Erase (S, P, 1);
P := Pos (' ', S);
End;
DelSpace := S;
End;
Var S, Sx : String;
Begin
S := 'OLA MUNDO BELO ';
Sx := Repete ('X', Length(S));
Writeln ('[', Sx,']');
Writeln ('[', S, ']');
Writeln ('[', DelSpace (S),']');
End.
A saída para o programa é:
[XXXXXXXXXXXXXXXXXXXXX]
[OLA MUNDO BELO ]
[OLA MUNDO BELO ]
Nesta saída, o primeiro texto impresso é uma série de caracteres repetidos apenas para
dar uma referência para a contagem de caracteres nas linhas que seguem. A
com caracteres repetidos foi gerada a partir da função .
Introdução à Programação: Conceitos e Práticas. Página 223
A lógica da função utiliza uma estrutura com . Uma é
inicializada com vazio e em cada repetição é concatenado em S um caractere
, perfazendo ao final uma com caracteres iguais a .
A função faz a filtragem da , onde a lógica é localizar uma sequência
de dois caracteres branco e atualizar para que receba a mesma
porém eliminado um dos caracteres. A função , ilustrada abaixo, codifica a
lógica que dada uma , uma posição e uma quantidade , é gerada uma
sem os caracteres a partir da posição . A lógica utilizada concatena os textos a
esquerda e a direita de um dos espaços em branco.
20.5. Repetição – REPEAT⋯UNTIL
O objetivo da construção de controle do tipo é prover um mecanismo
onde uma ou mais instruções são executadas uma ou mais vezes dependendo do valor
de uma expressão lógica. O ciclo é repetido até que o valor da expressão lógica seja
.
20.5.1. Sintaxe e Semântica
A sintaxe da instrução de repetição tipo é:
Onde é a condição que determina o controle da repetição.
A seguinte figura ilustra os componentes da instrução , bem como destaca que
a dinâmica da estrutura inclui vários ciclos de execução da instrução controlada, onde o
fim da repetição ocorre quanto a expressão lógica assume o valor .
S x x .. x z z ... z z z
1 2 3 ..
N
Copy (S, 1, Ini-1) Copy (S, Ini+N, 255)
Erase x x .. x z z ... z z z
Ini
Introdução à Programação: Conceitos e Práticas. Página 224
As instruções controladas são executas uma vez. Em seguida o valor da expressão
lógica do é testado. Caso seja o fluxo do processamento é desviado
para o início das instruções controladas. O loop é encerrado quando o valor da
expressão lógica for .
20.5.2. Exemplos
i. Refazer a lógica da leitura dos dados e cálculo da soma e a média, sendo que o usuário
digitará o texto para indicar que deseja finalizar a entrada de dados.
Neste exemplo, a entrada de dados não pode ser armazenada em uma variável
numérica, pois o usuário digitará o texto para informar que não deseja prosseguir
com a entrada de dados. Assim, a linha a ser digitada pelo usuário deverá ser
armazenada em uma variável do tipo . Desta forma, o programa aceitará qualquer
texto como entrada sem provocar erro de execução. Porém, para efetuar o acúmulo dos
valores fornecidos, será necessário converter o texto de entrada para uma variável
numérica. O acúmulo deste valor só é feito após certificar que o conteúdo do texto
fornecido é compatível com um dado numérico. Segue a lógica proposta:
Program ExRUNTIL;
Function IsNumber (L : String) : Boolean;
Var X : Real; Code : Integer;
Begin
Val (L, X, Code);
IsNumber := Code = 0;
End;
Introdução à Programação: Conceitos e Práticas. Página 225
Function Str2Real (L : String) : Real;
Var X : Real; Code : Integer;
Begin
Val (L, X, Code);
Str2Real := X;
End;
Procedure LerDados;
Var T : Integer;
M, S : Real;
Linha : String;
Begin
S := 0;
T := 0;
Writeln ('Entre com os dados e digite ''fim'' para encerrar');
Repeat
Readln (Linha);
If IsNumber (Linha) Then
Begin
S := S + Str2Real (Linha);
Inc (T);
End;
Until Linha = 'fim';
If T <> 0 Then
Begin
M := S/T;
Writeln ('SOMA = ', S:7:2);
Writeln ('MEDIA = ', M:7:2);
End Else
Writeln ('Não há dados disponíveis');
End;
Begin
LerDados;
End.
As funções e auxiliam na construção da lógica principal. A função
testa se o conteúdo de uma satisfaz as regras de formação de um
número real. A função recebe uma string como entrada e retorna como
resultado o equivalente numérico. Assim, a lógica principal se apresenta de forma
bastante legível e de fácil compreensão.
Introdução à Programação: Conceitos e Práticas. Página 226
Uma saída exemplo para o programa é:
Entre com os dados e digite 'fim' para encerrar
10.5
20.5
10.5
fim
SOMA = 41.50
MEDIA = 13.83
ii. Escrever um programa que faz a leitura de várias linhas que representam uma redação
qualquer, e informa a quantidade de vogais contidas em todo o texto. O usuário digitará
para indicar que deseja finalizar a entrada de dados. Segue a lógica proposta:
Program ExREDACAO;
Function IsVogal (Ch : Char) : Boolean;
Begin
Case UpCase(Ch) Of
'A', 'E', 'I', 'O', 'U' : IsVogal := True;
Else
IsVogal := False;
End;
End;
Function ContaVogal (L : String) : Integer;
Var I, C : Integer;
Begin
C := 0;
For I := 1 To Length (L) Do
C := C + Ord (IsVogal(L[I]));
ContaVogal := C;
End;
Procedure LerDados;
Var
V : Integer; Linha : String;
Begin
V := 0;
Writeln ('Entre com os textos e digite ''fim'' para encerrar');
Repeat
Readln (Linha);
If Linha <> 'fim'Then
V := V + ContaVogal (Linha);
Until Linha = 'fim';
Writeln ('VOGAIS = ', V);
End;
Introdução à Programação: Conceitos e Práticas. Página 227
Begin
LerDados;
End.
As funções e auxiliam na construção da lógica principal. A função
testa se um é um dos caracteres ASCII que representam vogais. A função
recebe uma string como entrada e retorna como resultado a quantidade de
caracteres que são vogais.
A função é implementada incluindo uma lógica de contagem. A variável de
contagem é inicializada com zero . A estrutura principal é uma repetição por
uma quantidade de vezes que é dada pelo número de caracteres da fornecida
. A instrução repetida se resume em incrementar a variável
de contagem de ou . O valor ou é obtido convertendo o resultado do
teste de cada caractere da string [ ] para número utilizando a função
. Assim, a lógica principal se apresenta de forma bastante legível e de fácil
compreensão.
Uma saída exemplo para o programa é:
Entre com os textos e digite 'fim' para encerrar
OLA mundo
BELO
fim
VOGAIS = 6
Introdução à Programação: Conceitos e Práticas. Página 228
21. Array
Os tipos de dados vistos até o momento são de natureza escalar, ou seja, permitem
definir variáveis com capacidade para representar apenas um elemento. Uma variável
do tipo tem capacidade para armazenar apenas um número em determinado
instante. O conteúdo pode ser alterado, porém sempre será apenas um elemento. O
mesmo acontece com os demais tipos escalares: , , e . O tipo
antecipou o conceito de , e é um tipo de dado que permite representar um
agregado de elementos escalares, no caso uma sequência de .
Uma série de problemas práticos conduz a situações onde é fundamental manter
simultaneamente na memória uma coleção de elementos de uma determinada
natureza.Alguns exemplos seriam: nomes de alunos de uma turma, disciplinas de um
curso, notas de uma turma de uma disciplina em um bimestre, notas de uma turma de
uma disciplina em todos os bimestres, relação de professores de um curso,
temperaturas diárias em um ano, cotação da bolsa em cinco anos, vendas diárias de
uma loja, vendas diárias de todas as filiais, medições de grandezas, salários de
empregados de uma empresa, e etc.
Os exemplos citados poderiam ser submetidos a lógicas para obter: média de cada
aluno em uma disciplina ordenados de várias formas, livro de chamada, relação de
professores por curso, disciplina que mais reprova, regressão linear de grandezas
medidas, semana com a menor temperatura média, folha de pagamento, e etc.
Assim, é importante que uma linguagem de programação permita modelar e processar
entidades que representam uma coleção de dados. Em contraste ao tipo escalar, o tipo
de dado voltado para representar simultaneamente vários elementos é denominado tipo
vetorial.
A linguagem Pascal disponibiliza o tipo para definir variáveis capazes de
armazenar uma coleção de elementos. Com este tipo de dado é possível modelar
problemas que precisam dos conceitos de e .
Introdução à Programação: Conceitos e Práticas. Página 229
21.1. Vetor
Um vetor é uma estrutura composta por uma quantidade conhecida de elementos,
sendo todos do mesmo tipo. O vetor também é chamado de agregado homogêneo. A
seguinte figura compara uma variável com uma variável do :
X:
V:
Tabela 1: Comparação Escalar x Vetor
A variável é do tipo escalar (p. ex. ) e a variável é do tipo vetor ou .
Apenas uma célula de memória está disponível para uso através de , enquanto que
através de várias células estão disponíveis para o programa. A variável foi
declarada como , e declarada como
A linguagem é essencialmente escalar, sendo que as expressões refletem esta
característica e operam somente sobre dados escalares12. Assim, as células de um
vetor devem ser refenciadas individualmente, o que demanda que a variável seja
complementada com uma informação que identifique especificamente uma célula. Este
complemento é o , que indica a sua posição no vetor. Por exemplo, a
primeira célula poderia ser [ ], a segunda [ ] e assim sucessivamente. Agora temos
a definição completa do endereço de memória referente a uma célula vinculada ao
vetor. Esta célula individualizada preenche todos requisitos de uma variável escalar, e
também é denominada .
12
O tipo string não é um tipo primitivo do Pascal. Os dialetos Pascal incluem facilidades para manipular strings, que
no detalhe é um array de char.
Introdução à Programação: Conceitos e Práticas. Página 230
21.2. Sintaxe e Semântica
O vetor é um com apenas uma dimensão, também denominado unidimensional.
A sintaxe simplificada para a definição do tipo vetor é:
[ ]
A natureza do é necessariamente ordinal, ou seja, o índice de
um deve ser do tipo 13, , ou 14. Variáveis podem
ser criadas a partir desta definição.
O seguinte exemplo ilustra as várias possibilidades de declaração de uma variável do
tipo . À direita são apresentadas algumas atribuições às células de cada ,
permitindo também compreender o processo de indexação.
Declaração das variáveis Programa Principal
Var
Va : Array[1..100] Of Integer;
Vb : Array[0..499] Of LongInt;
Vc : Array[-10..10] Of Word;
Vx : Array[1..1000] Of Real;
Vt : Array[0..10000] Of Char;
Vq : Array['a'..'z'] Of Real;
Vr : Array['0'..'9'] Of Real;
Vs : Array[False..True] Of Integer;
Vn : Array[Boolean] Of Integer;
Begin
Va[3] := -1;
Vb[35] := 80;
Vc[-5] := $ffff;
Vx[300] := 1.28;
Vt[0] := 'x';
Vq['d'] := 3.26;
Vr['4'] := -1.32e10;
Vs[False] := -43;
Vn[True] := 43;
End.
A seguinte tabela ilustra a quantidade de memória que cada vetor ocupa, bem como
destaca o número de elementos e o tamanho de cada elemento que compõe o .
Declaração
#bytes
do
Elemento
#elementos
#bytes do
array
Va : Array[1..100] Of Integer; 2 100 200
Vb : Array[0..499] Of LongInt; 4 500 2000
Vc : Array[-10..10] Of Word; 2 21 42
Vx : Array[1..1000] Of Real; 8 1000 8000
Vt : Array[0..10000] Of Char; 1 10001 10001
Vq : Array['a'..'z'] Of Real; 8 26 208
13
Inclui as variações dos inteiros.
14
O tipo enumerado será apresentado mais adiante.
Introdução à Programação: Conceitos e Práticas. Página 231
Vr : Array['0'..'9'] Of Real; 8 10 80
Vs : Array[False..True] Of Integer; 2 2 4
Vn : Array[Boolean] Of Integer; 2 2 4
A forma utilizada para especificar o tamanho de um é estabelecendo uma faixa
para o índice. O primeiro elemento da faixa é o índice da primeira célula, e o último
elemento da faixa é o índice para a última célula. As células serão de tamanho
suficiente para acomodar um elemento do tipo base.
Nos exemplos acima, temos que o foi definido com elementos do
tipo , sendo que a primeira célula é índice e a última é índice . O
foi definido com elementos do tipo , sendo que o primeiro é índice
e o último é índice . O índice do é do tipo , sendo que a primeira
célula é identificada pelo e a última pelo , perfazendo ao todo
elementos. Os índices do tipo devem ser interpretados como similares a
índices inteiros equivalentes a posição do caractere na tabela . A seguinte figura
ilustra estes conceitos para a variável :
O índice de um deve ser uma expressão compatível com o tipo utilizado na
definição da dimensão. Seguem alguns exemplos de índices com expressões cujo
resultado é de tipo compatível com a definição do índice.
Var
Va : Array[1..100] Of Integer;
Vq : Array['a'..'z'] Of Real;
Vs : Array[False..True] Of Integer;
Vt : Array[0..10000] Of Char;
I : Integer;
Begin
Introdução à Programação: Conceitos e Práticas. Página 232
Va[I-1] := -125;
Vq[Chr(Ord('a') + I Mod 27)] := -2.16e-32;
Vs[I Mod 2 = 0] := 27;
Vt[3*Va[I]+120] := Chr (Vs[False] mod 45);
End.
A figura a seguir faz a conexão entre a definição da variável do tipo e a linha de
código com o comando de atribuição. Observe que no caso da variável , o índice é
definido como e na expressão de atribuição o acesso a célula é feito
com uma expressão [ ] Ainda em , o tipo de cada elemento é definido
como e o valor atribuído também é . O mesmo ocorre com a variável
, onde o índice é definido como e na expressão de atribuição o acesso
a célula é feito com uma expressão do tipo [ ] para o
índice. Ainda em , o tipo de cada elemento é definido como real e o valor atribuído na
expressão também é . A compatibilidade é mantida para e .
Figura 66: Correspondência entre Definição do Vetor e a Atribuição.
O tipo é de fato um vetor ou de char, que em função da sua importância
em grande parte dos problemas, fez com que a maioria das versões de
trouxessem facilidades embutidas para processamento. A principal facilidade é a
atribuição, ondeuma instrução do tipo faz com que os caracteres
sejam armazenados em sequência a partir da célula [ ].
21.3. Exemplos
i. Reescrever o programa que lê vários números fornecidos via teclado e apresente ao
final algumas estatísticas, tais como: somatória destes valores, média, maior valor, e etc.
A lógica deste problema será alterada em relação às várias versões já implementadas. A
parte de entrada de dados será separada da parte de processamento dos dados. Assim,
um módulo fará a leitura dos dados e os armazenará em um vetor e outros módulos
Introdução à Programação: Conceitos e Práticas. Página 233
farão os cálculos que forem necessários. O programa principal fará a junção destes
módulos para atender ao requisito do problema. Segue a solução proposta:
Program ExLERVETOR;
Type TVReal = Array[1..100] Of Real;
Procedure LerDados (Var V : TVReal; Var N : Integer);
Var X : Real;
Begin
Writeln ('Entre com os dados');
N := 0;
Readln (X);
While X <> -1 Do
Begin
Inc (N);
V[N] := X;
Readln (X);
End;
End;
Function SomaVet (V : TVReal; N : Integer) : Real;
Var I : Integer;
S : Real;
Begin
S := 0;
For I := 1 To N Do
S := S + V[I];
SomaVet := S;
End;
Function MaiorVet (V : TVReal; N : Integer) : Real;
Var I : Integer;
M : Real;
Begin
M := V[1];
For I := 2 To N Do
If V[I] > M
Then M := V[I];
MaiorVet := M;
End;
Var Vx : TVReal;
Nx : Integer;
Soma, Media, Maior : Real;
Begin
Introdução à Programação: Conceitos e Práticas. Página 234
LerDados (Vx, Nx);
Soma := SomaVet (Vx, Nx);
Media := Soma/Nx;
Maior := MaiorVet (Vx, Nx);
Writeln ('SOMA = ', Soma :7:2);
Writeln ('MEDIA = ', Media:7:2);
Writeln ('MAIOR = ', Maior:7:2);
End.
O problema foi modularizado e deu origem a três rotinas: a procedure e as
funções e . O programa principal organiza as chamadas a fim de
atender a especificação do problema. Observe que no programa principal, inicialmente é
invocada a procedure , que interage com o usuário fazendo a aquisição de
dados e armazenando na memória. Em seguida, três linhas de código fazem os cálculos
da , e do . Seguem os comentários de cada módulo:
A procedure está definida através do seguinte cabeçalho:
Procedure LerDados (Var V : TVReal; Var N : Integer);
Esta definição informa que são necessários dois parâmetros passados pelo método por
referência. Isto indica que o trabalho feito pela rotina será devolvido ao programa
chamador através dos parâmetros e . É importante destacar que será prática comum
na manipulação de arrays que além da variável com os elementos, também
acompanhará a variável que indica a quantidade de elementos efetivamente em uso.
Isto ocorre normalmente pelo fato da definição da variável array sempre reservar um
tamanho máximo fixo, e de fato as lógicas trabalham com qualquer quantidade de
elementos até este limite máximo. Esta quantidade deve ser indicada para as rotinas
que processarão o vetor.
Voltando à rotina , temos que o primeiro parâmetro é do tipo e o
segundo é do tipo . Caso não houvesse restrição por parte do , o
cabeçalho seria o seguinte:
Procedure LerDados (Var V : Array[1..100] Of Real; Var N : Integer);
Porém, o requer que o tipo de um parâmetro seja expresso por apenas uma
palavra, e não por uma sentença. Assim, deve ser utilizado um recurso da linguagem,
que permite fazer associação de tipos. É possível criar um novo tipo a partir de outros já
existentes. Deste modo, o programador contorna esta situação utilizando a cláusula
, como em:
Type TVReal = Array[1..100] Of Real;
Com esta definição colocada no topo do programa, podemos então criar variáveis ou
parâmetros do tipo . O uso de também é aceito por vários
compiladores, porém será estimulada a prática de definição de um novo tipo.
Introdução à Programação: Conceitos e Práticas. Página 235
Apresentado e esclarecido este conceito, então a especificação do módulo é
efetuar a leitura de alguns números via teclado e armazená-los no parâmetro . Além
disso, a quantidade de dados lidos será armazenada no parâmetro . Assim, ao invocar
esta procedure, os argumentos deverão ser variáveis ( ) e ao
retornar ao chamador, estes argumentos terão os mesmos conteúdos atribuídos aos
parâmetros.
A lógica interna de consiste em:
Writeln ('Entre com os dados');
N := 0;
Readln (X);
While X <> -1 Do
Begin
Inc (N);
V[N] := X;
Readln (X);
End;
Escrever mensagem na tela
Zerar o contador de elementos
Ler dado e armazenar em X
Loop enquanto valor digitado ≠ -1
Para um X válido
Incrementar o contador de elementos
Armazenar elemento X na célula V[N]
Ler dado e armazenar em X
Voltar para início do loop.
O seguinte diagrama lógico ilustra graficamente o fluxo do processamento:
Vamos supor que o programa está em execução e durante a sessão de execução o
usuário entrou com os seguintes números: e . Nestas condições
apresentamos na sequência o comportamento da memória nos vários ciclos da
repetição:
Introdução à Programação: Conceitos e Práticas. Página 236
a) Incialmente é zero, o usuário fornece o número , a condição do loop é testada, o
processamento entra no bloco , incrementa que passa a conter , armazena o
valor de na célula [ ] e ainda no interior do faz uma nova leitura de dado.
N: 0/1 V: 25 V[1] N: 1/2 V: 25 V[1]
V[1] := X ? V[2] V[2] := X 32 V[2]
? V[3] ? V[3]
X: 25 ? V[4] X: 32 ? V[4]
inc(N) ? V[5] inc(N) ? V[5]
True True
False False
? V[96] ? V[96]
? V[97] ? V[97]
? V[98] ? V[98]
? V[99] ? V[99]
? V[100] ? V[100]
.
.
.
.
.
.
Primeiro Ciclo Segundo Ciclo
X <> -1
fim
X <> -1
fim
X <> -1
fim
N: 2/3 V: 25 V[1] N: 3 V: 25 V[1]
V[3] := X 32 V[2] 32 V[2]
18.2 V[3] 18.2 V[3]
X: 18.2 ? V[4] X: -1 ? V[4]
inc(N) ? V[5] ? V[5]
True True
False False
? V[96] ? V[96]
? V[97] ? V[97]
? V[98] ? V[98]
? V[99] ? V[99]
? V[100] ? V[100]
.
.
.
.
.
.
Terceiro Ciclo Término do Loop
X <> -1
fim
X <> -1
fim
X <> -1
fim
X <> -1
fim
Introdução à Programação: Conceitos e Práticas. Página 237
b) O usuário fornece o número , a condição do loop é testada, o processamento entra no
bloco , incrementa que passa a conter , armazena o valor de na célula
[ ] e ainda no interior do faz uma nova leitura de dado.
c) O usuário fornece o número , a condição do loop é testada, o processamento entra
no bloco , incrementa que passa a conter , armazena o valor de na
célula [ ] e ainda no interior do loop faz uma nova leitura de dado.
d) O usuário fornece o número , a condição do loop é testada e indica o fim da
repetição.
Após o final do loop a variável conterá , indicando a quantidade de elementos
armazenados, e as três primeiras células do vetor conterão os dados fornecidos. O
fluxo de execução retorna para a unidade chamadora, que neste caso é o programa
principal, e avança para a próxima linha de código.
Begin
LerDados (Vx, Nx);
Soma := SomaVet (Vx, Nx);
Media := Soma/Nx;
Maior := MaiorVet (Vx, Nx);
Writeln ('SOMA = ', Soma :7:2);
Writeln ('MEDIA = ', Media:7:2);
Writeln ('MAIOR = ', Maior:7:2);
End.
Chamar LerDados com Vx e Nx
Obter a soma com SomaVet com Vx e Nx
Obter a média
Obter o maior com MaiorVet com Vx e Nx
Mostrar a soma
Mostrar a média
Mostrar o maior
A atribuição inicia com a avaliação da expressão que
contém apenas a chamada a função SomaVet com os argumentos e .
A lógica interna de consiste em:
Function SomaVet (V : TVReal;
N : Integer) : Real;
Var I : Integer;
S : Real;
BeginS := 0;
For I := 1 To N Do
S := S + V[I];
SomaVet := S;
End;
O primeiro parâmetro é um vetor e o segundo
parâmetro é um inteiro que indica a
quantidade de elementos do vetor.
Variáveis para o For e para acumular a soma.
Zerar a soma
Repetir N vezes
Acumula em S o valor de V[I]
Resultado da função em S
Encerrada a chamada de o controle volta para a unidade chamadora que é o
programa principal, que completa o processamento da linha, atribuindo o resultado de
à variável .
Introdução à Programação: Conceitos e Práticas. Página 238
Na sequência é executada a linha que avalia a expressão indicada,
calculando a divisão da soma pelo número de termo, e armazena o resultado em .
A próxima linha é que invoca a função copiando
os argumentos e para os parâmetros desta função. A lógica interna de
consiste em:
Function MaiorVet (V : TVReal;
N : Integer) : Real;
Var I : Integer;
M : Real;
Begin
M := V[1];
For I := 2 To N Do
If V[I] > M
Then M := V[I];
MaiorVet := M;
End;
O primeiro parâmetro é um vetor e o segundo
parâmetro é um inteiro que indica a
quantidade de elementos do vetor.
Variáveis para o For e para armazenar o
maior
Iniciar Maior com o primeiro elemento de V
Repetir para I de 2 até N
Se o elemento V[I] > do que o maior atual
Então faça V[I] ser o maior
Resultado da função em maior
Encerrada a chamada de o controle volta para a unidade chamadora que é o
programa principal, que completa o processamento da linha, atribuindo o resultado de
à variável .
As próximas três linhas:
Writeln ('SOMA = ', Soma :7:2);
Writeln ('MEDIA = ', Media:7:2);
Writeln ('MAIOR = ', Maior:7:2);
escrevem na tela os conteúdos das três variáveis , e . A seguinte
interface de entrada e saída ilustra a interação do programa com o usuário:
Entre com os dados
25
32
18.2
-1
SOMA = 75.20
MEDIA = 25.07
MAIOR = 32.00
Introdução à Programação: Conceitos e Práticas. Página 239
ii. Escrever um programa que calcule o valor de a partir dos coeficientes do polinômio
e de um valor de :
⋯
A sugestão para este problema é estruturar a lógica nos módulos para:
a) Entrada de dados que obtém via teclado os valores de ⋯ e
de e os armazena na memória;
b) Cálculo do valor de a partir de
c) Apresentar o vetor e os resultados na tela.
Segue a solução proposta:
Program ExPOLI;
Type TVReal = Array[1..100] Of Real;
Procedure LerDados (Var V : TVReal; Var N : Integer);
Var X : Real;
Begin
Writeln ('Entre com os dados');
N := 0;
Readln (X);
While X <> -1 Do
Begin
Inc (N);
V[N] := X;
Readln (X);
End;
End;
Procedure MostrarVet (V : TVReal; N : Integer);
Var I : Integer;
Begin
For I := 1 To N Do
Writeln ('V[', I:2,'] = ', V[I]:8:3);
End;
Function Polinomio (V : TVReal; N : Integer; X : Real) : Real;
Var I : Integer;
S : Real;
Begin
S := 0;
For I := N DownTo 1 Do
S := S*X + V[I];
Polinomio := S;
Introdução à Programação: Conceitos e Práticas. Página 240
End;
Var Vx : TVReal;
Nx : Integer;
X, Y : Real;
Begin
LerDados (Vx, Nx);
Write ('Entre com X = '); Readln (X);
Y := Polinomio (Vx, Nx, X);
MostrarVet (Vx, Nx);
Writeln ('P(', X:5:2, ') = ', Y:7:2);
End.
A procedure é a mesma do exercício anterior. A função que calcula o valor de
foi implementada utilizando a seguinte forma, denominada fatoração de :
( ( ( ⋯ )))
A lógica é semelhante ao código aplicado para solucionar os exercícios sobre séries.
Compare as duas lógicas e identifique a pequena alteração feita.
A procedure apresenta na tela os coeficientes do polinômio permitindo ao
usuário inspecionar se foram considerados os dados certos.
O programa principal ordena as chamadas na sequência lógica estabelecida para este
problema, e proporciona a seguinte interface:
Entre com os dados
1
2
3
-1
Entre com X = 3
V[ 1] = 1.000
V[ 2] = 2.000
V[ 3] = 3.000
P( 3.00) = 34.00
iii. Escrever um programa que localiza a posição de um elemento em um vetor.
Problemas de busca são corriqueiros em grande parte dos problemas. Isto faz com que
se estude muito sobre métodos eficientes para este propósito. Neste exercício faremos
implementações básicas, ficando fora do escopo métodos mais elaborados. Segue uma
solução para a busca por um elemento em um vetor não ordenado:
Program ExBUSCA;
Introdução à Programação: Conceitos e Práticas. Página 241
Type TVReal = Array[1..100] Of Real;
Procedure LerDados (Var V : TVReal; Var N : Integer);
{igual aos exemplos anteriores}
End;
{
Function Buscar (V : TVReal; N : Integer; X : Real) : Integer;
Var P, I : Integer;
Begin
P := 0;
For I := 1 To N Do
If V[I] = X Then
Begin
P := I;
Break;
End;
Buscar := P;
End;
Function Buscar (V : TVReal; N : Integer; X : Real) : Integer;
Var I : Integer;
Begin
I := 1;
While (I <= N) And (V[I] <> X) Do
Inc(I);
If I > N
Then Buscar := 0
Else Buscar := I;
End;
}
Function Buscar (V : TVReal; N : Integer; X : Real) : Integer;
Var I : Integer;
Begin
Inc (N);
V[N] := X;
I := 1;
While (V[I] <> X) Do
Inc(I);
If I = N
Then Buscar := 0
Else Buscar := I;
End;
Introdução à Programação: Conceitos e Práticas. Página 242
Var Vx : TVReal; Nx : Integer;
X : Real; P : Integer;
Begin
LerDados (Vx, Nx);
X := 28;
P := Buscar (Vx, Nx, X);
Writeln ('Posicao de ',X:5:1,' = ', P);
End.
Foram incluídas três versões distintas de uma busca linear, onde os elementos do vetor
são percorridos a fim de encontrar a primeira ocorrência do elemento . Caso este
elemento não seja encontrado a função retorna zero. Duas das funções estão como
comentário.
A primeira versão utiliza a seguinte lógica:
P := 0;
For I := 1 To N Do
If V[I] = X Then
Begin
P := I;
Break;
End;
Inicialmente a posição é definida como zero, e parte-se para percorrer todo o vetor em
busca do elemento , que uma vez encontrado á posição é salva, e a repetição é
encerrada.
A segunda versão utiliza a seguinte lógica:
I := 1;
While (I <= N) And (V[I] <> X) Do
Inc(I);
If I > N
Then Buscar := 0
Else Buscar := I;
A estratégia é a mesma da implementação anterior, porém o com o é
substituído pelo . A expressão lógica que controla a repetição indica que o loop
deve ser encerrado assim que encontrar um [ ] ou quando já percorreu todo o
vetor sem nada encontrar . Ao terminar o , o valor de é testado para
saber se o seu valor não extrapolou o tamanho do vetor, que em caso positivo significa
elemento não encontrado.
A terceira versão utiliza a seguinte lógica:
Inc (N);
V[N] := X;
Introdução à Programação: Conceitos e Práticas. Página 243
I := 1;
While (V[I] <> X) Do
Inc(I);
If I = N
Then Buscar := 0
Else Buscar := I;
Esta função visa simplificar a expressão que controla o , fazendo com que fique
apenas um teste. Para que isto seja feito com segurança, observe que as duas primeiras
linhas cuidam de inserir o elemento no final do vetor, e incrementar o comprimento do
vetor em . Assim, há a certeza de que pelo menos um elemento exista no vetor. Caso
a busca indique que o elemento encontrado está na posição do novo , então significa
que no vetor original o não foi encontrado. Destaca-se também que a instrução
controlada pelo é simplemente o incremento de , cujo significado é
simplesmente avançar a busca para o próximo elemento.
Segue uma interface de entradae saída para um caso exemplo:
Entre com os dados
18
32
28
17
-1
Posicao de 28.0 = 3
iv. Escrever um programa que ordene um vetor de números em ordem decrescente:
Segue a proposta de uma lógica bem simples:
Program ExORDENA;
Type TVReal = Array[1..100] Of Real;
Procedure LerDados (Var V : TVReal; Var N : Integer);
Begin
{igual ao exemplo anterior}
End;
Procedure MostrarVet (V : TVReal; N : Integer);
Begin
{igual ao exemplo anterior}
End;
Procedure Trocar (Var A, B : Real);
var T : Real;
Introdução à Programação: Conceitos e Práticas. Página 244
Begin
T := A;
A := B;
B := T;
End;
Function GetPosMaior (V : TVReal; Ini, N : Integer) : Integer;
Var P, I : Integer;
Begin
P := Ini;
For I := Ini+1 To N Do
If V[I] > V[P]
Then P := I;
GetPosMaior := P;
End;
Procedure MaiorFirst (Var V : TVReal; Ini, N : Integer);
Var P, I : Integer;
Begin
P := GetPosMaior (V, Ini, N);
Trocar (V[Ini], V[P]);
End;
Procedure Ordenar (Var V : TVReal; N : Integer);
Var I : Integer;
Begin
For I := 1 To N-1 Do
MaiorFirst (V, I, N);
End;
Var Vx : TVReal;
Nx : Integer;
Begin
LerDados (Vx, Nx);
Ordenar (Vx, Nx);
MostrarVet (Vx, Nx);
End.
O programa principal indica a sequência da lógica mais abstrata, que é:
1. Ler os dados do vetor para a memória;
2. executar a ordenação; e
3. apresentar o resultado na tela.
Os módulos e são os mesmos do exemplo anterior. A procedure
encaminha o vetor e seu tamanho para o processo de ordenação. A
procedure recebe estes argumentos nos parâmetros e . A passagem de
Introdução à Programação: Conceitos e Práticas. Página 245
parâmetro do vetor é por referência, implicando que toda alteração no parâmetro
reflete no argumento .
Procedure Ordenar (Var V : TVReal; N : Integer);
Var I : Integer;
Begin
For I := 1 To N-1 Do
MaiorFirst (V, I, N);
End;
A lógica da procedure é:
1. Trazer o maior elemento entre e para a posição ;
2. Trazer o maior elemento entre e para a posição ;
3. Trazer o maior elemento entre e para a posição ;
4. ...
5. Trazer o maior elemento entre e para a posição .
Ao final desta sequência o vetor estará em ordem decrescente. A forma geral desta
sequência é:
Trazer o maior elemento entre e para a posição , com variando de a
. Esta lógica foi transformada em:
For I := 1 To N-1 Do
MaiorFirst (V, I, N);
A rotina faz o trabalho de identificar a posição do maior elemento entre as
posições e e trazê-lo para a posição do vetor , como indicado no código
abaixo:
Procedure MaiorFirst (Var V : TVReal; Ini, N : Integer);
Var P, I : Integer;
Begin
P := GetPosMaior (V, Ini, N);
Trocar (V[Ini], V[P]);
End;
A abstração está novamente presente nesta rotina, pois trazer para a posição do
vetor o maior elemento entre e , foi codificado em duas linhas, sendo uma que
chama que retorna a posição do maior elemento de entre as posições
e . A segunda linha executa o módulo que os conteúdos das células e .
A função tem o seguinte código:
Function GetPosMaior (V : TVReal; Ini, N : Integer) : Integer;
Var P, I : Integer;
Begin
P := Ini;
Introdução à Programação: Conceitos e Práticas. Página 246
For I := Ini+1 To N Do
If V[I] > V[P]
Then P := I;
GetPosMaior := P;
End;
Neste módulo a lógica utilizada assume que o primeiro elemento do intervalo dado é a
posição do maior elemento, e em seguida o vetor é percorrido do segundo elemento do
intervalo até o seu final, perguntando se o elemento é maior que o maior atual. Em caso
positivo, a posição do novo elemento assume como a posição do maior. Ao final, este
valor é retornado como resultado da função.
A procedure recebe dois parâmetros por referência, ou seja, dois endereços de
memória, e executa a troca destes conteúdos. O conteúdo da primeira variável é
copiado para a segunda e vice-versa. Uma variável auxiliar é utilizada para não haver
perda de conteúdo nas movimentações.
O programa principal ordena as chamadas na sequência lógica estabelecida para este
problema, e proporciona a seguinte interface:
Entre com os dados
6
8
3
9
2
7
-1
V[ 1] = 9.000
V[ 2] = 8.000
V[ 3] = 7.000
V[ 4] = 6.000
V[ 5] = 3.000
V[ 6] = 2.000
A lógica utilizada é chamada de método de ordenação por seleção direta. Outros
métodos estão disponíveis e são objetos de disciplina específica. A título de exemplo,
segue o módulo que ordena pelo método denominado :
Procedure BubbleSort (Var V : TVReal; N : Integer);
Var I, J : Integer;
Begin
For I := 2 To N-1 Do
For J := N DownTo I Do
If V[J-1] < V[J] Then
Trocar (V[J-1], V[J]);
End;
Para o vetor inicializado com os dados do caso exemplo, ilustramos na sequência o
passo a passo de cada um dos cinco ciclos de execução da lógica do módulo ,
até alcançar a configuração final:
Introdução à Programação: Conceitos e Práticas. Página 247
v. Escrever um programa que forneça as seguintes operações sobre um vetor de números:
a. Copiar para os elementos de que se encontram entre as posições e ;
b. Retornar a posição da primeira ocorrência dos elementos do vetor em um
vetor ;
c. Eliminar os elementos do vetor que estejam entre as posições e , inclusive;
A de parte dos elementos de um vetor para um segundo vetor deve considerar os
movimentos de memória ilustrados na seguinte figura:
I: 1 6 V[1] 9 V[1]
8 V[2] I: 2 8 V[2] P: 2
3 V[3] 3 V[3]
9 V[4] P: 4 6 V[4]
2 V[5] 2 V[5]
N: 6 7 V[6] N: 6 7 V[6]
9 V[1] 9 V[1]
8 V[2] 8 V[2]
I: 3 3 V[3] 7 V[3]
6 V[4] I: 4 6 V[4] P: 4
2 V[5] 2 V[5]
N: 6 7 V[6] P: 6 N: 6 3 V[6]
9 V[1] V: 9 V[1]
8 V[2] 8 V[2]
7 V[3] 7 V[3]
6 V[4] 6 V[4]
I: 5 2 V[5] 3 V[5]
N: 6 3 V[6] P: 6 2 V[6]
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Introdução à Programação: Conceitos e Práticas. Página 248
Figura 67: Cópia de Sub-Vetor.
A quantidade de elementos copiada é dada por , e os índices das células do
vetor de origem são diferentes dos índices das células do vetor de destino. Se a
repetição for de a , então os índices envolvidos serão [ ] [ ].
A busca pela de uma sequência de elementos dentro de outra sequência
de elementos pode ser estruturada pela comparação dos primeiros elementos de
e . Não havendo coincidência, então se compara os elementos a partir do segundo
elemento de com os elementos de . Esta comparação será feita
vezes, ou até que uma igualdade seja verificada e a posição armazenada. A seguinte
figura ilustra esta lógica:
Figura 68: Posição de Sub-Vetor.
No exemplo deste diagrama, a quantidade máxima de repetições que seria feita a
comparação entre parte de com seria vezes, pois irá escorregando até que o
último elemento de e de fiquem emparelhados. Logo, serão feitas
comparações de vetores.
Para fazer a comparação dos elementos de cada vetor, a lógica inicia o resultado
com a posição do primeiro elemento do sub-vetor de a ser comparado, e testa
elemento a elemento em busca de alguma diferença. Caso sejam encontrados
elementos distintos então a repetição interna é interrompida, e uma próxima comparação
de sub-vetor será feita em novo ciclo. Caso não haja diferença, então é constatado que
p q
Va: 1 2 3 4 5 6 7 8
Vb: 2 3 4 5
V: 1 2 3 4 5 6 7 8
1° Ciclo: Falha
Vt: 2 3 4 5
V: 1 2 3 4 5 6 7 8
2° Ciclo: Sucesso
Vt: 2 3 4 5
1° Ciclo
2° Ciclo
Introdução à Programação: Conceitos e Práticas. Página 249
foi encontrado um sub-vetor idêntico ao vetor pesquisado, e o ciclo principal é
encerrado.
A terceira lógica trata de os elementos entreas posições e de um vetor .
A seguinte figura ilustra os movimentos necessários. Os elementos que estão a partir da
posição deverão ser copiados para as posições a partir de .
Figura 69: Eliminação de um Sub-Vetor.
Segue a implementação destas lógicas:
Program ExVETOR;
Type TVReal = Array[1..100] Of Real;
Procedure LerDados (Var V : TVReal; Var N : Integer);
Begin
{igual ao exemplo anterior}
End;
Procedure MostrarVet (V : TVReal; N : Integer);
Begin
{igual ao exemplo anterior}
End;
Procedure EliminarVet (Var V : TVReal; Var N : Integer;
p, q : integer);
Var I, D : Integer;
Begin
D := Q-P+1;
For I := Q+1 To N Do
V[I-D] := V[I];
N := N-D;
End;
Procedure CopiarToVet (Va : TVReal; Na : Integer;
p, q : Integer;
p q
V: 1 2 3 4 5 6 7 8 N = 8
V: 1 6 7 8 N = 4
Introdução à Programação: Conceitos e Práticas. Página 250
Var Vb : TVReal; Var Nb : Integer);
Var I : Integer;
Begin
For I := p To q Do
Vb[I-p+1] := Va[I];
Nb := q-p+1;
End;
Function PosVet (Vt : TVReal; Nt : Integer;
V : TVReal; N : Integer) : Integer;
Var P, I, J : Integer;
Begin
P := 0;
For I := 1 To N - Nt + 1 Do
Begin
P := I;
For J := 1 To Nt Do
If V[I+J-1] <> Vt[J] Then
Begin
P := 0;
Break;
End;
If P <> 0
Then Break;
End;
PosVet := P;
End;
Var Vx : TVReal; Nx : Integer;
Vy : TVReal; Ny : Integer;
V : TVReal; N : Integer;
Begin
LerDados (V, N);
CopiarToVet (V, N, 2, 5, Vx, Nx);
MostrarVet (Vx, Nx);
Writeln ('POS = ', PosVet (Vx, Nx, V, N));
EliminarVet (V, N, 2, 5);
MostrarVet (V, N);
End.
A rotina altera o próprio vetor passado como argumento, o que demanda a
passagem de parâmetro por referência.
A rotina copia um vetor origem para um vetor destino, o que determina que
a passagem do vetor destino deva ser necessariamente por referência, pois as
alterações no parâmetro deverá ser refletida no argumento.
Introdução à Programação: Conceitos e Práticas. Página 251
O programa principal ordena as chamadas na sequência lógica estabelecida para este
problema, e proporciona a seguinte interface:
Entre com os dados
1
2
3
4
5
6
7
8
-1
V[ 1] = 2.000
V[ 2] = 3.000
V[ 3] = 4.000
V[ 4] = 5.000
POS = 2
V[ 1] = 1.000
V[ 2] = 6.000
V[ 3] = 7.000
V[ 4] = 8.000
vi. Escrever um programa que conta a quantidade de vezes que cada letra do alfabeto se
repete em uma .
A solução deverá receber uma e produzir como resultado um vetor com as
quantidades de vezes que cada letra aparece repetida nesta . Segue a proposta
de uma solução:
Program ExCONTALETRA;
Type TVInt = Array['A'..'Z'] Of Integer;
Procedure ContarLetra (S : String; Var V : TVInt);
Var I : Integer; C : Char;
Begin
For C := 'A' To 'Z' Do
V[C] := 0;
For I := 1 To Length(S) Do
Begin
C := UpCase(S[I]);
If (C >= 'A') AND (C <= 'Z')
Then Inc (V[C]);
End;
End;
Procedure MostrarVet (V : TVInt);
Var C : Char;
Vetor V
Vetor Vx
Novo Vetor V
Posição de Vx em V
Introdução à Programação: Conceitos e Práticas. Página 252
Begin
For C := 'A' To 'Z' Do
If V[C] <> 0
Then Writeln ('V[''', C,'''] = ', V[C]:4);
End;
Var S : String; Vc : TVInt;
Begin
Readln (S);
ContarLetra (S, Vc);
MostrarVet (Vc);
End.
A rotina implementa a lógica principal. Observar que ao contrário do que foi
dito anteriormente, este caso não demanda a presença de uma variável com a
quantidade de elementos em uso no vetor, uma vez que o vetor utilizado foi definido
com o mesmo tamanho que a quantidade de letras do alfabeto. A seguinte definição de
tipo é a base para o vetor com as contagens:
Type TVInt = Array['A'..'Z'] Of Integer;
O índice do vetor é do tipo char, sendo uma célula associada a cada letra de a .
Recordamos que o Pascal permite que o índice de seja qualquer faixa do tipo
ordinal.
A seguinte lógica foi implementada na rotina :
For C := 'A' To 'Z' Do
V[C] := 0;
For I := 1 To Length(S) Do
Begin
C := UpCase(S[I]);
If (C >= 'A') AND (C <= 'Z')
Then Inc (V[C]);
End;
O primeiro serve para zerar cada uma das células que irão conter as quantidades
de cada letra na .
O segundo , percorre cada caractere da string , perguntando se o seu equivalente
maiúsculo é uma letra de a . Em caso positivo, a célula correspondente àquela
letra é incrementada de .
Introdução à Programação: Conceitos e Práticas. Página 253
Segue a seguinte interface de entrada e saída para um caso:
ola mundo belo, bom dia
V['A'] = 2
V['B'] = 2
V['D'] = 2
V['E'] = 1
V['I'] = 1
V['L'] = 2
V['M'] = 2
V['N'] = 1
V['O'] = 4
V['U'] = 1
Introdução à Programação: Conceitos e Práticas. Página 254
22. Expressões Textuais
As expressões textuais expandem nossa capacidade de processar informações para
além da manipulação de números. A linguagem apresentava nas suas
primeiras versões uma característica bem marcante que era a vocação para codificar
fórmulas matemáticas. Porém, a demanda por programas que inclui a necessidade de
tratamento de textos impulsionou as linguagens no sentido de prover estes recursos.
Desta maneira, as expressões textuais permitem combinar constantes, variáveis,
operadores e funções de tal forma a prover transformações sobre este tipo de dados.
22.1. Tipos Textuais
Os tipos de dados básicos para manipulação de textos disponíveis no são
o e a 15. O tipo ocupa de memória e permite armazenar um
caractere . O tipo ocupa até de memória e permite armazenar
uma sequencia de caracteres .
22.2. Constantes
Observe o seguinte exemplo, onde são apresentadas as declarações de variáveis do
tipo e , bem como as formas de se representar um valor literal, ou
.
As variáveis , , , , e são do tipo e ocupam de memória cada
uma delas. As variáveis , e são do tipo e foram definidas de tal forma a
15
Não disponível no Pascal padrão, mas disponível nas principais implementações.
Introdução à Programação: Conceitos e Práticas. Página 255
ocuparem , e de memória e permitem armazenar textos com até ,
e caracteres. Ao não indicar o tamanho de uma variável , é assumido o
comprimento máximo de caracteres acrescido de caractere para controle da
linguagem totalizando de memória.
De modo semelhante ao que foi feito com as variáveis numéricas, vamos apresentar o
mapa de memória resultante das atribuições feitas. A seguinte tabela apresenta os
endereços de memória para cada uma das variáveis bem como a quantidade de
ocupa por elas.
Program exemplo; Endereço de Memória
Var #bytes Var Decimal Hexa Offset
C1 : Char; 1 @C1 4243456 40C000 1
C2 : Char; 1 @C2 4243457 40C001 1
C3 : Char; 1 @C3 4243458 40C002 1
C4 : Char; 1 @C4 4243459 40C003 1
C5 : Char; 1 @C5 4243460 40C004 1
C6 : Char; 1 @C6 4243461 40C005 1
S1 : String; 256 @S1 4243462 40C006 256
S2 : String[30]; 31 @S2 4243718 40C106 31
S3 : String[80]; 81 @S3 4243749 40C125 81
Total 374 374
A coluna indica a diferença entre os endereços de duas variáveis declaradas em
sequência, permitindo observar se foram utilizados bytes de preenchimento decorrentes
do alinhamento de endereço. Neste caso, tanto o tipo , quanto o tipo não
tem seus endereços iniciais alinhados.
Introdução à Programação:Conceitos e Práticas. Página 256
i. Início do programa, porém nenhuma instrução executada:
End. Var Célula
End. Var Célula
End. Var Célula
1 40C000 C1 $00 25 40C018 $00 298 40C129 $00
2 40C001 C2 $00 26 40C019 $00 299 40C12A $00
3 40C002 C3 $00 263 40C106 S2 $00 300 40C12B $00
4 40C003 C4 $00 264 40C107 $00 301 40C12C $00
5 40C004 C5 $00 265 40C108 $00 302 40C12D $00
6 40C005 C6 $00 266 40C109 $00 303 40C12E $00
7 40C006 S1 $00 267 40C10A $00 304 40C12F $00
8 40C007 $00 268 40C10B $00 305 40C130 $00
9 40C008 $00 269 40C10C $00 306 40C131 $00
10 40C009 $00 270 40C10D $00 307 40C132 $00
11 40C00A $00 271 40C10E $00 308 40C133 $00
12 40C00B $00 272 40C10F $00 309 40C134 $00
13 40C00C $00 273 40C110 $00 310 40C135 $00
14 40C00D $00 274 40C111 $00 311 40C136 $00
15 40C00E $00 275 40C112 $00 312 40C137 $00
16 40C00F $00 276 40C113 $00 313 40C138 $00
17 40C010 $00 277 40C114 $00 314 40C139 $00
18 40C011 $00 278 40C115 $00 315 40C13A $00
19 40C012 $00 279 40C116 $00 316 40C13B $00
20 40C013 $00 280 40C117 $00 317 40C13C $00
21 40C014 $00 294 40C125 S3 $00 318 40C13D $00
22 40C015 $00 295 40C126 $00 319 40C13E $00
23 40C016 $00 296 40C127 $00 320 40C13F $00
24 40C017 $00 297 40C128 $00 321 40C140 $00
Pode ser observado que os estão com valor zero, porém cabe reforçar que esta
pode não ser a ação de outros compiladores, ficando a recomendação para que o
programador inicialize explicitamente as variáveis.
A tabela ilustra apenas a parte inicial do espaço de memória ocupado pelas variáveis
, e a fim de não tornar muito extenso o quadro.
Introdução à Programação: Conceitos e Práticas. Página 257
ii. Todas as variáveis do tipo char inicializadas:
End. Var Célula
End. Var Célula
End. Var Célula
1 40C000 C1 $61 25 40C018 $00 298 40C129 $00
2 40C001 C2 $5B 26 40C019 $00 299 40C12A $00
3 40C002 C3 $A7 263 40C106 S2 $00 300 40C12B $00
4 40C003 C4 $A8 264 40C107 $00 301 40C12C $00
5 40C004 C5 $03 265 40C108 $00 302 40C12D $00
6 40C005 C6 $41 266 40C109 $00 303 40C12E $00
7 40C006 S1 $00 267 40C10A $00 304 40C12F $00
8 40C007 $00 268 40C10B $00 305 40C130 $00
9 40C008 $00 269 40C10C $00 306 40C131 $00
10 40C009 $00 270 40C10D $00 307 40C132 $00
11 40C00A $00 271 40C10E $00 308 40C133 $00
12 40C00B $00 272 40C10F $00 309 40C134 $00
13 40C00C $00 273 40C110 $00 310 40C135 $00
14 40C00D $00 274 40C111 $00 311 40C136 $00
15 40C00E $00 275 40C112 $00 312 40C137 $00
16 40C00F $00 276 40C113 $00 313 40C138 $00
17 40C010 $00 277 40C114 $00 314 40C139 $00
18 40C011 $00 278 40C115 $00 315 40C13A $00
19 40C012 $00 279 40C116 $00 316 40C13B $00
20 40C013 $00 280 40C117 $00 317 40C13C $00
21 40C014 $00 294 40C125 S3 $00 318 40C13D $00
22 40C015 $00 295 40C126 $00 319 40C13E $00
23 40C016 $00 296 40C127 $00 320 40C13F $00
24 40C017 $00 297 40C128 $00 321 40C140 $00
Este segundo quadro permite que seja visualizado o efeito das atribuições realizadas às
variáveis do tipo char.
Instrução Comentário
C1 := 'a'; A célula apresenta o valor hexadecimal $61 (97) que corresponde ao
código ASCII da letra 'a'.
C2 := '['; A célula apresenta o valor hexadecimal $5B (91) que corresponde ao
código ASCII do caractere '['.
C3 := #167; A célula apresenta o valor hexadecimal $A7 (167) que corresponde ao
código ASCII do caractere 'º'.
C4 := #$A8; A célula apresenta o valor hexadecimal $A8 (168) que corresponde ao
código ASCII do caractere '¿'.
C5 := ^C; A célula apresenta o valor hexadecimal $08 (8) que corresponde ao código
Introdução à Programação: Conceitos e Práticas. Página 258
ASCII do caractere '♥'. Equivale a Ctrl+Letra C.
C6 := 'A'; A célula apresenta o valor hexadecimal $41 (65) que corresponde ao
código ASCII da letra 'A'.
Uma constante pode ser escrita de várias formas, sendo que as atribuições
ilustradas apresentam as mais usuais. A forma direta é envolver o caractere desejado
entre apóstrofe. A segunda maneira é indicar o código precedido pelo caractere
Os caracteres com código inferior a podem ser designados pelo caractere
(equivalente a tecla ) seguido por uma letra do alfabeto
iii. Após a execução de todas as instruções, porém antes de encerrar o programa:
End. Var Célula
End. Var Célula
End. Var Célula
1 40C000 C1 $61 25 40C018 $00 298 40C129 $00
2 40C001 C2 $5B 26 40C019 $00 299 40C12A $00
3 40C002 C3 $A7 263 40C106 S2 $00 300 40C12B $00
4 40C003 C4 $A8 264 40C107 $00 301 40C12C $00
5 40C004 C5 $03 265 40C108 $00 302 40C12D $00
6 40C005 C6 $41 266 40C109 $00 303 40C12E $00
7 40C006 S1 $00 267 40C10A $00 304 40C12F $00
8 40C007 $00 268 40C10B $00 305 40C130 $00
9 40C008 $00 269 40C10C $00 306 40C131 $00
10 40C009 $00 270 40C10D $00 307 40C132 $00
11 40C00A $00 271 40C10E $00 308 40C133 $00
12 40C00B $00 272 40C10F $00 309 40C134 $00
13 40C00C $00 273 40C110 $00 310 40C135 $00
14 40C00D $00 274 40C111 $00 311 40C136 $00
15 40C00E $00 275 40C112 $00 312 40C137 $00
16 40C00F $00 276 40C113 $00 313 40C138 $00
17 40C010 $00 277 40C114 $00 314 40C139 $00
18 40C011 $00 278 40C115 $00 315 40C13A $00
19 40C012 $00 279 40C116 $00 316 40C13B $00
20 40C013 $00 280 40C117 $00 317 40C13C $00
21 40C014 $00 294 40C125 S3 $00 318 40C13D $00
22 40C015 $00 295 40C126 $00 319 40C13E $00
23 40C016 $00 296 40C127 $00 320 40C13F $00
24 40C017 $00 297 40C128 $00 321 40C140 $00
Agora temos um quadro completo incluindo a configuração de memória para as
variáveis do tipo . Os detalhes da atribuição , são
Introdução à Programação: Conceitos e Práticas. Página 259
apresentados no mapa a seguir. O primeiro byte está na posição [ ] e o primeiro
caractere na posição [ ]. O primeiro byte está reservado para a linguagem e é
utilizado para armazenar o comprimento da , que é dado pela quantidade de
caracteres atribuídos à variável. Assim, o texto possui dezesseis
caracteres, o que fez surgir na posição [ ] o valor que equivale a em decimal.
As demais posições contém o valor numérico correspondente ao código do
caractere atribuído.
40C01B S1 $10 s1[0]
40C01C $4C s1[1] L
40C01D $69 s1[2] i
40C01E $6E s1[3] n
40C01F $67 s1[4] g
40C020 $75 s1[5] u
40C021 $61 s1[6] a
40C022 $67 s1[7] g
40C023 $65 s1[8] e
40C024 $6D s1[9] m
40C025 $20 s1[10]
40C026 $50 s1[11] P
40C027 $61 s1[12] a
40C028 $73 s1[13] s
40C029 $63 s1[14] c
40C02A $61 s1[15] a
40C02B $6C s1[16] l
40C02C $00 s1[17]
Fica para o leitor analisar os efeitos na memória e na tela das atribuições em e e
seus respectivos .
S2 := 'Ola Mundo'#33;
S3 := 'Linha 1'#10'Linha 2'#10'Linha 3';
22.3. Operadores
O operador disponível para textos é o de concatenação representado pelo símbolo .
São necessários dois operandos do tipo texto, a esquerda e a direita, produzindo como
resultado um texto formado pela junção dos caracteres destes operandos. O seguinte
exemplo ilustra esta operação:
Introdução à Programação: Conceitos e Práticas. Página 260
Var Sa, Sb, Sc : String;
Begin
Sa := 'Ciencia';
Sb := 'Computacao';
Sc := Sa + ' da ' + Sb;
Writeln (Sc);
End.
A variável irá conter . É comum pressupor que a
linguagem adiciona um espaço em branco entre os textos concatenados, o que não é
fato em Pascal, ficando ao programador a incumbência de codificar este caractereseparador, caso seja necessário.
22.4. Funções
A linguagem Pascal disponibiliza algumas funções e procedimentos que podem ser
utilizadas na manipulação de textos. Segue uma descrição resumida de cada uma
delas:
1)
Retorna o comprimento (quantidade de caracteres) de uma . O
comprimento máximo é e uma vazia tem comprimento zero.
Exemplo:
Program ExLENGTH;
Var Sa : String;
N : Integer;
Begin
Writeln ('Entre com uma frase');
Readln (Sa);
N := Length(Sa);
Writeln ('O comprimento deste texto é: ', N);
Writeln ('O ultimo caractere é: ', Sa[N]);
end.
A interface de entrada e saída para o programa é:
Entre com uma frase
Ola mundo belo
O comprimento deste texto é: 14
O ultimo caractere é: o
Introdução à Programação: Conceitos e Práticas. Página 261
2)
Retorna o equivalente maiúsculo de um . No padrão se aplica
somente ao tipo char, enquanto que no é aceito também um
argumento .
Exemplo:
Program ExUPCASE;
Var Sa : String;
Ch : Char;
Begin
Writeln ('Entre com uma frase toda em minuscula');
Readln (Sa);
Ch := UpCase(Sa[1]);
Writeln ('O equivalente maiusculo do primeiro char é: ', Ch);
end.
A interface de entrada e saída para o programa é:
Entre com uma frase toda em minuscula
ola mundo belo
O equivalente maiusculo do primeiro char é: O
3)
Retorna o caractere correspondente a posição na tabela .
Exemplo:
Program ExCHR;
Var I : Integer;
Ch : Char;
Begin
Writeln ('Entre com um número entre 32 e 128');
Readln (I);
Ch := Chr(I);
Writeln ('O caractere ASCII numero ', I, ' é o ''', Ch,'''');
end.
A interface de entrada e saída para o programa é:
Entre com um número entre 32 e 128
76
O caractere ASCII numero 76 é o 'L'
Introdução à Programação: Conceitos e Práticas. Página 262
4)
Retorna o valor inteiro utilizado para representar o ordinal. São considerados
ordinais os tipos , , e . No caso do tipo é
retornada a posição na tabela .
Exemplo:
Program ExORD;
Var B : Integer;
Ch : Char;
Begin
B := Ord('a')- Ord('A');
Writeln ('A distancia entre ''a'' e ''A'' = ', B);
Ch := Chr (Ord('D') + B);
Writeln ('O minúsculo da letra ''D'' = ''', Ch, '''');
Writeln ('False = ', Ord(False));
end.
A interface de entrada e saída para o programa é:
A distancia entre 'a' e 'A' = 32
O minúsculo da letra 'D' = 'd'
False = 0
5)
Retorna a posição inicial de uma string em outra. Caso não esteja
contida em , então zero é retornado.
Substr
m u n d o
S
o l a m u n d o b e l o
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Pos = 5
Exemplo:
Program ExPOS;
Introdução à Programação: Conceitos e Práticas. Página 263
Var B : Integer;
S : String;
Ch : Char;
Begin
Readln (S); // Obter uma string
Ch := UpCase(S[1]); // Ch := maiusculo do primeiro caractere.
B := Pos (Ch, 'AEIOU'); // B := posicao de Ch em 'AEIOU'
Writeln (B); // Imprimirá zero se Ch não for vogal ou
// a posição de Ch na string dada.
end.
A interface de entrada e saída para o programa é:
Ola Mundo Belo
4
6)
Retorna uma parte de uma . O resultado é uma cópia dos
caracteres de a partir da posição .
index = 5
S
o l a m u n d o b e l o
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Count = 4
m u n d
Exemplo:
Program ExCOPY;
Var Sx,
S : String;
Begin
S := 'ola mundo belo';
Sx := Copy (S, 5, 4);
Writeln (Sx); // imprimirá 'mund'
Introdução à Programação: Conceitos e Práticas. Página 264
end.
A interface de entrada e saída para o programa é:
mund
7)
A procedure converte um número para o formato . O primeiro
parâmetro é um valor inteiro ou real e o segundo parâmetro deve ser a variável
que receberá a . Uma especificação de formato pode ser adicionada ao
número.
Exemplo:
Program ExSTR;
Function IntToStr (I : Longint) : String;
Var S : String;
begin
Str (I, S);
IntToStr := S;
end;
Function RealToStr (R : Real) : String;
Var S : String;
begin
Str (R:9:6, S);
RealToStr := S;
end;
Var L : String;
begin
L :='[' + IntToStr(-475) + ']';
Writeln (L);
L :='[' + RealToStr(PI) + ']';
Writeln (L);
end.
A interface de entrada e saída para o programa é:
[-475]
[ 3.141593]
8)
Introdução à Programação: Conceitos e Práticas. Página 265
A procedure converte uma para número. O primeiro parâmetro é a
cujo conteúdo se deseja converter para o seu equivalente numérico. O
segundo parâmetro é o endereço de memória onde será armazenado o valor
convertido. O terceiro parâmetro é o endereço de memória onde será
depositado um número inteiro que indica o da conversão. Caso a
conversão foi bem sucedida, este último parâmetro conterá zero, caso contrário
conterá a posição do primeiro caractere que violou a conversão.
Exemplo:
Program ExVAL;
Var S, Sx : String;
X, P1, P2, C : Integer;
Begin
S := '>>>>>> [368] <<<<<<';
P1 := Pos ('[', S);
P2 := Pos (']', S);
Sx := Copy(S, P1+1, P2-P1-1);
Val (Sx, X, C);
Writeln (X);
End.
Neste exemplo, uma possui em qualquer posição um número escrito
entre colchetes. Utilizando são obtidas as posições ( e ) dos
caracteres [ e ] . Em seguida é utilizada a função para obter uma
( ) contendo somente o número em formato texto ( ). Por fim, a procedure
converte este texto para número, armazena em , e imprime na tela.
A interface de entrada e saída para o programa é:
368
22.5. Exemplos
i. Elaborar um programa que isola a primeira palavra de uma . Considerar que o
texto dado contenha várias palavras separadas por um espaço em branco. Incluir
também a lógica que retorna uma cópia da sem a primeira palavra.
A solução proposta utiliza uma função denominada para obter a primeira palavra de
uma , e a função para retornar o texto sem a primeira palavra.
A lógica da função é dada por:
Function Car (S : String) : String;
Introdução à Programação: Conceitos e Práticas. Página 266
Var P : Integer;
Begin
P := Pos (' ', S);
Car := Copy (S, 1, P-1);
End;
A linha de código computa a posição em da primeira ocorrência do
espaço em branco. A função é utilizada par obter esta informação. O resultado é
armazenado em .
Com o valor de P já é possível obter a primeira palavra, bastando para isto copiar um
pedaço de , iniciando na primeira posição e quantidade de caracteres igual a , tal
como codificado na linha .
A função tem lógica semelhante. Segue implementação completa.
Program ExCAR;
Function Car (S : String) : String;
Var P : Integer;
Begin
P := Pos (' ', S);
Car := Copy (S, 1, P-1);
End;
Function Cdr (S : String) : String;
Var P : Integer;
Begin
P := Pos (' ', S);
Cdr := Copy (S, P+1, 255);
End;
var L : String;
Begin
L := 'Ola Mundo Belo';
Writeln (Car (L));Writeln (Cdr (L));
End.
A interface de entrada e saída para o programa é:
Ola
Mundo Belo
ii. Partindo das funções já codificadas, pede-se: elaborar uma solução que forneça
separadamente a segunda palavra e a terceira palavra de uma . O texto
informado é composto por pelo menos três palavras separadas por um espaço em
branco.
Introdução à Programação: Conceitos e Práticas. Página 267
Program ExCAR2;
Function Car (S : String) : String;
Var P : Integer;
Begin
P := Pos (' ', S);
Car := Copy (S, 1, P-1);
End;
Function Cdr (S : String) : String;
Var P : Integer;
Begin
P := Pos (' ', S);
Cdr := Copy (S, P+1, 255);
End;
Function Car2 (S : String) : String;
Begin
Car2 := car(cdr(S));
End;
Function Car3 (S : String) : String;
Begin
Car3 := car(cdr(cdr(S)));
End;
var L : String;
Begin
L := 'esta frase tem mais de tres palavras';
Writeln (Car2 (L));
Writeln (Car3 (L));
End.
O código do exemplo anterior foi completado com as funções e que retornam
respectivamente a segunda palavra e a terceira palavra de uma . Observar que a lógica
utiliza uma combinação das funções e para obter o processamento desejado. Os
detalhes sobre a posição do espaço em branco e da cópia do texto desejado ficam apenas nas
funções e .
A segunda palavra de uma string pode ser obtida, primeiramente tomando a original sem
sua primeira palavra e desta string isolando a primeira palavra . A
mesma estratégia pode ser empregada para a função , que retorna a terceira palavra.
A interface de entrada e saída para o programa é:
frase
tem
Introdução à Programação: Conceitos e Práticas. Página 268
22.6. Matriz
O tipo utilizado para definir vetores também pode ser utilizado para definir
matrizes. Muitos problemas do mundo real podem ser modelados sob a forma de
matrizes tanto pela conveniência da representação quanto pela necessidade de fazer
uso da álgebra matricial. As linguagens imperativas clássicas não trazem operadores
embutidos para processar matrizes, ficando ao programador a incumbência de codificar
as rotinas com os recursos escalares da linguagem. Algumas linguagens do paradigma
funcional, tal como , disponibilizam uma grande quantidade de operadores
matriciais. A seguinte figura compara tipos , e :
Figura 70: Comparação Escalar, Vetor e Matriz
A variável pode ter seu conteúdo acessado diretamente através do nome da
variável, enquanto que um necessita do nome mais um índice, e a matriz requer
além do nome mais dois índices, um para linha e outro para coluna. Este último caso,
para matrizes de duas dimensões. De forma geral, uma matriz de dimensão demanda
índices para identificar uma célula.
22.7. Sintaxe e Semântica
A sintaxe simplificada para a definição de um array com dimensões é:
[ { }]
1 2 ... J ... ?
X: V: [1] M: 1
[2] 2
[3] 3
X [4] 4
[5] ...
I
...
V[I] [I] ?
M[I,J]
[?]
Introdução à Programação: Conceitos e Práticas. Página 269
A diferença em relação a definição de um vetor está na inclusão de uma lista de
. A notação { } significa zero ou mais repetições do
conteúdo entre chaves. Assim, caso não tenha nenhuma repetição desta parte, o array
fica reduzido a um vetor. Ficam valendo as observações feitas no tópico sobre vetores,
onde a natureza do é necessariamente escalar, ou seja, o índice
de um deve ser do tipo 16, , ou 17. Variáveis
podem ser criadas a partir desta definição.
O seguinte exemplo ilustra as várias possibilidades de declaração de uma variável do
tipo . À direita são apresentadas algumas atribuições às células de cada ,
permitindo também compreender o processo de indexação.
Var
Ma : Array[1..100, 1..30] Of Integer;
Mb : Array[0..499, 1..50] Of LongInt;
Mc : Array[-10..10, -5..5] Of Word;
Mx : Array[1..1000,'a'..'z'] Of Real;
Mt : Array[0..10000, 1..100] Of Char;
Mq : Array['a'..'z', 1..50] Of Real;
Mr : Array['0'..'9', 1..80] Of Real;
Ms : Array[False..True, 'a'..'z'] Of Integer;
Mn : Array[Boolean, 0..99] Of Integer;
Vs : Array[1..100] Of String;
Begin
Ma[3 , 10 ] := -1;
Ma[3][10] := 0;
Mb[35 , 20 ] := 80;
Mc[-5 , 3 ] := $ffff;
Mx[300 , 'd'] := 1.28;
Mt[0 , 50 ] := 'x';
Mq['d' , 30 ] := 3.26;
Mr['4' , 40 ] := -1.32e10;
Ms[False, 'v'] := -43;
Mn[True , 60 ] := 43;
Vs[1] := 'ola mundo belo';
Vs[1,3] := 'A';
Vs[1][3] := 'E';
End
Os exemplos acima são exclusivamente de matrizes bidimensionais, porém sintaxe
semelhante se aplica a arrays multidimensionais. Observar a definição da variável
como vetor de , o que lhe confere também a característica de matriz, uma vez
que cada célula de faz referência a uma , que é um vetor de char. Alguns
16
Inclui as variações dos inteiros.
17
O tipo enumerado será apresentado mais adiante.
Introdução à Programação: Conceitos e Práticas. Página 270
compiladores estenderam a sintaxe do Pascal padrão permitindo a indexação tanto na
forma [ ] ou [ ][ ], sendo esta última notação o estilo utilizado pela linguagem .
Para não gerar problemas de migração de código, é recomendado manter o primeiro
estilo. A exceção se dá no caso de vetor de , onde a segunda forma é a mais
adequada.
Todas as considerações feitas para vetor em relação às expressões válidas para o
índice, também se aplicam para multidimensionais, o que vale dizer, que o
índice de uma dimensão pode ser qualquer expressão compatível com o tipo adotado
na definição da variável.
Fica para o leitor analisar as variáveis declaradas e extrair a quantidade de memória
alocada a cada uma delas.
Observar que as dimensões dos foram definidas através de constantes
numéricas. Em determinadas situações podem surgir lógicas que com frequência fazem
uso do tamanho máximo de uma determinada dimensão. Assim, ao longo do código
surgirão referências a este valor. Por exemplo, um determinado programa define a
dimensão de uma matriz para linhas. Adicionalmente, em vários locais do programa
existem expressões aritméticas e lógicas fixando este valor nos seus cálculos. Porém,
quando por alguma necessidade a quantidade de linhas for redefinida para , o
programador deverá percorrer todo o código em busca deste número para as devidas
readequações.
Nestas circunstâncias, é recomendado o uso da cláusula , onde constantes
podem estar associadas a . A sintaxe da cláusula é:
{ }
Os exemplos que seguem ilustram o uso deste recurso:
CONST
ENTER = #13;
ESCAPE = #27;
SPACE = #32;
MAXCOL = 100;
MAXLIN = 200;
MAXLEN = MAXCOL * MAXLIN;
MSG = 'OLA MUNDO';
Begin
{programa principal}
end.
Introdução à Programação: Conceitos e Práticas. Página 271
O símbolo utilizado para relacionar o identificador com a constante é e não .
22.8. Exemplos
i. Escrever um programa que preencha uma matriz bidimensional com dados lidos via
teclado e apresente estes dados na tela.
Uma matriz bidimensional é um array definido com dois índices. Assim, para percorrer
cada uma das células da matriz, quer seja para armazenar um dado ou obter o dado
armazenado, será demandado do programa, no caso de uma lógica simples, a utilização
de duas repetições aninhadas. A repetição mais interna processa mais rapidamente