Capítulo 1 INTRODUÇÃO ÀS LINGUAGENS DE PROGRAMAÇÃO 1.1 Histórico de Linguagens de Programação Para um computador executar uma dada tarefa é necessário que se informe a ele, de uma maneira clara, como ele deve executar aquela tarefa. Em outras palavras, para cada tarefa a ser executada, o computador deve ser programado. Infelizmente, a linguagem nativa dos computadores convencionais (i.e., a linguagem que eles entendem a nível de máquina) é muito limitada em dois aspectos fundamentais. Primeiro, qualquer construção nesta linguagem deve ser representada por números binários formados apenas a partir de zeros (0) e uns (1) (cada um destes dígitos é chamado de bit). Além disso, os computadores entendem apenas um conjunto relativamente pequeno de instruções como, por exemplo, mova este número de uma posição para outra em memória ou some estes dois números inteiros. Cada computador (ou, mais precisamente, cada CPU de computador) possui um conjunto específico de instruções deste tipo denominado de o conjunto de instruções do computador. Programas escritos na linguagem binária (i.e., usando 0s e 1s) usando o conjunto de instruções de um computador são ditos serem programas em linguagem de máquina, e este tipo de programação é considerada como programação de baixo nível. Como você já deve ter desconfiado, programas escritos em linguagem de máquina são difíceis de escrever, ler e modificar (para nós, humanos, não para os computadores). Um grande melhoramento sobre a linguagem de máquina foi o surgimento da linguagem assembly (ou linguagem de montagem), que usa mnemônicos para representar instruções originalmente escritas em linguagem de máquina (por exemplo, uma instrução para adicionar números identificada por 10011011 em linguagem de máquina poderia ser identificada como ADD em assembly). A linguagem assembly também permite que sejam atribuídos nomes a variáveis (i.e., posições na memória do computador), por exemplo, uma variável identificada por 00111010 em linguagem de máquina poderia ser identificada (a critério do programador) por (digamos) X. Como a linguagem assembly é estranha ao computador (que só entende 0s e 1s), programas escritos nesta linguagem requerem um programa especial, chamado assembler (ou 1
2 INTRODUÇÃO ÀS LINGUAGENS DE PROGRAMAÇÃO Capítulo 1 montador), que traduz instruções escritas em assembly para instruções em linguagem de máquina. Apesar de ter sido um melhoramento considerável (para o programador) com relação à linguagem de máquina, programação em linguagem assembly ainda é difícil, e este tipo de programação ainda é considerado de baixo nível. No início da era dos computadores (i.e., de meados da década de 40 até meados da década de 50), todos os programas de computador eram de baixo nível (i.e., escritos em linguagem de máquina ou em assembly). Uma facilidade ainda maior para o programador foi a introdução das chamadas linguagens de alto nível, que permitem que programas sejam escritos usando uma linguagem mais próxima da linguagem humana. Apesar desta proximidade, as linguagens de programação de alto nível não podem ser consideradas como um meio de comunicação entre seres humanos. Em vez disso, elas constituem um forma de comunicação entre homens e computadores. Isto é, através delas o programador pode comandar, por meio de instruções, o computador a realizar uma tarefa desejada. O grande trunfo das linguagens de alto nível é que elas permitem que se especifiquem instruções sem a preocupação sobre detalhes do computador no qual o programa será executado. A maioria dos programas de hoje são escritos em linguagens de alto nível, mas muitos programadores experientes ainda usam a linguagem assembly por questões de eficiência (veja mais adiante). 1.2 Compiladores e Interpretadores Todo programa escrito numa linguagem de programação que não seja a própria linguagem de máquina de um computador requer uma tradução antes de ser executado neste computador. Já foi visto acima que a linguagem assembly (que é uma linguagem de baixo nível) requer um tipo especial de tradutor, chamado assembler (ou montador), que traduz programas escritos nesta linguagem para programas em linguagem de máquina. Como foi visto acima, a linguagem assembly evita que o programador escreva programas usando seqüências de números binários da linguagem de máquina através do uso de identificadores simbólicos (mnemônicos e nomes de variáveis) para estas seqüências de números. Existe portanto uma correspondência um-a-um entre instruções de um programa em linguagem assembly e a tradução deste programa em linguagem de máquina. Em outras palavras, a cada instrução escrita em assembly, existe uma única instrução em linguagem de máquina correspondente. Por isso, o assembler é o tipo de tradutor mais simples que existe.
Capítulo 1 INTRODUÇÃO ÀS LINGUAGENS DE PROGRAMAÇÃO 3 Linguagens de alto nível requerem tradutores mais complicados, visto que, usualmente, uma única instrução em linguagem de alto nível pode corresponder a muitas instruções em linguagem de máquina. Existem dois tipos de tradutores para linguagens de alto nível: compiladores e interpretadores. Um compilador é um programa que traduz programas escritos numa linguagem de alto nível (programas fonte) em programas (programa objeto) escritos numa linguagem de baixo nível (linguagem de máquina ou assembly) de um computador. Um compilador depende não apenas da linguagem para a qual ele foi projetado, mas também do computador no qual programas escritos nesta linguagem devem ser traduzidos. Assim, é comum falar-se de um compilador (da linguagem) X (por exemplo, Pascal) para o computador Y (por exemplo, Macintosh). Usualmente, um programa escrito numa linguagem de alto nível tem várias partes que são compiladas separadamente. Para reunir as várias partes de um programa de modo a formar uma única unidade executável, utiliza-se um programa chamado de link editor (ou editor de ligações). Um link editor é um programa que recebe como entrada um conjunto de programas compilados e faz as devidas ligações entre estes programas. O produto resultante do link editor é um programa integrado pronto para ser executado. O segundo tipo de tradutor é o interpretador. Diferentemente do compilador, um interpretador não traduz um programa inteiro em seu equivalente em linguagem de máquina. Isto é, um interpretador não produz programas objeto. Em vez disso, ele simula um computador cuja linguagem de máquina seria aquela sendo traduzida. Assim, cada instrução do programa (em linguagem de alto nível) é traduzida exatamente antes de ser executada e de acordo com o fluxo de execução do programa. Isto significa que instruções que são executadas mais de uma vez, também são traduzidas este mesmo número de vezes; instruções que não são executadas não são traduzidas. Em contraste, um compilador traduz todas as instruções de um programa exatamente uma vez. Outra diferença importante entre compiladores e interpretadores é que, uma vez compilado, um programa torna-se independente do compilador (i.e., torna-se um programa standalone), enquanto que um programa interpretado depende sempre do interpretador para ser executado. Finalmente, programas compilados executam mais rapidamente do que programas equivalentes interpretados (por que?). Os programadores (usualmente) não precisam se preocupar em entender como um tradutor traduz seus programas para linguagem de máquina durante a elaboração de um programa, mas este conhecimento pode ser essencial na fase de depuração do programa.
4 INTRODUÇÃO ÀS LINGUAGENS DE PROGRAMAÇÃO Capítulo 1 1.3 Tipos de Linguagens 1.3.1 Linguagens de Baixo Nível x Linguagens de Alto Nível Linguagens de alto nível oferecem vantagens ao programador que podem ser analisadas segundo as seguintes propriedades desejáveis de um programa: Portabilidade. Um programa escrito em linguagem de alto nível pode ser transportado para qualquer computador que tenha um tradutor apropriado. Em contraste, um programa escrito em baixo nível é específico para um tipo de computador; i.e., se o programador desejar executar este programa num outro computador, o programa terá que ser reescrito. Legibilidade. Ao contrário das linguagens de baixo nível, as linguagens de alto nível oferecem notações que se assemelham às linguagens humanas. Como conseqüência, programas escritos em linguagens de alto nível são muito mais fáceis de serem escritos e lidos do que os correspondentes programas escritos em linguagem de baixo nível. Entretanto, note que escrever um programa numa linguagem de alto nível não garante esta propriedade: a legibilidade de um programa depende também (e muito) do próprio programador. Um dos objetivos deste livro é prover orientações para a escrita de programas legíveis e fáceis de serem modificados. Manutenibilidade. A facilidade que um programa tem de ser mantido (i.e., modificado) é denominada manutenibilidade. Esta propriedade está intimamente relacionada com a legibilidade: quanto mais fácil de ser lido um programa é, mais fácil ele será de ser modificado. Portanto, programas escritos em linguagem de alto nível são mais fáceis de serem modificados e depurados do que programas correspondentes escritos em linguagem de baixo nível. Eficiência. A eficiência de um programa é medida pelo espaço ocupado em memória e pela rapidez com que é executado. Um programa eficiente ocupa pouco espaço e tem uma execução rápida. Diferentes seqüências de instrução em linguagem de máquina podem resultar em programas que são funcionalmente equivalentes (i.e., produzem os mesmos resultados). Algumas combinações de instrução são executadas mais rapidamente do que outras. Escrevendo um programa diretamente em linguagem de máquina (ou em assembly), o programador pode usualmente decidir que seqüência de instruções é a mais eficiente, enquanto que escrevendo numa linguagem de
Capítulo 1 INTRODUÇÃO ÀS LINGUAGENS DE PROGRAMAÇÃO 5 alto nível, o programador tem pouco controle sobre como um compilador traduz o programa para linguagem de máquina. A realidade é que mesmo os mais sofisticados compiladores produzem código ineficiente. Algumas linguagens de alto nível (como C e Modula-2) possuem facilidades para acomodar código de baixo nível. 1.3.2 Linguagens de Caráter Específico x Linguagens de Caráter Geral Linguagens de caráter específico (ou voltadas para aplicação) são linguagens desenvolvidas visando uma área de aplicação e dificilmente podem ser utilizadas para o desenvolvimento de programas em outra área. Por exemplo, a linguagem COBOL foi desenvolvida para a área comercial e dificilmente poderia ser utilizada para a construção de um programa científico. Em contraste, uma linguagem de caráter geral pode em princípio ser utilizada para escrever programas em qualquer área. A linguagem Pascal é uma linguagem de caráter geral. 1.3.3 Paradigmas de Linguagens de Programação Um paradigma de linguagem indica a filosofia ou conceitos teóricos norteando o desenvolvimento da linguagem. Alguns dos paradigmas de linguagem mais comuns são apresentados a seguir. Uma discussão mais abrangente está além do escopo deste livro. Paradigma funcional: programação baseada em teoria matemática das funções. Exemplos: Lisp e Scheme. Paradigma lógico: programação baseada em lógica. Exemplo: Prolog. Paradigma orientada por objetos: Exemplos: Smalltalk, Java e C ++. Paradigma modular: programação baseada em módulos com compilação independente. Exemplo: Modula-2.