• Nenhum resultado encontrado

Seria quase impossível o desenvolvimento de sistemas complexos de software, como os sistemas operacionais, os softwares de redes e a grande quantidade de aplicações disponíveis atualmente, se os seres humanos fossem obrigados a expressar os seus algoritmos diretamente em linguagem de máquina. Trabalhar com um volume significativo de detalhes da linguagem de máquina e, ao mesmo tempo, tentar organizar sistemas complexos é, no mínimo, uma experiência desgastante. Como conseqüência, linguagens de programação semelhantes ao nosso pseudocódigo foram desenvolvidas, permitindo expressar algoritmos em um formato que fosse tanto agradável como fácil de traduzir para instruções em linguagem de máquina. Tais linguagens evitam a complexidade dos registradores, endereços de memória e ciclos de máquina durante o processo de desenvolvimento de programas, concentrando-se, em lugar disso, nas características do problema a ser solucionado. Neste capítulo, estudaremos o tópico de linguagens de programação.

C A P Í T U L O 5

5.1 Perspectiva histórica As primeiras gerações de linguagens Independência de máquina Paradigmas de programação 5.2 Conceitos tradicionais de

programação Variáveis e tipos de dados Estruturas de dados Constantes e literais Instruções de atribuição Instruções de controle Comentários 5.3 Módulos de programas Procedimentos Parâmetros Funções

Instruções de entrada e saída 5.4 Implementação de linguagens

O processo de tradução Ligação e carregamento

Pacotes para o desenvolvimento de software *5.5 Programação orientada a objeto

Classes e objetos Construtores Recursos adicionais *5.6 Programação de atividades concorrentes *5.7 Programação declarativa Dedução lógica Prolog

5.1 Perspectiva histórica

Iniciemos o nosso estudo apresentando o desenvolvimento histórico das linguagens de programação. As primeiras gerações de linguagens

Originalmente, o processo de programação era realizado de uma forma laboriosa, em que o pro- gramador escrevia todos os algoritmos em linguagem de máquina. Este método teve grande importância para a já então rigorosa atividade do projeto de algoritmos, a qual, porém, na maioria dos casos, condu- zia ao aparecimento de erros que precisavam ser localizados e corrigidos (processo conhecido como

depuração) antes da liberação final do programa.

O primeiro passo para simplificar o processo de programação foi eliminar o uso de dígitos numé- ricos para representar os códigos de operação e os operandos, presentes nas linguagens de máquina. Para esta finalidade, foi comum o emprego de mnemônicos no lugar de notações hexadecimais para represen- tar os diversos códigos de operações durante a etapa de projeto. Em vez de empregar o código de opera- ção correspondente à instrução para carregar um registrador, o programador escreveria, por exemplo, LD, ou, para simbolizar o armazenamento do conteúdo de um registrador na memória, usaria ST. No caso dos operandos, foram estabelecidas regras segundo as quais o programador atribuía nomes descritivos (em geral chamados identificadores) às posições de memória e os utilizava nas instruções em lugar dos endereços numéricos de posições de memória. Um caso específico deste uso foi a utilização de nomes como R0, R1, R2,... para os registradores do processador.

Escolhendo nomes descritivos para as posições de memória e usando mnemônicos para represen- tar códigos de operação, os programadores aumentaram significativamente a legibilidade de seqüências de instruções de máquina. Como exemplo, retomemos a rotina em linguagem de máquina apresentada na Figura 2.7 da Seção 2.2, a qual efetua a adição dos conteúdos das posições de memória 6C e 6D e guarda o resultado na posição 6E. As instruções, em notação hexadecimal, apresentam-se como:

156C 166D 5056 306E C000

Se associarmos o nome Valor à posição de memória 6C, Imposto à posição 6D e Total à posição 6E, usando a técnica mnemônica, a mesma rotina poderá ser expressa da seguinte maneira:

LD R5, Valor LD R6, Imposto ADDI R0, R5 R6 ST R0, Total HLT

A maioria concorda em que a segunda forma, embora ainda incompleta, é melhor que a primei- ra para representar a rotina. (Note-se que o mnemônico ADDI é usado para representar o código de operação de adição de inteiros, para distingui-lo do código de operação de adição de números reais, representado por ADDF.)

Quando estas técnicas foram introduzidas, os programadores escreviam em papel os programas nesta notação e depois os traduziam para uma forma que fosse utilizável em máquinas. Não demorou, porém, até que este processo de tradução fosse ele próprio identificado como um procedimento que poderia ser executado pela máquina. Como conseqüência, o uso de mnemônicos foi formalizado como uma linguagem de programação, chamada linguagem de montagem*, e um programa chamado

montador** foi desenvolvido para traduzir os progra-

mas escritos em linguagem de montagem para um for- mato que fosse compatível com as máquinas. O progra- ma recebeu o nome de montador porque sua tarefa era a de montar instruções de máquina a partir de códigos de operação e operandos, obtidos da conversão de mnemô- nicos e identificadores. O termo de linguagem de montagem tem origem neste fato.

O surgimento das linguagens de montagem cons- tituiu um gigantesco passo na pesquisa de melhores ambientes de programação. De fato, muitos as conside- raram como representantes de uma geração totalmente nova de linguagens de programação. A propósito, as lin- guagens de montagem passaram a ser conhecidas como linguagens de segunda geração, em contrapartida às de primeira geração, representadas pelas próprias lingua- gens de máquina.

Embora as linguagens de segunda geração apre- sentassem muitas vantagens sobre as linguagens de má- quina a que correspondiam, ainda estavam longe de ofe- recer um ambiente de programação adequado. Afinal de contas, as primitivas utilizadas nas linguagens de mon- tagem eram essencialmente as mesmas encontradas nas

linguagens de máquina correspondentes. A diferença estava simplesmente na sintaxe usada para repre- sentá-las. Uma conseqüência desta forte associação entre linguagens de montagem e linguagens de má- quina é que qualquer programa escrito em uma linguagem de montagem depende inerentemente de máquina. Isso quer dizer que as instruções de um programa são expressas em termos dos atributos de uma máquina em particular. Por sua vez, um programa escrito em linguagem de montagem não pode ser transportado com facilidade de uma máquina para outra, pois teria de ser reescrito de forma compatível com a configuração de registradores e com o conjunto de instruções da nova máquina.

Outra desvantagem de uma linguagem de montagem é que, embora um programador não seja forçado a programar as instruções na forma de padrões de bits, seu raciocínio continua voltado às minú- cias do programa, aos pequenos passos incrementais da linguagem de máquina, em vez de concentrar-se na lógica global da solução para a sua tarefa. A situação é análoga à do projeto de uma casa em termos de tábuas, vidros, pregos, tijolos e assim por diante. É verdade que, em última análise, a construção da casa exige uma descrição baseada em tais componentes elementares, mas o processo de projeto ficará mais fácil se pensarmos em termos de cômodos, alicerces, janelas, telhados e assim por diante.

Em resumo, os componentes elementares nos quais um produto deve, em última instância, ser expresso não são necessariamente os elementos mais adequados para serem utilizados durante o seu projeto. O processo de projeto transcorre de forma mais apropriada com o uso de primitivas de alto nível, em que cada uma represente um conceito associado a alguma característica importante do produto. Uma vez terminado o projeto, tais primitivas podem ser traduzidas em termos de conceitos de nível mais baixo, de forma que os detalhes de implementação se evidenciem, do mesmo modo como uma construto- ra extrai, do projeto de um edifício, a lista de material a ser comprado.

Seguindo esta filosofia, os pesquisadores de computação começaram a desenvolver linguagens de programação que fossem mais direcionadas ao desenvolvimento de software do que as de montagem, de baixo nível. O resultado foi o aparecimento de uma terceira geração de linguagens de programação, que se distinguiram das anteriores pelo fato de suas primitivas serem de nível mais alto e independentes de

**N. de T. Em inglês, assembler.

Software multiplataforma