Baixe o app para aproveitar ainda mais
Prévia do material em texto
Manual da Linguagem: C Luis Fernando Espinosa Cocian Engenheiro Eletricista Mestre em Engenharia Elétrica Professor de Engenharia Elétrica Universidade Luterana do Brasil 2 No interesse da difusão do conhecimento e da cultura, o autor envidou o máximo esforço para localizar os detentores dos direitos autorais de todo o material utilizado, dispondo-se a possíveis acertos posteriores caso, inadvertidamente, tenha sido omitida a identificação de algum deles. Copyright © 2004 by Luis Fernando Espinosa Cocian 1ª Edição 2004 Capa: Luis Fernando Espinosa Cocian Revisão Técnica: Dr. Eng. Eletricista Valner João Brusamarello Revisão da Língua Portuguesa: Licenciada em Letras Leonice Lesina Espinosa Projeto Gráfico e Editoração: Roseli Menzen Luis Fernando Espinosa Cocian nasceu em Montevidéu, Uruguai, no ano de 1970. Formou-se em Engenharia Elétrica na Universidade Federal do Rio Grande do Sul (UFRGS) em 1992. Recebeu o grau de Mestre em Engenharia na UFRGS em 1995 na área de Instrumentação Eletroeletrônica. Desenvolve as suas atividades profissionais como diretor do curso de Engenharia Elétrica da Ulbra. na cidade de Canoas, e como sócio-diretor da empresa Mater Matris Technae Ltda.. em Porto Alegre. Ministra as disciplinas de Instrumentação e Introdução a Engenharia Elétrica no curso de Engenharia Elétrica da Ulbra. Nos seus momentos livres, gosta de ler sobre os mais variados assuntos, especialmente astronomia, biologia, filosofia e medicina. Dados Internacionais de Catalogação na Publicação (CIP) Bibliotecária Responsável: Ana Lígia Trindade CRB/10-1235 Dados técnicos do livro Fontes: Carmina, Lucida Casual Papel: Offset 75 (miolo) e Supremo 240g (capa) Medidas: 16 cm x 23 cm Impresso na Gráfica da ULBRA Julho /2004 3 Dedicatória A minha querida mãe Belén. 4 Agradecimentos Espero ter tempo suficiente na minha vida para poder agradecer: A minha esposa Leonice pela ajuda constante e pelos dias de trabalho que levou para revisar este texto. Aos meus queridos professores pelo conhecimento transmitido. Aos meus queridos alunos pela chance de aprender com eles. Ao colega Valner João Brusamarello pela grande ajuda na revisão técnica dos manuscritos originais. Ao nosso pequeno “pero” grande time de professores do curso de Engenharia Elétrica da Ulbra: Adriane Parraga, Alexandre Balbinot, Dalton Vidor, Marilia Amaral da Silveira, Miriam Noemi Cáceres Villamayor, Rosa Leamar Dias Blanco, João Carlos Vernetti dos Santos, Marcelo Barbedo, Marilaine de Fraga Santana, Julio Cabrera, Augusto Alexandre Durganti de Matos, Paulo César Cardoso Godoy, Valner João Brusamarello e Milton Zaro, pelo companheirismo e amizade recebidos ao longo destes anos, e que com o seu trabalho me fazem trabalhar menos. Aos nossos queridos desenvolvedores da interface DevC++: Colin Laplace, Mike Berg, e Hongli Lai, da Bloodshed Software; aos desenvolvedores do compilador Mingw: Mumit Khan, Jan Jaap van der Heidjen, Colin Hendrix e também aos demais programadores GNU, que permitiram incluir o compilador no CD anexo sem custos, e que universalizam o acesso às tecnologias e promovem o software livre. Ao nosso prezado Pró-Reitor de Graduação, Nestor Luiz João Beck e à nossa querida diretora do Núcleo de Avaliações Externas, professora Delzimar da Costa Lima, pelo apoio recebido. Aos meus bebês, Linda, Katherine e Marx, pela alegria e carinho. 5 Prefácio A maioria dos livros de programação em linguagem C enfoca os temas dos seus capítulos no estudo básico da linguagem e nas suas palavras-chave, utilizando exemplos simples de processamento da informação. Esses livros têm como público alvo, pessoas que desejam obter uma noção básica da linguagem. Por isso, são um pouco deficientes no que se refere à especificação e planejamento de projetos de software otimizado e ao acesso ao hardware; temas de conhecimento indispensável para engenheiros e técnicos da área eletroeletrônica, e especialmente, para aqueles profissionais que trabalham no desenvolvimento de sistemas de aquisição de dados, controle automático, comunicação de dados e de processamento digital de sinais e em sistemas de tempo real. A lacuna existente de livros de programação que enfatizem o controle do hardware e a otimização dos recursos, motivou a recopilação de ideias e projetos elaborados ao longo de anos nas salas de aula, de forma a repassar essa informação aos técnicos, estudantes e engenheiros que estão iniciando a aprendizagem da linguagem C. Este livro aborda a programação utilizando como hardware o PC IBM compatível com sistema operacional MSDOS® e Windows®, compiladores Dev C++ da Bloodsheed Software (licença pública GNU), TurboC® da Borland Inc.(Inprise Inc.) e Microsoft Visual C++® da Microsoft Inc. Também, são mostrados alguns exemplos utilizando os compiladores Franklin C® (para a família de microcontroladores 8x51) e PCW (CCS para microcontroladores PIC da Microchip®). O leitor não terá problemas de adaptação na utilização de outros sistemas operacionais e compiladores, tais como os utilizados em ambientes Linux (ou Unix). A linguagem de programação C é padronizada, mas a sua utilização pode diferir em alguns pontos quando for utilizada em compiladores para plataformas diferentes de software e hardware. Nesses casos deverão ser estudadas as modificações a serem efetuadas no código fonte gerado por cada compilador. Os exemplos de programação foram escolhidos de forma que estejam adequados ao escopo deste livro. Nos anexos foi colocada a informação referente às portas de comunicação do PC, chips de suporte e outras informações consideradas relevantes. Alguns problemas requerem pequenas montagens em hardware, cuja implementação também é mostrada através de diagramas esquemáticos. O software Dev C++ foi incluído no CDROM que acompanha este livro. Upgrades e novas versões deste software podem ser obtidas, sem custo, no 6 sítio da Bloodshed Software em: www.bloodshed.net Os códigos em formato texto e outras informações úteis podem ser acessados no meu sítio pessoal em www.cocian.synthasite.com/ Os capítulos deste livro foram organizados da seguinte maneira: os capítulos 1 a 4 fornecem uma breve introdução do funcionamento dos computadores digitais programáveis, das linguagens de programação, da codificação da informação e do projeto de sistemas de software; O capítulo 5 oferece um atalho para o início das atividades práticas, de forma simples e objetiva, com a finalidade de que o leitor comece a se familiarizar com a linguagem C e com o uso do compilador da sua escolha; No capítulo 6, inicia um conjunto de informações formais e completo sobre a linguagem C, estendendo-se até o capítulo 16; O capítulo 17 trata sobre as interrupções, apresentando exemplos de utilização, inclusive com aplicações de microcontroladores; O capítulo 18 trata das interfaces paralelas e o 19 do uso e programação das interfaces seriais. O apêndice A comenta os padrões seriais de transmissão mais utilizados, tais como RS232, RS422, RS485 e outros, assim como, a especificação e conexão dos sistemas, blindagem e proteção contra transientes; os apêndices B e C são exemplos de aplicação da linguagem C em microcontroladores Microchip PIC e os da família Intel 8x51; O apêndice D (no CD) mostra um exemplo de comunicação com as portas no sistema Windows®; O apêndice E (no CD) apresentauma tabela dos mais variados tipos de conectores, com a descrição da pinagem correspondente; O apêndice F (no CD) apresenta alguns exemplos de códigos que fazem o uso da porta paralela para controlar conversores AD e DA, teclados, módulos de LCD, geradores PWM, acionamentos de relés, motores de passo e outros programas de cálculo numérico e de manipulação da informação. E finalmente, o apêndice G mostra um exemplo de primeiros passos para trabalhar com os mais variados compiladores propostos neste livro. O CD que acompanha este livro, possui apresentações Powerpoint® dos apêndices B e C, os textos dos apêndices D, E e F, códigos compilados, um programa de gravação para microcontroladores 89C52 elaborado pela Mater Matris Technae (antiga Antrax Technology), diagramas esquemáticos de gravador e alguns outros programas gratuitos que podem auxiliar a aprendizagem, além do Dev C++ que poderá ser utilizado para iniciar as tarefas de estudo. Para iniciar as atividades, propõe-se ao leitor efetuar a leitura dos primeiros quatro capítulos, posteriormente escolher o compilador a ser utilizado durante a aprendizagem, ler e tentar executar as tarefas do anexo G, para o compilador escolhido, e depois disto tudo continuar com a leitura do capítulo 5. Às 7 vezes, costumo falar brincando que “...a programação se aprende pelos dedos e não pelos olhos....”, querendo dizer com isso, que a prática é o componente essencial da aprendizagem nesta área, e que não adianta decorar e entender livros inteiros sem ter compilado, executado e depurado pelo menos uma centena de programas. O nosso lema segue o tradicional ditado que diz “... la práctica hace al maestro...”. Este livro contém informação suficiente para uma disciplina de um semestre, apresentando uma lista de exercícios no final de cada capítulo. Não espero que este seja o melhor livro de programação para engenheiros, mas é uma primeira tentativa de reunir informações sobre os mais diversos assuntos relacionados com a área da engenharia elétrica e de computação. Espero que com os conhecimentos adquiridos neste livro, o leitor possa facilmente extrapolar os conhecimentos de programação adquiridos, para começar os estudos de linguagens mais sofisticadas, tais como C++, assim como as linguagens de “alto nível” projetadas para trabalhar na rede mundial, tais como, Java ou C#. Parte da informação contida neste livro foi adaptada de livros, sítios da internet, de manuais de fabricantes e de arquivos de ajuda dos principais compiladores existentes no mercado. Nesses casos, colocaram-se as referências de origem. Tomou-se especial cuidado para não infringir qualquer direito autoral, tanto de textos, quanto de trechos de código. Por ser a primeira edição deste livro, e devido ao seu vasto conteúdo, peço desculpas pelos erros de edição que venham a acontecer. Os programas de exemplo foram todos testados nos vários compiladores sugeridos. As palavras dos comentários efetuados nos programas exemplo não foram acentuadas de forma proposital, porque alguns compiladores, preparados para o idioma inglês, não suportam caracteres acentuados. Caso tenha algum comentário ou sugestão relacionada a este livro, por favor, envie um e-mail com as suas sugestões para o meu endereço cocian@ig.com.br. Terei prazer em responder. Luis Fernando Espinosa Cocian Porto Alegre, 13 de junho de 2004 8 1. INTRODUCÃO Os tópicos que serão discutidos neste capítulo incluem: Linguagens de programação História da Linguagem C 9 1.1. Por que estudar a linguagem C? A linguagem C tem sido utilizada com sucesso em todos os tipos imagináveis de problemas de programação, desde sistemas operacionais, planilhas de texto, até em sistemas expertos, e hoje em dia, estão disponíveis compiladores eficientes para máquinas de todo tipo de capacidade de processamento, desde as Macintosh da Apple, até os supercomputadores Cray. Basicamente todas as linguagens de programação conseguem os mesmos efeitos, algumas de forma mais eficiente que outras, sempre dependendo do tipo de aplicação para a qual será destinada. A linguagem C de programação tem se tornado muito popular devido à sua versatilidade e ao seu poder. Uma das grandes vantagens da linguagem C é a sua característica de "alto nível" e de "baixo nível" ao mesmo tempo, permitindo o controle total da máquina (hardware e software) por parte do programador, permitindo efetuar ações sem depender do sistema operacional utilizado. A linguagem C é frequentemente denominada de linguagem de programação de “nível médio”. Isso não é no sentido de capacidade de processamento entre as linguagens de alto e as de “baixo nível”, mas sim no sentido da sua capacidade de acessar as funções de “baixo nível”, e ao mesmo tempo, de constituir os blocos de construção para constituir uma linguagem de “alto nível”. A maioria das linguagens de “alto nível”, por exemplo FORTRAN, fornece o necessário para que o programador consiga efetuar o processamento que deseja, já implementado na própria linguagem. As linguagens de “baixo nível” , como o assembly, fornecem somente o acesso às instruções básicas da máquina digital. As linguagens de nível médio, tais como C, provavelmente, não fornecem todos os blocos de construção oferecidos pelas linguagens de “alto nível”, mas, fornecem ao programador todos os blocos de construção necessários para produzir os resultados que são necessários. Alguns dos pontos positivos que tornaram essa linguagem tão popular são: 1. A portabilidade do compilador 2. O conceito de bibliotecas padronizadas 3. A quantidade e variedade de operadores poderosos 4. A sintaxe elegante 5. O fácil acesso ao hardware quando necessário 6. A facilidade com que as aplicações podem ser otimizadas, tanto na codificação, quanto na depuração, pelo uso de rotinas isoladas e encapsuladas. Em algumas aplicações de engenharia, é necessário manter o controle total do hardware através do software para efetuar acionamentos e temporizações precisas em tempo real, basicamente sistemas determinísticos. Uma grande 10 área de atuação da engenharia é na simulação de processos físicos, onde é necessária uma grande otimização dos recursos, tais como, espaço de memória e tempo de processamento. A linguagem C foi projetada para a construção de sistemas operacionais, com o consequente controle do hardware. Em aplicações de engenharia, a linguagem C é utilizada frequentemente para implementar: 1. Software básico. 1. Programas executivos e aplicativos em CLPs[1]. 2. Firmware[2] e software aplicativo em coletores de dados, telefones celulares e outros sistemas dedicados. 3. Controle eletrônico automático em automóveis. 4. Instrumentos inteligentes 5. Gateways[3] de comunicação. 6. Modems[4]. 7. Programadores de FPGAs[5] (alternativa para a linguagem VHDL). 8. Periféricos em geral. 9. Interfaces Homem-Máquina 10. Sistemas operacionais. 11. Drivers[6] de comunicação e de dispositivos. 12. Programas do tipo Vírus e antivírus. 13. Firmware e software em satélites artificiais e veículos espaciais. 14. Processamento digital de sinais 15. Processamento de Imagens 16. Programas de Inteligência Artificial e redes neurais. 17. Modelagem numérica de sistemas físicos para simulação de efeitos dinâmicos em eletromagnetismo, fenômenos de transporte e termodinâmica. A linguagem C é a indicada em sistemas que envolvem software e hardware, e onde se deseja tero controle total da máquina digital. Apesar disso, alguns engenheiros preferem utilizar a linguagem assembly, ainda hoje, por conhecimento limitado da linguagem C ou pela falta de espaço de memória disponível, geralmente devido à manutenção de projetos de hardware antigos ou mal elaborados. A assembly é a melhor linguagem de programação? A resposta é que a eficiência da assembly para sistemas grandes e complexos é muito pobre, além do código não poder ser reutilizado para repetir a aplicação em outros microprocessadores que não o de origem e de ser de difícil depuração. Ante essa resposta, frequentemente, alguns os engenheiros respondem que o código gerado pela assembly é mais rápido e utiliza menos recursos da máquina, o que otimizaria o seu desempenho. A isso, pode ser complementado que a maior rapidez na execução e o menor uso de recursos 11 para efetuar uma tarefa vai depender do programador. A probabilidade de que os programadores de uma empresa que produz compiladores consigam obter o código mais eficiente em C do que o nosso próprio, em assembly, é muito maior, já que eles, sem lugar a dúvidas, gastaram muitas horas e dias procurando gerar o código mais eficiente possível. Isso é comparado com a escolha do tipo de câmbio quando a compra de um veículo: Câmbio manual ou automático? Alguns preferem o manual, dando como justificativa de que é possível a mudança mais rápida das marchas. Mas tem muita gente que não tem a habilidade motora suficiente para efetuar essa tarefa de modo eficiente o tempo inteiro. Obviamente, um piloto profissional efetuará as marchas de forma muito mais rápida do que uma pessoa comum. Nas salas de aula costumo fazer a seguinte analogia: “programar em assembly é como varrer o nosso jardim com uma escova de dentes”. Para se ter uma ideia da importância estratégica na escolha da linguagem, especialmente na programação de firmware para sistemas integrados, imagine que um programa feito em linguagem C, com aproximadamente 500 linhas, considerado de complexidade média, tem como equivalente assembly um outro que se constitui de aproximadamente 5000 linhas, num compilador assembly para 8051, constituindo-se num programa de complexidade elevada. Se compararmos o tempo de desenvolvimento, um programa em linguagem C com 500 linhas, pode ser escrito e depurado em aproximadamente 20 horas de trabalho de engenheiro (R$ 50.00/hora), o que teria um custo fixo de R$1000.00. O mesmo programa em assembly, levará aproximadamente 70 horas, dando um custo final de R$ 3500,00. Mas isso não é o pior, o grande problema está na atualização e na correção do bugs por outras pessoas que não os programadores originais. Nestes casos, o custo e tempo de atualizar um programa em assembly são em torno de dez vezes mais caro, que atualizar um programa em linguagem C. Isso tem ocasionado que em várias aplicações, a solução mais viável foi ignorar o programa anterior e refazer tudo novamente. Nesses casos todo o investimento inicial foi perdido. Já me deparei com a atualização de sistemas de software, escritos em assembly, que tinham somado investimentos de US$300.000,00, que tiveram de ser descartados por um software escrito em linguagem C, mais funcional, rápido, eficiente e mais fácil de atualizar, com investimentos totais de US$20.000,00, e tempo de desenvolvimento da ordem de dez por cento com relação ao projeto anterior. 12 1.2. Quando essa linguagem ficará obsoleta? Como qualquer ferramenta tecnológica, a linguagem C deverá ficar obsoleta algum dia, mas pode-se antecipar que isso não ocorrerá até pelo menos o fim da segunda década dos 2000, devido à quantidade enorme de linhas de código produzidas e que normalmente são reaproveitadas. Por ser uma linguagem extremamente simples, fácil de aprender, clara e objetiva, aplicável à maioria dos problemas de engenharia, essa linguagem provavelmente sobreviverá por mais 30 anos. 13 1.3. Breve história da Linguagem C A linguagem C foi inventada na década de 70. Seu inventor, Dennis Ritchie, implementou-a pela primeira vez, usando um DEC PDP-11 rodando o sistema operacional UNIX. A linguagem C é derivada de outra: a B, criada por Ken Thompson. O histórico a seguir mostra a evolução das linguagens de programação que certamente influenciaram a linguagem C. Algol 60 – Projetado por um comitê internacional. CPL – Combined Programming Language. Desenvolvida em Cambridge e na Universidade de Londres em 1963. BCPL – Basic Combined Programming Language. Desenvolvida em Cambridge por Martin Richards em 1967. B – Desenvolvida por Ken Thompson, nos Laboratórios Bell em 1970, a partir da linguagem BCPL. C – Desenvolvida por Dennis Ritchie, nos Laboratórios Bell em 1972. Aparece também a figura de Brian Kernighan como colaborador. ANSI C – O comitê ANSI (American National Standards Institute) foi reunido com a finalidade de padronizar a linguagem C em 1983. C++ - A linguagem C se torna ponto de concordância entre teóricos do desenvolvimento da teoria de Object Oriented Programming (programação orientada a objetos): surge a linguagem C++ com alternativa para a implementação de grandes sistemas. Essa linguagem consegue interpretar linhas de código escritas em C. A linguagem Algol apareceu alguns anos depois da linguagem Fortran. Esta era bem sofisticada e, sem lugar a dúvidas, influenciou muito o projeto das linguagens de programação que surgiram depois. Seus criadores deram especial atenção à regularidade da sintaxe, estrutura modular e outras características associadas com linguagens estruturadas de “alto nível”. Os criadores do CPL pretendiam fazer baixar, até a realidade de um computador real, os elevados intentos do Algol. Isto tornou a linguagem de difícil aprendizagem e implementação. Desta surge o BCPL como um aperfeiçoamento da CPL. No início da linguagem B, o seu criador Ken Thompson, projetando a linguagem para o sistema UNIX, tenta simplificar a linguagem BCPL. Porém, a linguagem B não ficou bem coesiva, ficando boa somente para o controle do hardware. Logo após de ter surgido a linguagem B, surge uma nova máquina, o PDP- 11. O sistema operacional Unix e o compilador B foram adaptados para essa máquina. A linguagem B começa a ser questionada devido à sua relativa lentidão, por causa do seu desenho interpretativo. Além disso, a linguagem B 14 era orientada a palavra enquanto o PDP-11 era orientado a byte. Por essas razões começou-se a trabalhar numa linguagem sucessora da B. A criação da linguagem C é atribuída a Dennis Ritchie, que restaurou algumas das generalidades perdidas pela BCPL e B. Isso foi conseguido através do hábil uso dos tipos de dados enquanto mantinha a simplicidade e o contato com o computador. 15 1.4. Exercícios 1. Quais são os pontos fortes da linguagem C ? 2. Pesquise sobre a linguagem C++ e verifique se é compatível com a linguagem C. Podemos usar um compilador C++ para compilar programas em C? 3. A linguagem C pode ser utilizada com o sistema operacional Linux? Se a resposta for positiva, pesquisar dois compiladores na internet. 16 2. OS COMPUTADORES Os tópicos que serão discutidos neste capítulo incluem: Os Computadores Os Programas de Computador As linguagens de Programação 17 2.1. O que são os computadores ? Não há como controlar os computadores sem conhecer o que são e como funcionam. Os computadoressão basicamente máquinas que executam tarefas, tais como, cálculos matemáticos e comunicações eletrônicas de informação, sob o controle de um grupo de instruções inserido de antemão, denominado programa. Os programas usualmente residem dentro do computador e são lidos e processados pela eletrônica do sistema que compõe o computador. Os resultados do processamento do programa são enviados a dispositivos eletrônicos de saída, tais como, um monitor de vídeo, uma impressora ou um modem. Essas máquinas são utilizadas para efetuar uma ampla variedade de atividades com confiabilidade, exatidão e velocidade. 18 2.2. Como os computadores funcionam ? A parte física do computador é conhecida como hardware. O hardware do computador inclui: a memória, que armazena tanto os dados quanto as instruções; a unidade central de processamento (CPU[7]) que executa as instruções armazenadas na memória; o barramento[8] que conecta os vários componentes do computador; os dispositivos de entrada, tais como, o mouse ou o teclado, que permitem ao usuário poder comunicar-se com o computador e os dispositivos de saída, tais como, impressoras e monitores de vídeo, que possibilitam a visualização das informações processadas pelo computador. O programa que é executado pelo computador é chamado de software. O software é geralmente projetado para executar alguma tarefa particular, por exemplo, controlar o braço de um robô para a soldagem de um chassi de automóvel ou desenhar um gráfico. 19 2.3. Tipos de Computadores Os computadores podem ser digitais ou analógicos. A palavra digital refere- se aos processos que manipulam números discretos (por exemplo, sistemas binários: 0s e 1s) que podem ser representados por interruptores elétricos que abrem ou fecham (implementados por transistores trabalhando na região de saturação e de corte respectivamente). O termo analógico refere-se a valores numéricos que têm faixa de variação contínua. 0 e 1 são números analógicos, assim como 1.5 ou o valor da constante . Como exemplo, considere uma lâmpada incandescente que produz luz em um momento e não a produz em outro quando manipulado um interruptor (iluminação digital). Se o interruptor for substituído por um dimmer[9], então a iluminação ficará analógica uma vez que a intensidade de luz pode variar continuamente entre os estados de ligada e desligada. Os primeiros computadores eram analógicos, mas devido à sensibilidade a perturbações externas e pelas necessidades de serem sistemas confiáveis, foram substituídos por computadores digitais que trabalham com informação codificada de forma discreta. Estes são mais imunes a interferências externas e internas. A natureza dos sinais utilizados na codificação da informação pode ser na forma de campos elétricos gerados por cargas (circuitos que utilizam transistores como chaves), campos eletromagnéticos (computadores que utilizam fótons), fenômenos eletroquímicos (computadores orgânicos), forças hidráulicas, pneumáticas e outros. 20 2.4. Software Básico e o Sistema Operacional Quando um computador é ligado, a primeira coisa que ele faz é a procura de instruções armazenadas na sua memória. Usualmente o primeiro grupo de instruções é um programa especial que permite o inicio da operação. Essas instruções mandam o computador executar outro programa especial chamado sistema operacional, que é o software que facilita a utilização da máquina por parte do usuário. Ele faz o computador esperar por instruções do usuário (ou de outras máquinas) por comandos de entrada, relata os resultados destes comandos e outras operações, armazenamento e gerenciamento de dados e controla a sequência das ações do software e do hardware. Quando o usuário requisita a execução de um programa, o sistema operacional o carrega na memória de programa do computador e o instrui para executar o mesmo. Figura 2. Componentes básicos de um sistema computador 2.4.1. A Memória Para processar a informação eletronicamente, os dados são armazenados no computador na forma de dígitos binários, ou bits, cada um tendo duas possíveis representações (0 ou 1 lógicos). Se adicionarmos um segundo bit a unidade única de informação, o número de representações possíveis é dobrado, resultando em quatro possíveis combinações: 00, 01, 10, 11. Um terceiro bit adicionado a esta representação de dois bits duplica novamente o número de combinações, resultando em oito possibilidades: 000, 001, 010, 011, 100, 101, 110, ou 111. Cada vez que um bit é adicionado, o número 21 possível de combinações é duplicado. Um conjunto de 8 bits é chamado de byte. Cada byte possui 256 possíveis combinações de 0 e 1’s. O byte é uma quantidade frequentemente utilizada como unidade de informação porque possibilita a representação do alfabeto ocidental completo, incluindo os símbolos das letras em maiúsculas e minúsculas, dígitos numéricos, sinais de pontuação e alguns símbolos gráficos. Como alternativa, o byte poderá representar simplesmente uma quantidade numérica positiva entre 0 e 255. O computador divide o total da memória disponível em dois grupos lógicos: a memória de programa e a memória de dados. A memória física do computador pode ser do tipo RAM[10] que pode ser tanto lida quanto escrita e ROM (Read Only Memory) que somente pode ser lida. Em geral as memórias RAM são voláteis, isto é, são apagadas quando o sistema é desenergizado. Outros tipos de memórias são: PROMs (OTPs), EPROMs, EEPROMs, Flash-EEPROMs, NVRAM que são não voláteis, isto é, os dados permanecem inalterados mesmo depois de desligar o sistema. A Figura 2-1 mostra o processo de gravação da letra ‘A’ (com código ASCII igual a 41H em hexadecimal, 01000001 em binário ou 65 em decimal) na posição de memória número 0. Este chip de memória hipotético, possui 16 posições de memória de 1 byte cada, endereçáveis por 4 bits (24 = 16 posições possíveis). No processo de escrita, a CPU coloca o dado a ser gravado no barramento de endereços, logo coloca o endereço onde os dados serão armazenados, no barramento de endereços, e finalmente indica ao chip que a operação é de escrita ativando o sinal WR (WRITE). Figura 2-1 – Processo de gravação No processo de leitura, a CPU coloca o endereço da posição de memória que deseja ser lida no barramento de endereços e indica que o processo é de leitura, acionando o pino RD (READ). A memória posteriormente coloca o dado armazenado naquela memória no barramento de dados. Após certo intervalo de tempo, a CPU faz a leitura desses dados. 22 2.4.2. Os Barramentos O barramento (bus) é usualmente um conjunto paralelo de fios que interconecta os vários dispositivos componentes do hardware do sistema, tais como a CPU e a memória, habilitando e gerenciando a comunicação de dados entre estes. Usualmente existem três tipos de barramentos: o barramento de controle que controla o funcionamento dos componentes do sistema; o barramento de endereços, onde é colocada a informação de origem ou destino para a informação a ser enviada ou recuperada; e o barramento de dados, onde trafegam os dados. O número de linhas componentes do barramento limita a capacidade de endereçamento de memória para o computador. Por exemplo, o 8051 possui 16 linhas de endereços no seu barramento externo, e portanto, ele poderá endereçar no máximo 216 = 65536 posições de memória. Já um barramento de 32 bits conseguirá endereçar 232 = 4294967296 posições no máximo. 23 2.5. A Unidade Centralde Processamento - CPU A informação originária de um dispositivo de entrada ou da memória é transferida através do barramento para a CPU, sendo esta a unidade que interpreta os comandos e executa os programas. A CPU é um circuito microprocessador, constituído de uma peça única feita em silício e óxidos contendo milhões de componentes eletrônicos numa área muito pequena. A informação é armazenada em posições de memórias especiais colocadas dentro do microprocessador chamadas de Registradores. Estes são pequenas memórias de armazenamento temporário para instruções ou dados. Enquanto um programa estiver sendo executado, existe um registrador especial que armazena o próximo lugar da memória de programa que corresponde à próxima instrução a ser executada; frequentemente chamado de Contador de Programa[11]. A unidade de controle situada no microprocessador coordena e temporiza as funções da CPU e recupera a próxima instrução da memória a ser executada. Numa sequência típica de operação, a CPU localiza a instrução seguinte no dispositivo apropriado da memória. A instrução é transferida por meio do barramento para um registrador especial de instruções dentro da CPU. Depois disso, o contador de programa é incrementado para se preparar para a próxima instrução. A instrução corrente é analisada pelo decodificador, que determina o que esta determina que deve ser feito. Os dados necessários pela instrução serão recuperados através do barramento e colocados em outros registradores da CPU. A CPU então executa a instrução e o resultado é armazenado também em outros registradores especiais ou copiado para lugares específicos da memória. 24 Figura 2-2 – Processador 8051 FX Intel Uma característica importante das CPUs é o tamanho dos dados (em número de bits) que esta pode manipular com uma única instrução. Assim, temos atualmente, CPUs de 8 bits (ex. PIC16C877, 8051), de 16 bits (ex. 8088, 80286, 80196), 32 bits (80386, 80486, 80586, Pentium) e algumas menos comuns de 64 bits. O tamanho indica, por exemplo, que um processador 80486 consegue somar dois números representados com 32 bits utilizando uma instrução de código de máquina, sendo que a mesma operação deverá ser repartida em várias instruções para ser efetuada em uma CPU com um número de bits de menor. A Figura 2-2 mostra um processador 8051FX da Intel com os blocos componentes da pastilha de circuito integrado. 25 2.6. Programas de Computador Um programa de computador é basicamente uma lista de instruções que este deverá executar. Uma vez que as máquinas digitais só conseguem interpretar informações na forma de sinais elétricos, e que os humanos interpretam na forma de imagens ou sons, é necessária uma ferramenta que implemente uma interface simplificada para a programação. Estas ferramentas são chamadas de linguagens de programação. As linguagens de programação contêm uma série de comandos que formam o software. Em geral, a linguagem que é diretamente codificada em números binários interpretáveis pelo hardware do computador é mais rapidamente entendida e executada. As linguagens que usam palavras e outros comandos que refletem o pensamento lógico humano são mais fáceis de utilizar, mas são mais lentas, já que esta deve ser traduzida antes, para que o computador possa interpretá-la. O software de um computador consiste em programas ou lista de instruções que controla a operação da máquina. O termo pode ser referido a todos os programas utilizados com um computador específico ou simplesmente a um único programa. O software é a informação abstrata armazenada como sinais elétricos na memória do computador, em contraste como os componentes de hardware, tais como, a unidade central de processamento e os dispositivos de entrada e saída. Esses sinais são decodificados pelo hardware e interpretados como instruções, sendo que cada tipo de instrução guia ou controla o hardware por um breve intervalo de tempo. Uma grande variedade de softwares é utilizada num computador. Uma forma fácil de entender esta variedade é pensar em níveis. O nível mais baixo é o que está mais perto do hardware da máquina. O mais alto está mais perto do operador humano. Os humanos raramente interagem com o computador no nível mais baixo, mas o fazem utilizando tradutores chamados Compiladores. Um compilador é um programa de software cujo propósito é converter programas escritos em uma linguagem de “alto nível”, numa de “baixo nível” que possa ser interpretado pelo hardware. Os compiladores eliminam o processo tedioso de conversar com um computador na sua própria linguagem binária. Em uma camada acima do nível mais baixo, poderá existir um software chamado de Sistema Operacional que controla o sistema em si. Ele organiza as funções de hardware, tais como, a leitura e escrita em dispositivos de entrada e saída (teclado, monitor de vídeo, discos magnéticos, etc.), interpretando comandos do usuário e administrando o tempo e os recursos para os programas de aplicação 26 O software de aplicação adapta o computador a um propósito especial, tal como, o processamento e monitoração de variáveis de controle de uma fábrica ou para efetuar a modelagem de uma parte de uma máquina. As aplicações são escritas em qualquer uma das várias linguagens compiladas que forem mais apropriadas para a aplicação específica. Essas linguagens, inventadas pelos seres humanos, não podem ser diretamente entendidas pelo hardware do computador. Elas devem ser traduzidas por um compilador apropriado que as converta em códigos de zeros e uns que a máquina possa processar. O software é usualmente distribuído em discos magnéticos ou ópticos. Quando o disco é lido pela unidade de leitura apropriada, o software será copiado na memória. Então o sistema operacional do computador passa o controle para a aplicação no processo que ativa o programa. Quando o programa é terminado, o sistema operacional reassume o controle da máquina e espera alguma requisição por parte do usuário. 27 2.7. Linguagens de Programação Como foi visto anteriormente, um programa de computador é um conjunto instruções que representam um algoritmo para a resolução de algum problema. Essas instruções são escritas através de um conjunto de códigos (símbolos e palavras). Esse conjunto de códigos possui regras de estruturação lógica e sintática própria. Dizemos que esse conjunto de símbolos e regras forma uma linguagem de programação. 2.7.1. A Linguagem de Máquina Os programas de computador que podem ser executados por um sistema operacional são frequentemente chamados de executáveis. Um programa executável é composto de uma sequência de um grande número de instruções extremamente simples conhecida como código de máquina. Estas instruções são específicas para cada tipo especial de CPU e ao hardware à qual se dedicam (por exemplo, microprocessadores Pentium®, 80486, 8051, PIC16F877, Z80, etc.) e que têm diferentes linguagens de máquina e requerem diferentes grupos de códigos para executar a mesma tarefa. O número de instruções de código de máquina normalmente é pequeno (de 20 a 200 dependendo do computador e da CPU). Instruções típicas são para copiar dados de um lugar da memória e adicionar o conteúdo de dois locais de memória (usualmente registradores internos da CPU). As instruções de código de máquina são informações bináriasnão compreensíveis facilmente por humanos, e por causa disso, as instruções não são usualmente escritas diretamente em código de máquina. 2.7.2. A Linguagem Assembly A linguagem Assembly utiliza comandos que são mais fáceis de entender pelos programadores que a linguagem de máquina. Cada instrução da linguagem de máquina tem um comando equivalente na linguagem assembly. Por exemplo, na linguagem assembly, o comando “MOV A,B” instrui o computador a copiar dados de um lugar para outro. A mesma instrução em linguagem de máquina poderá ser uma cadeia de 8 bits binários ou mais, dependendo do tipo de CPU (por exemplo 0011 1101). Uma vez que o programa em assembly é escrito, deve ser convertido em um programa em linguagem de máquina através de outro programa chamado de Assembler[12]. A linguagem assembly é a mais rápida e poderosa devido a sua correspondência com a linguagem de máquina. É uma linguagem muito difícil de utilizar. Às vezes, instruções em linguagem assembly são inseridas no meio de instruções de “alto nível” para executar tarefas específicas de hardware ou para acelerar a execução de algumas tarefas. 2.7.3. Linguagens de “Alto Nível” 28 As linguagens de “alto nível” foram desenvolvidas devido às dificuldades de programação utilizando linguagens assembly. As linguagens de “alto nível” são mais fáceis de utilizar que as linguagens de máquina e a assembly, devido aos seus comandos que lembram a linguagem natural humana. Ainda que essas linguagens independem da CPU a ser utilizada, elas contêm comandos gerais que trabalham em diferentes CPUs da mesma forma. Por exemplo, um programador escrevendo na linguagem C para mostrar uma saudação num dispositivo de saída (por exemplo, um monitor de vídeo) somente teria que colocar o seguinte comando: printf(“Bom dia, engenheiro !”); Esse comando direcionará a saudação para o dispositivo de saída e funcionará, sem importar que tipo de CPU o computador utiliza. De forma análoga à linguagem assembly, as linguagens de “alto nível” devem ser traduzidas. Para isso, é utilizado um software chamado Compilador. Um compilador transforma um programa escrito numa linguagem de “alto nível” num programa em código de máquina específico. Por exemplo, um programador pode escrever um programa em uma linguagem de “alto nível” tal como C, e então, prepará-lo para ser executado em diferentes máquinas, tais como, um supercomputador Cray Y-MP ou simplesmente um PC, usando compiladores projetados para cada uma dessas máquinas. Essa característica acelera a tarefa de programação e faz o software mais portável para diferentes usuários e máquinas. A oficial naval e matemática americana, Grace Murray Hopper, ajudou a desenvolver a primeira linguagem de software de “alto nível” comercialmente disponível, a FLOW-MATIC, em 1957. É creditada a ela a invenção do termo bug, para indicar que o programa executado pelo computador, apresenta um defeito no funcionamento. Em 1945, ela descobriu uma falha do hardware num computador Mark II, ocasionada por um inseto que ficou preso entre os relés eletromecânicos, componentes do sistema lógico. Na década de 1950 (1954 a 1958), o cientista de computação Jim Backus da International Business Machines, Inc. (IBM) desenvolveu a linguagem FORTRAN (FORmula TRANslation). Esta permanece até hoje, especialmente no mundo científico, como uma linguagem padrão de programação, já que facilitava o processamento de fórmulas matemáticas. Em 1964, foi criada a linguagem BASIC (Beginner’s All-purpose Symbolic Instruction Code), desenvolvida por dois matemáticos: o americano John Kemeny e o húngaro Thomas Kurtz, no Dartmouth College. Essa linguagem era muito fácil de aprender, comparada com as anteriores, e ficou popular devido a sua simplicidade, natureza interativa e a sua inclusão nos computadores pessoais. Diferente de outras linguagens que requerem que todas as suas instruções sejam traduzidas para linguagens de máquina, antes 29 de serem executadas, estas são interpretadas, isto é, são convertidas em linguagem de máquina, linha a linha, enquanto o programa está sendo executado. Os comandos em BASIC tipificam a linguagem de “alto nível” devido a sua simplicidade e semelhança com a linguagem natural humana. Um exemplo de programa que divide um número por dois, pode ser escrito como: 10 INPUT “ENTRE COM O NÚMERO,” X 20 Y=X/2 30 PRINT “A metade do número é ,” Y Outras linguagens de “alto nível”, em uso hoje em dia, incluem C, C++, Ada, Pascal, LISP, Prolog, COBOL, HTML, e Java, entre outras. 2.7.4. Linguagens Orientadas a Objetos As linguagens de programação orientadas a objetos[13] , tais como o C++, são baseadas nas linguagens tradicionais de “alto nível”, mas elas habilitam o programador a pensar em termos de coleções de objetos cooperativos no lugar de uma lista de comandos. Os objetos, tais como um círculo, têm propriedades, tais como o raio do círculo e o comando que o desenha na tela. Classes de objetos podem ter características inerentes de outra classe de objetos. Por exemplo, uma classe que define quadrados pode herdar características, tais como, ângulos retos de uma classe que define os retângulos. Esses grupos de classes de programação simplificam a tarefa de programação, resultando em programas mais eficientes e confiáveis. Figura 2-3 - Exemplo de Fluxograma 2.7.5. Algoritmos Um algoritmo é o procedimento para resolver um problema complexo, 30 utilizando uma sequência precisa e bem determinada de passos simples e não ambíguos. Tais procedimentos eram utilizados originalmente em cálculos matemáticos e, hoje são usados em programas de computador e em projetos de hardware. Usualmente são auxiliados por fluxogramas que são utilizados para facilitar o entendimento da sequência dos passos. Os fluxogramas representam a sequência de passos implementada pelo algoritmo de forma gráfica. A Figura 2-3 mostra o exemplo de um fluxograma. 2.7.6. Exemplos de Codificação de Instruções Existem muitas linguagens de programação. Pode-se escrever um algoritmo para resolução de um problema por intermédio de qualquer linguagem. A seguir, são mostrados alguns exemplos de trechos de códigos escritos utilizando algumas linguagens de programação. Exemplo: trecho de um algoritmo escrito numa pseudo-linguagem de “alto nível”, que recebe um número (armazenado na variável num), calcula e mostra os valores múltiplos de 1 a 10 para o mesmo. ler num para n de 1 até 10 passo 1 fazer tab num * n imprime tab fim fazer Exemplo: trecho do mesmo programa escrito em linguagem C: unsigned int num,n,tab; scanf("%d",&num); for(n = 1; n <= 10; n++){ tab = num * n; printf("\n %d", tab); } Exemplo: trecho do mesmo programa escrito em linguagem BASIC: 10 INPUT num 20 FOR n = 1 TO 10 STEP 1 30 LET tab = num * n 40 PRINT chr$ (tab) 50 NEXT n Exemplo: trecho do mesmo programa escrito em linguagem Fortran: read (num); do 1 n = 1:10 tab = num * n write(tab) 10 continue Exemplo: trecho do mesmo programa escrito em linguagem Assembly para INTEL 8088: MOV CX,0 ; coloca zero no registrador CX IN AX,PORTA ; coloca um valor do buffer de teclado no registrador AX MOV DX,AX ; copia o valor de AX para DX LABEL: INC CX ; incrementa em um o valor armazenado em CX 31 MOV AX,DX; copia o valor de DX para AX MUL CX ; multiplica o valor armazenado em CX pelo valor em AX OUT AX, PORTB ; o valor resultante é enviado ao buffer de saída de display CMP CX,10 ; o valor armazenado em CX é comparado com 10 JNE LABEL ; se a contagem ainda não chegou a 10, pula para LABEL e repete 32 2.8. Exercícios 1. Abra a tampa do seu computador pessoal. Efetue um esboço dos blocos componentes. Identifique os barramentos, a CPU, memórias e outros dispositivos. 2. Qual é a diferença entre um supercomputador e um computador pessoal? 3. Pesquisar sobre conexão de processadores em paralelo. 4. Quantos sistemas operacionais você conhece? 5. Qual é a diferença entre uma Word e um byte? 6. Qual é a diferença entre um microprocessador e um microcontrolador? 7. Quantos microprocessadores e microcontroladores compõem o seu computador pessoal? 8. Verificar a existência de um microcontrolador dentro do seu teclado. 9. Por que é importante a elaboração de fluxogramas antes de efetuar a codificação das instruções. 33 3. A CODIFICAÇÃO DA INFORMAÇÃO Os tópicos que serão discutidos neste capítulo incluem: Os sistemas de numeração Organização dos dados Operações lógicas Números com e sem sinal A codificação ASCII Durante séculos, o ser humano vem codificando e armazenando a informação mediante símbolos gráficos (linguagem escrita) e regras de montagem (gramática). A informação pode ser definida como o conhecimento derivado do estudo ou a experiência, basicamente, uma coleção de fatos ou dados. Provavelmente essa capacidade abstrata de armazenar a informação ao longo dos séculos permitiu ao ser humano se impor sobre as outras espécies animais. A informação em si é um conceito abstrato que para ser compartilhado com outras pessoas deve ser representado de forma coerente e simples. A linguagem escrita é um meio coerente, onde temos uma série de símbolos que associados de formas diferentes representam informações diferentes. A representação da informação abstrata numa série de símbolos (podem ser gráficos, sonoros, elétricos, luminosos) é conhecida como codificação. A informação codificada depende fortemente do contexto em que esta for transmitida, por exemplo, o código ELÉTRON pode significar uma referência a um componente da matéria, ao nome de um cão de estimação ou simplesmente a um conjunto de caracteres que implementa uma senha de acesso. Existem infinitas possibilidades de codificar uma informação. A informação escrita é codificada utilizando letras do alfabeto (existem inúmeros alfabetos diferentes no planeta) e regras de construção (existem inúmeros idiomas com gramáticas totalmente diferentes). A informação, repassada na forma de ondas de deslocamento de ar, possui suas regras (língua falada). No século XX, começaram a ser utilizados outros tipos de codificação, como os sinais de radiofrequência (ondas de rádio), onde um mecanismo eletrônico codificava o sinal sonoro ou visual em ondas eletromagnéticas e no receptor estes sinais são decodificados, ou seja, convertidos novamente em sinais de imagem ou som. Nos computadores, a informação é codificada na forma de campos magnéticos (nos discos rígidos ou disquetes) ou de campos elétricos (nas memórias). Para o ser humano colocar as informações num computador, precisa de ferramentas que executem esta tradução de sinais gráficos em sinais elétricos, por exemplo. Para criar essas ferramentas ou utilizá-las com 34 maior eficiência, deve-se conhecer como os computadores codificam a informação. Os computadores eletrônicos codificam a informação de forma discreta utilizando sistemas binários. Esses sistemas trabalham com a unidade mínima de informação, o bit. O conceito de quantidade de informação está relacionado diretamente com a probabilidade de acontecer uma determinada informação. Por exemplo, se existirem duas informações possíveis, a probabilidade é de 50% para cada uma delas. Se existirem 3 informações possíveis, a probabilidade diminui para 33.3%. Se somente existir uma informação, aí a probabilidade de acontecer é 100%, ou seja, não há informação a ser repassada ou transmitida. Disso, deduz-se que para existir alguma informação, deverão existir no mínimo duas informações possíveis, basicamente sistemas binários, onde o mínimo de informação é representado por uma variável que pode assumir um de dois valores possíveis, sendo a natureza desse sinal, um campo elétrico, magnético, eletromagnético, diferença de pressão, temperatura, força, etc.. No caso de ter que codificar um conjunto maior de informações (mais que duas), procede-se a aumentar o número de unidades de informação (bits) que as compõem, analogamente, como é feito com as letras do alfabeto. Como exemplo, pode-se imaginar a codificação da informação abstrata “UM” como quantidade. Isso pode ser codificado pela mente humana como símbolos: “UM”, “um”, ”Um”, “uM”, “1”, “ 1 ” , “01”, “1.0”, ”1.100”, “One”, “Un”, “Uno”, “Une”, “Eins”, etc. Nos computadores binários, devido aos limitados recursos, quando comparados com os da mente humana, procura-se sempre a compactação da informação. A informação de quantidade pode ser armazenada na sua forma mais compacta na memória do computador, como uma sequência de estados de um conjunto específico de transistores que estariam, por exemplo, nos estados “0000 0001”, onde um estado representado por 0 identifica um transistor operando na região de saturação, e um 1 significa um transistor na região de corte. Este conjunto de bits forneceria a informação abstrata de quantidade. Alguns exemplos de codificação binária: Quantidade abstrata 2 num computador de 8 bits: 0000 0010 Quantidade abstrata 2 num computador de 16 bits: 0000 0000 0000 0010 Símbolo gráfico ‘2’ (Codificação ASCII): 0011 0010 Quantidade abstrata 0 num computador de 8 bits: 0000 0000 Quantidade abstrata 0 num computador de 16 bits: 0000 0000 0000 0000 Símbolo gráfico ‘0’ (Codificação ASCII): 0011 0000 O número total de bits utilizado na representação da informação limita o 35 número máximo de informações possíveis. Assim, se for escolhido um conjunto de 8 bits para representar um conjunto de informações, o máximo possível será de 28 = 256 possíveis informações. A regra geral é que o número máximo de informações possíveis representáveis por n bits será de 2n. Por exemplo, se o nosso conjunto de informações fossem os símbolos numéricos do sistema decimal, do 0 ao 9 (10 informações ao todo), somente serão necessários 4 bits (24 = 16) e ainda sobrariam 6 possíveis informações. 36 3.1. Sistemas de Numeração Os sistemas de numeração [4] são os vários sistemas de notação que são ou têm sido usados para representar quantidades abstratas chamadas de números. Um sistema de numeração é definido pela base que utiliza, ou seja, o número de símbolos diferentes requeridos para que o sistema possa representar qualquer série infinita de números. Desta forma, o sistema decimal, utilizado por quase todos os habitantes do planeta (exceto para aplicações de computadores), requer dez símbolos diferentes, também chamados de dígitos, para representar os números, sendo um sistema de base 10.Ao longo da história, muitos sistemas de numeração diferentes têm sido usados, de fato, qualquer número acima de 1 pode ser usado como base. Algumas culturas têm usado sistemas baseados nos números 3, 4 e 5. Os babilônios usavam o sistema sexagesimal, baseado no número 60, e os romanos usavam (para certos propósitos), o sistema duodecimal, baseado no número 12. Os Maias usavam um sistema bigecimal, baseados no número 20. O sistema binário, baseado no número 2, foi utilizado por algumas tribos e, junto com o sistema octal baseado no 8 e no hexadecimal, baseado no 16, são usados hoje em dia em sistemas microprocessados. Figura 3-1 – Antigos sistemas de numeração[14] 3.1.1. Valores de acordo com a Posição O sistema universalmente adotado para a notação matemática, hoje em dia, é o sistema decimal (exceto para sistemas de computação). A posição do símbolo num sistema de base 10 denota o valor deste em termos de valores exponenciais da sua base (regra também válida em outros sistemas). Isto é, no sistema decimal a quantidade representada pela combinação dos dez símbolos utilizados ( 0, 1, 2, 3, 4, 5, 6, 7, 8, e 9 ) depende da posição no número. Assim, o número 3098323 é uma abreviação para: (3 × 106) + (0 × 105) + (9 × 104) + (8 × 103) + (3 × 102) + (2 × 101) + (3 × 100). O primeiro “3” (lendo da direita para a esquerda) representa três unidades; o segundo “3” representa trezentas unidades; e o terceiro “3”, três milhões de 37 unidades. Neste sistema o zero representa duas funções muito importantes: indica a não existência ou nada, e também serve para indicar os múltiplos de base 10, 100, 1000 e assim sucessivamente. Também é usado para indicar as frações de valores inteiros: 1/10 pode ser escrito como 0.1, 1/100 , como 0.01 e assim sucessivamente. Dois dígitos são suficientes para representar um número no sistema binário; 6 dígitos (0, 1, 2, 3, 4, 5) são necessários para representar um número no sistema sexagesimal; e 12 dígitos (0, 1, 2, ,3 , 4, 5, 6, 7, 8, 9, d (símbolo para o dez), z (símbolo para o onze)) são necessários para representar um número no sistema duodecimal. O número 30155 no sistema sexagesimal equivale a: (3 × 64) + (0 × 63) + (1 × 62) + (5 × 61) + (5 × 60) = 3959 no sistema decimal. O número 2zd no sistema duodecimal equivale a (2 × 122) + (11 × 121) + (10 × 120) = 430 no sistema decimal. Para escrever um número n de base 10 como um número de base b, deve-se dividir (no sistema decimal) n por b desprezando os valores fracionários, depois dividir o quociente por b novamente, e assim sucessivamente, até que seja obtido um quociente igual a zero. Os restos sucessivos da divisão são os dígitos da expressão de n no sistema de base b. Por exemplo, para expressar o número 3959 decimal no seu equivalente de base 6 temos: Multiplicador[15] Divisor Resto 3959 6 659 5 109 5 18 1 3 0 0 3 Assim temos que 395910 = 301556. A base normalmente é escrita na forma de subscrito do número. Quanto maior for a base, maior o número de símbolos requeridos, porém, menos dígitos serão necessários para expressar um dado número. O número 12 é conveniente como base devido que e exatamente divisível por 2, 3, 4 e 6, por esta razão alguns matemáticos têm adotado o sistema de base 12, no lugar do sistema de base 10. 3.1.2. Sistema Binário O sistema binário tem um papel muito importante na tecnologia da computação. Qualquer número decimal pode ser expresso no sistema binário pela soma das diferentes potências de dois. Por exemplo, começando pela direita 10101101 representa (1 × 20) + (0 × 21) + (1 × 22) + (1 × 23) + (0 × 24) + (1 × 25) + (0 × 26) + (1 × 27) = 173. 38 Esse exemplo pode ser utilizado para converter números binários em decimais. Para a conversão de números decimais em binários, procede-se pelo método de divisões sucessivas, armazenando os restos das divisões, como visto anteriormente. As operações aritméticas de sistemas binários são extremamente simples. As regras básicas são : 1 + 1 = 10, e 1 × 1 = 1. O zero funciona como no sistema decimal: 1 × 0 = 0, e 1 + 0 = 1. A soma, a subtração e a multiplicação são executadas de forma similar ao sistema decimal: Já que somente existem dois dígitos (ou bits) envolvidos, os sistemas binários são utilizados em computadores, uma vez que qualquer número binário pode ser representado, por exemplo, pela posição de uma série de chaves liga- desliga. A posição “liga” poderia corresponder ao 1, e a posição “desliga” poderia corresponder ao 0. No lugar de chaves, podem ser utilizados pontos magnetizados de um disco ferromagnético ou magneto-óptico, onde a magnetização numa direção indica um 1, e na outra um 0. Também podem ser utilizados Flip-Flops, que são dispositivos eletrônicos que podem ter somente uma tensão elétrica (ou estado) de saída e podem ser chaveados para o outro estado através de um pulso elétrico. Os circuitos lógicos nos computadores executam as diferentes operações com números binários, sendo que a conversão de números decimais para binários, e vice-versa, é feita eletronicamente. Base 10 (Decimal) Base 2 (Binário) 8 4 2 1 0 1 2 3 4 5 7 8 9 10 11 12 13 14 15 0 0 0 0 0 0 0 1 5 = 0 1 0 1 Base 10 Base 2 8 4 2 1 5 0 1 0 1 5 = (0 x 8) + (1 x 4) + (0 x 2) + (1 x 1) 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1 1 1 0 0 0 Na base 10 as colunas são organizadas pelo peso das potências de 10 (unidades 1 0 0 1 1 0 1 0 39 1 0 1 1 = 100, dezenas = 101, centenas = 102, e milhares 103). Na base 2, as colunas estão organizadas pelo peso das potências de 2 (unidades = 20, duplas = 21, quádruplas = 22 e óctuplas = 23. Este formato é chamado de 8-4-2-1 e é usado também nos computadores. 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 Tabela 3-1 – Sistema binário A maioria dos computadores opera usando lógica binária. O computador representa valores usando dois níveis de tensão elétrica (usualmente 0 e +5V). Com esses dois níveis pode-se representar exatamente dois valores diferentes. Por convenção, nomeamos estes dois valores possíveis como zero e um. Esses dois valores, coincidentemente, correspondem aos dois dígitos usados no sistema de numeração binário. Uma vez que existe uma correspondência entre os níveis lógicos usados nos microprocessadores e os dois dígitos usados em sistemas binários, existirá uma identificação perfeita entre o sistema de numeração e o sistema físico. 3.1.3. Formatos Binários Teoricamente um número binário pode conter um número infinito de dígitos (ou bits). Por exemplo, pode-se representar o número 5 por: 101000001010000000000101...000000000000101 Ainda qualquer número 0 pode preceder o número binário sem mudar o seu valor. Por analogia ao sistema decimal, podemos ignorar os zeros colocados à esquerda. Por exemplo, o valor 101 representa o número 5 por convenção. Num dispositivo microprocessador, por exemplo, um 8051 que trabalha com grupos de 8 bits, a interpretação dos números binários é facilitada representando-os com um conjunto múltiplo de 4 ou 8 bits. Assim, seguindo a convenção, podemos representar o número cinco como 01012 ou 000001012. Quando o número de dígitos for muito grande no sistema decimal, é usual separar os múltiplos de 1000 através de pontos para facilitar a leitura. Por exemplo, o número 4.294.967.296 fica mais fácil de interpretar que 4294967296. Da mesma forma, para números binários extensos écomum adotar uma técnica semelhante, que consiste em separar os dígitos em grupos de 4 bits separados por um espaço, o que é adequado para a representação no sistema hexadecimal. Por exemplo, o número binário 1010111110110010 será escrito como 1010 1111 1011 0010. Freqüentemente são compactadas informações diferentes no mesmo número 40 binário. Por exemplo, uma das formas da instrução MOV do processador 80x86 utiliza o código de 16 bits 1011 0rrr dddd dddd para empacotar três informações no mesmo número: cinco bits para o código de operação (10110), um campo de três bits para indicar o número do registrador (rrr) e um valor numérico de oito bits (dddd dddd). Por conveniência, designa-se um valor numérico às posições de cada bit: O bit que fica na extremidade direita do número binário é o bit da posição zero, também chamado de bit menos significativo ou LSB[16]. A cada bit à esquerda é atribuído um número sucessivo até o bit que fica na extremidade esquerda do número, também chamado de bit mais significativo ou MSB[17]. Um número de 8 bits tem posições que vão do zero até sete. Bit x x x x x x x x Posição 7 6 5 4 3 2 1 0 Um número de 16 bits tem posições que vão do zero até quinze. Bit x x x x x x x x x x x x x x x x Posição 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 41 3.2. Organização dos Dados Na matemática pura, um valor pode ser representado por um valor arbitrário de bits. Por outro lado, em aplicações de computadores, geralmente se trabalha com um grupo específico de bits, dependendo do microprocessador utilizado. Coleções de bits comuns são grupos de quatro bits (chamados nibbles), de oito bits (chamados bytes), grupo de n bits (chamados de words). Por exemplo, os microprocessadores Pentium utilizam words de 32 bits (4 bytes), alguns microcontroladores PIC utilizam 14 bits de palavra de código e 1 byte (8 bits) de dados. 3.2.1. Bits A menor unidade de dados em um computador binário é um único bit. Foi visto que um bit é capaz de representar somente um de dois valores diferentes (tipicamente zero e um). Baseado nesta suposição, pode-se ter a impressão de que existe um número muito pequeno de itens que podem ser representado com um único bit. Isso não é necessariamente verdade, desde que há um número infinito de itens que podem ser representados por um único bit. Com um único bit podem ser representados quaisquer dois itens diferentes. Exemplos incluem zero ou um, verdadeiro ou falso, ligado ou desligado, macho ou fêmea, certo ou errado, azul e vermelho, etc.. Dessa forma, não há limite para representar tipos de dados em binário (i.e. aqueles objetos que podem ter somente um de dois valores distintos). Pode ser utilizado um único bit para representar os números 966 e 1326, ou no lugar destes, 6242 e 6. Também podem ser representados dois objetos não relacionados entre si, com um único bit, como por exemplo, a cor vermelha e o número 3926. Generalizando ainda mais, bits diferentes podem representar coisas diferentes, por exemplo, um bit pode ser utilizado para representar os valores zero e um, enquanto o bit adjacente pode ser utilizado para representar os valores verdadeiro e falso. Agora como podemos saber o que os bits significam somente olhando para eles ? A resposta é que não há como. Mas isso mostra tudo o que está por trás das estruturas de dados do computador: os dados são o que o programador define que sejam. Se um programador utilizar um bit para representar o valor booleano (verdadeiro ou falso), então aquele bit (por definição do programador) representará verdadeiro ou falso. Para que esse bit tenha significado, o programador deverá ser consistente, ou seja, se utilizar um bit para representar verdadeiro ou falso em um ponto do seu programa, não poderá usar os valores verdadeiro ou falso usados naquele bit para representar vermelho ou azul depois. A maioria dos itens que podem ser modelados, requer mais que dois valores diferentes, desta forma, bits isolados são o tipo de dados menos utilizado 42 quando há informações mais complexas. 3.2.2. Nibbles Um nibble é uma coleção de 4 bits. Não é necessariamente uma estrutura de dados interessante, exceto por duas coisas: para os números BCD[18] e para os números hexadecimais. São necessários 4 bits para representar um único dígito BCD ou hexadecimal. Com um único nibble pode-se representar até 16 valores distintos (24). No caso dos números hexadecimais, os valores 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, e F são representados por quatro bits. Os números em BCD utilizam dez dígitos diferentes dígitos (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) e também requerem de quatro bits. De fato, quaisquer dezesseis valores distintos podem ser representados por um nibble.. 3.2.3. Bytes Sem dúvidas, a estrutura mais importante usada nos computadores é o byte. Um byte consiste de um grupo de oito bits. Como um byte contém 8 bits, ele pode representar 28 ou seja 256 valores diferentes. Geralmente, é usado um byte para representar valores numéricos na faixa de 0 a 255, ou números com sinal na faixa de –128 a +127. Os caracteres ASCII são outro tipo especial de dados que requerem mais de 256 valores diferentes para identificar as letras, números e principais símbolos do alfabeto ocidental. 3.2.4. Words Uma word é um grupo de n bits. A maioria dos novos sistemas trabalha com words de 16 bits, mas também existem sistemas com words de 8, 14, 32, 64 e 128 bits. As words são usualmente subdivididas em nibbles ou em bytes de acordo com a conveniência, devido ao seu grande número de bits. Uma word de 16 bits pode representar 65536 valores diferentes (216), que poderão ser valores numéricos positivos (unsigned) na faixa de 0 a 65535, ou valores com sinal (signed) de –32768 a +32767, ou ainda qualquer tipo de dado com não mais de 65536 valores. Alguns usos típicos para este tipo de variável são para representar valores numéricos inteiros, offsets[19] e segmentos de endereços de áreas de memória ou endereços de I/O. 3.2.5. Double Words Uma double word é um tipo de dado com o tamanho de duas words. Normalmente, dividida em uma word de ordem maior ou mais significativa, e uma word menos significativa. Os sistemas 80x86 utilizam por exemplo, words de 16 bits e double words de 32 bits. Uma double word de 32 bits pode representar um número inteiro sem 43 sinal na faixa de 0 a 4294967295 ou um número inteiro com sinal na faixa de –2147483648 a 2147483647. 3.2.6. Números com Ponto Flutuante Em essência, os computadores são máquinas que operam números inteiros, mas são capazes de representar números reais pela utilização de códigos complexos. O código mais popular para representar números reais é o padrão definido pela IEEE. Os computadores processam a informação em conjuntos de bits. Não há como colocar um ponto fracionário pela natureza física do armazenamento. Um número real pode ser representado em notação exponencial, i.e. por um valor inteiro multiplicado pela base elevada a um expoente. Por exemplo, o número 3.26 pode ser representado por 326 x 10-2. Utilizando esse tipo de representação, podemos definir um número real utilizando somente números inteiros, i.e. armazenando a informação da mantissa (326) e o expoente (-2) em um conjunto de bits. O termo “ponto flutuante” deriva do fato de que não há um número fixo de dígitos antes ou depois do ponto decimal; i.e. o ponto decimal pode flutuar. Existem, também, representações nas quais o número de dígitos, antes e depois do ponto, é fixo. Estas são chamadasde representações de ponto fixo. Em geral, as representações de ponto flutuante são lentas e menos exatas que as representações de ponto fixo, mas elas podem manipular uma faixa maior de números. Assim deve ser notado que a maioria dos números em ponto flutuante que um computador pode representar são somente aproximações. Um dos desafios da programação, com variáveis de ponto flutuante, é de assegurar que as aproximações levem a resultados precisos. Se o programador não tiver cuidado, pequenas discrepâncias nas aproximações podem levar a resultados finais errados. Devido ao fato que a matemática de ponto flutuante requerer grande parte dos recursos do computador (memória e tempo de processamento), muitos microprocessadores são equipados com um chip chamado de FPU[20], especializado em executar a aritmética de ponto flutuante, sendo também chamados de coprocessadores matemáticos. Usualmente os números de ponto flutuante são representados em 32 bits (4 bytes), mas também há representações em 64 (double, 8 bytes), 80 (long double, 10 bytes) e 128 bits (16 bytes). Um número float representado em 32 bits pode armazenar valores na faixa de 3.4E+/-38 (7 dígitos), um double de 64 bits, 1.7E+/-308 (15 dígitos) e um long double de 80 bits, 1.2E+/-4932 (19 dígitos). 44 45 3.3. O Sistema de Numeração Hexadecimal O grande problema de trabalhar com o sistema binário é o grande número de dígitos para representar valores relativamente pequenos. Para representar o valor 202 precisamos de 8 dígitos binários. A versão decimal requer somente três dígitos decimais, e desta forma, pode representar os números de forma muito mais compacta que o sistema de numeração binário. Esse fato não era muito aparente quando os engenheiros projetavam os primeiros sistemas computacionais binários. Quando se começa a manipular grandes valores, os números binários rapidamente ficam muito extensos. Desafortunadamente, os computadores trabalham em binário, de modo que na maioria das vezes é conveniente usar o sistema de numeração binário. Uma solução seria converter os números binários para o sistema decimal, mas a conversão não é uma tarefa trivial. O sistema hexadecimal (de base 16) oferece as duas características desejadas: são compactos e simples de convertê-los em binário e vice-versa. Por causa disso, a maioria dos computadores de hoje em dia usam a representação hexadecimal. Uma vez que a base do sistema hexadecimal é dezesseis, cada dígito à esquerda do ponto decimal representa algum valor, vezes as potências sucessivas de 16. Por exemplo, o número 123416 é equivalente a: 1 x 163 + 2 x 162 + 3 x 161 + 4 x 160 ou 4096 + 512 + 48 + 4 = 4660 (decimal). Cada dígito hexadecimal pode representar um de dezesseis valores entre 0 e 15. Uma vez que existem somente dez dígitos decimais, é necessário adicionar seis símbolos adicionais para os dígitos que representarão os valores de 10 a 15. Ao invés de criar novos símbolos para estes dígitos, foram redefinidas as letras A à F para simbolizar as novas quantidades. O exemplo que segue é um número hexadecimal válido: 1234 DEAD BEEF 0AFB FEED DEAF Usualmente é colocada a letra H (ou h) no final do número em hexadecimal, para deixar a base explícita. A seguir alguns exemplos de números em hexadecimal. 1234h 0DEADH 0BEEFh 2AFBh 6567FEEDh 46 Como pode ser observado, os números em hexadecimal são compactos e fáceis de ler. Além disso, a relação entre os números em hexadecimal e binário é direta, isto é, devido ao fato da base 16 ser uma potência exata da base 2. Um dígito em hexadecimal é representado por um nibble. A conversão é feita segundo a seguinte tabela: Binário Hexadecimal 0000 0 0001 1 0010 2 0011 3 0100 4 0101 5 0110 6 0111 7 1000 8 1001 9 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F Tabela 3-2 – Equivalência entre os sistemas binário e hexadecimal A tabela mostra toda a informação necessária para converter qualquer número hexadecimal em um número binário e vice-versa. Para converter um número hexadecimal em números binários, simplesmente substituem-se os quatro bits correspondentes para cada dígito hexadecimal. Por exemplo, para converter 0ABCDh em um valor binário, simplesmente converte-se cada dígito hexadecimal utilizando a tabela acima, assim: 0 A B C D Hexadecimal 0000 1010 1011 1100 1101 Binário Para converter um número binário no formato hexadecimal procede-se da seguinte forma: o primeiro passo é deixar o número binário com o número de dígitos divisível por quatro, colocando zeros à esquerda se for necessário. Por exemplo, dado o número binário 1011001010 com dez dígitos; acrescentam- se 2 dígitos à esquerda para obter um número de dígitos divisível por quatro (12 bits), obtendo o número 001011001010. O passo seguinte é separar o número binário em grupos de quatro bits; assim tem-se 0010 1100 1010. Finalmente compara-se cada grupo de quatro bits na tabela e substitui-se pelos dígitos equivalentes em hexadecimal, ficando assim 2CAh. A tabela de conversão normalmente é memorizada em poucos minutos, o que 47 agiliza o processo de conversão entre esses dois sistemas. 48 3.4. Operações Lógicas Existem quatro operações lógicas principais para trabalhar com números binários: AND, OR, XOR [21] e NOT. O operador AND precisa de pelo menos duas variáveis binárias, e o seu resultado é mostrado a seguir: 0 AND 0 = 0 0 AND 1 = 0 1 AND 0 = 0 1 AND 1 = 1 Uma forma mais compacta de representação é a forma tabular, chamada de tabela verdade. Usualmente o operador AND pode ser substituído pelo símbolo “.”. As funções lógicas também são implementadas em hardware (portas lógicas) possuindo símbolos especiais como mostra na Figura 3-2. Figura 3-2 – Porta AND “Se ambos os operandos de uma função AND são verdadeiros, o resultado será verdadeiro; caso contrário o resultado será falso”. Um fato importante de notar acerca do operador lógico AND é que o mesmo pode ser utilizado para forçar um resultado zero. Se um dos operandos for zero, o resultado será sempre zero independente do valor do segundo operando. Esta característica da operação AND é muito importante, especialmente quando se trabalha com conjuntos de bits e for necessário forçar certos bits para zero, mantendo os outros intactos. O operador OR lógico possui a seguinte característica: 0 OR 0 = 0 0 OR 1 = 1 1 OR 0 = 1 1 OR 1 = 1 Figura 3-3 – Porta OR Usualmente o operador OR pode ser substituído pelo símbolo “+”. 49 “Se um dos operandos da função OR for verdadeiro, o resultado será verdadeiro. O resultado falso será obtido somente quando os dois operadores forem falsos”. Um fato importante de notar-se acerca do operador lógico OR é que o mesmo pode ser utilizado para forçar um resultado “1”. Se um dos operandos for “1”, o resultado será sempre “1” independente do valor do segundo operando. Esta característica da operação OR é muito importante, especialmente quando se trabalha com conjuntos de bits e for necessário forçar
Compartilhar